From a1e07a80460240494c6fa6cee364506dcb609fae Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 27 Aug 2023 21:21:25 +0300 Subject: [PATCH 01/14] fix: reprogram standard library for env api --- lib/core.ts | 249 ++----- lib/iterators.ts | 216 ------ lib/lib.d.ts | 634 ++++++++++++++++ lib/map.ts | 65 +- lib/modules.ts | 12 + lib/promise.ts | 46 +- lib/regex.ts | 334 ++++----- lib/require.ts | 15 - lib/set.ts | 31 +- lib/utils.ts | 44 ++ lib/values/array.ts | 674 +++++++++--------- lib/values/boolean.ts | 34 +- lib/values/errors.ts | 122 +--- lib/values/function.ts | 303 ++++---- lib/values/number.ts | 75 +- lib/values/object.ts | 398 +++++------ lib/values/string.ts | 423 +++++------ lib/values/symbol.ts | 65 +- src/me/topchetoeu/jscript/Main.java | 5 +- .../values/OperationStatement.java | 36 +- src/me/topchetoeu/jscript/engine/Engine.java | 10 +- .../jscript/engine/frame/CodeFrame.java | 10 +- .../jscript/engine/frame/Runners.java | 200 +++--- .../jscript/engine/modules/Module.java | 2 +- .../jscript/engine/scope/GlobalScope.java | 10 +- .../jscript/engine/scope/ValueVariable.java | 2 +- .../jscript/engine/values/ArrayValue.java | 14 +- .../jscript/engine/values/CodeFunction.java | 2 +- .../jscript/engine/values/FunctionValue.java | 4 +- .../jscript/engine/values/ObjectValue.java | 46 +- .../jscript/engine/values/Values.java | 47 +- .../jscript/exceptions/EngineException.java | 2 +- .../jscript/interop/NativeTypeRegister.java | 10 +- .../jscript/interop/OverloadFunction.java | 2 +- .../jscript/polyfills/GeneratorFunction.java | 16 +- .../jscript/polyfills/Internals.java | 12 +- src/me/topchetoeu/jscript/polyfills/JSON.java | 4 +- src/me/topchetoeu/jscript/polyfills/Map.java | 2 +- .../jscript/polyfills/PolyfillEngine.java | 99 ++- .../topchetoeu/jscript/polyfills/Promise.java | 42 +- .../topchetoeu/jscript/polyfills/RegExp.java | 18 +- src/me/topchetoeu/jscript/polyfills/Set.java | 56 +- ...riptEngine.java => TypescriptEngine.java-} | 122 ++-- tsconfig.json | 27 +- 44 files changed, 2207 insertions(+), 2333 deletions(-) delete mode 100644 lib/iterators.ts create mode 100644 lib/lib.d.ts create mode 100644 lib/modules.ts delete mode 100644 lib/require.ts create mode 100644 lib/utils.ts rename src/me/topchetoeu/jscript/polyfills/{TypescriptEngine.java => TypescriptEngine.java-} (97%) diff --git a/lib/core.ts b/lib/core.ts index 562ac4b..75d0ff8 100644 --- a/lib/core.ts +++ b/lib/core.ts @@ -1,191 +1,68 @@ -type PropertyDescriptor = { - value: any; - writable?: boolean; - enumerable?: boolean; - configurable?: boolean; -} | { - get?(this: ThisT): T; - set(this: ThisT, val: T): void; - enumerable?: boolean; - configurable?: boolean; -} | { - get(this: ThisT): T; - set?(this: ThisT, val: T): void; - enumerable?: boolean; - configurable?: boolean; -}; -type Exclude = T extends U ? never : T; -type Extract = T extends U ? T : never; -type Record = { [x in KeyT]: ValT } +var env: Environment; -interface IArguments { - [i: number]: any; - length: number; -} - -interface MathObject { - readonly E: number; - readonly PI: number; - readonly SQRT2: number; - readonly SQRT1_2: number; - readonly LN2: number; - readonly LN10: number; - readonly LOG2E: number; - readonly LOG10E: number; - - asin(x: number): number; - acos(x: number): number; - atan(x: number): number; - atan2(y: number, x: number): number; - asinh(x: number): number; - acosh(x: number): number; - atanh(x: number): number; - sin(x: number): number; - cos(x: number): number; - tan(x: number): number; - sinh(x: number): number; - cosh(x: number): number; - tanh(x: number): number; - sqrt(x: number): number; - cbrt(x: number): number; - hypot(...vals: number[]): number; - imul(a: number, b: number): number; - exp(x: number): number; - expm1(x: number): number; - pow(x: number, y: number): number; - log(x: number): number; - log10(x: number): number; - log1p(x: number): number; - log2(x: number): number; - ceil(x: number): number; - floor(x: number): number; - round(x: number): number; - fround(x: number): number; - trunc(x: number): number; - abs(x: number): number; - max(...vals: number[]): number; - min(...vals: number[]): number; - sign(x: number): number; - random(): number; - clz32(x: number): number; -} - - -//@ts-ignore -declare const arguments: IArguments; -declare const Math: MathObject; - -declare var setTimeout: (handle: (...args: [ ...T, ...any[] ]) => void, delay?: number, ...args: T) => number; -declare var setInterval: (handle: (...args: [ ...T, ...any[] ]) => void, delay?: number, ...args: T) => number; - -declare var clearTimeout: (id: number) => void; -declare var clearInterval: (id: number) => void; - -/** @internal */ -declare var internals: any; -/** @internal */ -declare function run(file: string, pollute?: boolean): void; - -/** @internal */ -type ReplaceThis = T extends ((...args: infer ArgsT) => infer RetT) ? - ((this: ThisT, ...args: ArgsT) => RetT) : - T; - -/** @internal */ -declare var setProps: < - TargetT extends object, - DescT extends { [x in Exclude ]?: ReplaceThis } ->(target: TargetT, desc: DescT) => void; -/** @internal */ -declare var setConstr: (target: T, constr: ConstrT) => void; - -declare function log(...vals: any[]): void; -/** @internal */ -declare var lgt: typeof globalThis, gt: typeof globalThis; - -declare function assert(condition: () => unknown, message?: string): boolean; - -gt.assert = (cond, msg) => { - try { - if (!cond()) throw 'condition not satisfied'; - log('Passed ' + msg); - return true; - } - catch (e) { - log('Failed ' + msg + ' because of: ' + e); - return false; - } -} - -try { - lgt.setProps = (target, desc) => { - var props = internals.keys(desc, false); - for (var i = 0; i < props.length; i++) { - var key = props[i]; - internals.defineField( - target, key, (desc as any)[key], - true, // writable - false, // enumerable - true // configurable - ); +// @ts-ignore +return (_env: Environment) => { + env = _env; + env.global.assert = (cond, msg) => { + try { + if (!cond()) throw 'condition not satisfied'; + log('Passed ' + msg); + return true; + } + catch (e) { + log('Failed ' + msg + ' because of: ' + e); + return false; } } - lgt.setConstr = (target, constr) => { - internals.defineField( - target, 'constructor', constr, - true, // writable - false, // enumerable - true // configurable - ); + try { + run('values/object'); + run('values/symbol'); + run('values/function'); + run('values/errors'); + run('values/string'); + run('values/number'); + run('values/boolean'); + run('values/array'); + + env.internals.special(Object, Function, Error, Array); + + env.global.setTimeout = (func, delay, ...args) => { + if (typeof func !== 'function') throw new TypeError("func must be a function."); + delay = (delay ?? 0) - 0; + return env.internals.setTimeout(() => func(...args), delay) + }; + env.global.setInterval = (func, delay, ...args) => { + if (typeof func !== 'function') throw new TypeError("func must be a function."); + delay = (delay ?? 0) - 0; + return env.internals.setInterval(() => func(...args), delay) + }; + + env.global.clearTimeout = (id) => { + id = id | 0; + env.internals.clearTimeout(id); + }; + env.global.clearInterval = (id) => { + id = id | 0; + env.internals.clearInterval(id); + }; + + run('promise'); + run('map'); + run('set'); + run('regex'); + run('require'); + + log('Loaded polyfills!'); } - - run('values/object.js'); - run('values/symbol.js'); - run('values/function.js'); - run('values/errors.js'); - run('values/string.js'); - run('values/number.js'); - run('values/boolean.js'); - run('values/array.js'); - - internals.special(Object, Function, Error, Array); - - gt.setTimeout = (func, delay, ...args) => { - if (typeof func !== 'function') throw new TypeError("func must be a function."); - delay = (delay ?? 0) - 0; - return internals.setTimeout(() => func(...args), delay) - }; - gt.setInterval = (func, delay, ...args) => { - if (typeof func !== 'function') throw new TypeError("func must be a function."); - delay = (delay ?? 0) - 0; - return internals.setInterval(() => func(...args), delay) - }; - - gt.clearTimeout = (id) => { - id = id | 0; - internals.clearTimeout(id); - }; - gt.clearInterval = (id) => { - id = id | 0; - internals.clearInterval(id); - }; - - - run('iterators.js'); - run('promise.js'); - run('map.js', true); - run('set.js', true); - run('regex.js'); - run('require.js'); - - log('Loaded polyfills!'); -} -catch (e: any) { - var err = 'Uncaught error while loading polyfills: '; - if (typeof Error !== 'undefined' && e instanceof Error && e.toString !== {}.toString) err += e; - else if ('message' in e) { - if ('name' in e) err += e.name + ": " + e.message; - else err += 'Error: ' + e.message; + catch (e: any) { + if (!_env.captureErr) throw e; + var err = 'Uncaught error while loading polyfills: '; + if (typeof Error !== 'undefined' && e instanceof Error && e.toString !== {}.toString) err += e; + else if ('message' in e) { + if ('name' in e) err += e.name + ": " + e.message; + else err += 'Error: ' + e.message; + } + else err += e; + log(e); } - else err += e; -} +}; \ No newline at end of file diff --git a/lib/iterators.ts b/lib/iterators.ts deleted file mode 100644 index bdcd11f..0000000 --- a/lib/iterators.ts +++ /dev/null @@ -1,216 +0,0 @@ -interface SymbolConstructor { - readonly iterator: unique symbol; - readonly asyncIterator: unique symbol; -} - -type IteratorYieldResult = - { done?: false; } & - (TReturn extends undefined ? { value?: undefined; } : { value: TReturn; }); - -type IteratorReturnResult = - { done: true } & - (TReturn extends undefined ? { value?: undefined; } : { value: TReturn; }); - -type IteratorResult = IteratorYieldResult | IteratorReturnResult; - -interface Iterator { - next(...args: [] | [TNext]): IteratorResult; - return?(value?: TReturn): IteratorResult; - throw?(e?: any): IteratorResult; -} - -interface Iterable { - [Symbol.iterator](): Iterator; -} - -interface IterableIterator extends Iterator { - [Symbol.iterator](): IterableIterator; -} - -interface Generator extends Iterator { - // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places. - next(...args: [] | [TNext]): IteratorResult; - return(value: TReturn): IteratorResult; - throw(e: any): IteratorResult; - [Symbol.iterator](): Generator; -} - -interface GeneratorFunction { - /** - * Creates a new Generator object. - * @param args A list of arguments the function accepts. - */ - new (...args: any[]): Generator; - /** - * Creates a new Generator object. - * @param args A list of arguments the function accepts. - */ - (...args: any[]): Generator; - /** - * The length of the arguments. - */ - readonly length: number; - /** - * Returns the name of the function. - */ - readonly name: string; - /** - * A reference to the prototype. - */ - readonly prototype: Generator; -} - -interface GeneratorFunctionConstructor { - /** - * Creates a new Generator function. - * @param args A list of arguments the function accepts. - */ - new (...args: string[]): GeneratorFunction; - /** - * Creates a new Generator function. - * @param args A list of arguments the function accepts. - */ - (...args: string[]): GeneratorFunction; - /** - * The length of the arguments. - */ - readonly length: number; - /** - * Returns the name of the function. - */ - readonly name: string; - /** - * A reference to the prototype. - */ -} - -interface AsyncIterator { - // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places. - next(...args: [] | [TNext]): Promise>; - return?(value?: TReturn | Thenable): Promise>; - throw?(e?: any): Promise>; -} - -interface AsyncIterable { - [Symbol.asyncIterator](): AsyncIterator; -} - -interface AsyncIterableIterator extends AsyncIterator { - [Symbol.asyncIterator](): AsyncIterableIterator; -} - -interface AsyncGenerator extends AsyncIterator { - // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places. - next(...args: [] | [TNext]): Promise>; - return(value: TReturn | Thenable): Promise>; - throw(e: any): Promise>; - [Symbol.asyncIterator](): AsyncGenerator; -} - -interface AsyncGeneratorFunction { - /** - * Creates a new AsyncGenerator object. - * @param args A list of arguments the function accepts. - */ - new (...args: any[]): AsyncGenerator; - /** - * Creates a new AsyncGenerator object. - * @param args A list of arguments the function accepts. - */ - (...args: any[]): AsyncGenerator; - /** - * The length of the arguments. - */ - readonly length: number; - /** - * Returns the name of the function. - */ - readonly name: string; - /** - * A reference to the prototype. - */ - readonly prototype: AsyncGenerator; -} - -interface AsyncGeneratorFunctionConstructor { - /** - * Creates a new AsyncGenerator function. - * @param args A list of arguments the function accepts. - */ - new (...args: string[]): AsyncGeneratorFunction; - /** - * Creates a new AsyncGenerator function. - * @param args A list of arguments the function accepts. - */ - (...args: string[]): AsyncGeneratorFunction; - /** - * The length of the arguments. - */ - readonly length: number; - /** - * Returns the name of the function. - */ - readonly name: string; - /** - * A reference to the prototype. - */ - readonly prototype: AsyncGeneratorFunction; -} - - -interface Array extends IterableIterator { - entries(): IterableIterator<[number, T]>; - values(): IterableIterator; - keys(): IterableIterator; -} - -setProps(Symbol, { - iterator: Symbol("Symbol.iterator") as any, - asyncIterator: Symbol("Symbol.asyncIterator") as any, -}); - -setProps(Array.prototype, { - [Symbol.iterator]: function() { - return this.values(); - }, - - values() { - var i = 0; - - return { - next: () => { - while (i < this.length) { - if (i++ in this) return { done: false, value: this[i - 1] }; - } - return { done: true, value: undefined }; - }, - [Symbol.iterator]() { return this; } - }; - }, - keys() { - var i = 0; - - return { - next: () => { - while (i < this.length) { - if (i++ in this) return { done: false, value: i - 1 }; - } - return { done: true, value: undefined }; - }, - [Symbol.iterator]() { return this; } - }; - }, - entries() { - var i = 0; - - return { - next: () => { - while (i < this.length) { - if (i++ in this) return { done: false, value: [i - 1, this[i - 1]] }; - } - return { done: true, value: undefined }; - }, - [Symbol.iterator]() { return this; } - }; - }, -}); diff --git a/lib/lib.d.ts b/lib/lib.d.ts new file mode 100644 index 0000000..e1c5c7d --- /dev/null +++ b/lib/lib.d.ts @@ -0,0 +1,634 @@ +type PropertyDescriptor = { + value: any; + writable?: boolean; + enumerable?: boolean; + configurable?: boolean; +} | { + get?(this: ThisT): T; + set(this: ThisT, val: T): void; + enumerable?: boolean; + configurable?: boolean; +} | { + get(this: ThisT): T; + set?(this: ThisT, val: T): void; + enumerable?: boolean; + configurable?: boolean; +}; +type Exclude = T extends U ? never : T; +type Extract = T extends U ? T : never; +type Record = { [x in KeyT]: ValT } +type ReplaceFunc = (match: string, ...args: any[]) => string; + +type PromiseFulfillFunc = (val: T) => void; +type PromiseThenFunc = (val: T) => NextT; +type PromiseRejectFunc = (err: unknown) => void; +type PromiseFunc = (resolve: PromiseFulfillFunc, reject: PromiseRejectFunc) => void; + +type PromiseResult ={ type: 'fulfilled'; value: T; } | { type: 'rejected'; reason: any; } + +// wippidy-wine, this code is now mine :D +type Awaited = + T extends null | undefined ? T : // special case for `null | undefined` when not in `--strictNullChecks` mode + T extends object & { then(onfulfilled: infer F, ...args: infer _): any } ? // `await` only unwraps object types with a callable `then`. Non-object types are not unwrapped + F extends ((value: infer V, ...args: infer _) => any) ? // if the argument to `then` is callable, extracts the first argument + Awaited : // recursively unwrap the value + never : // the argument to `then` was not callable + T; + +type IteratorYieldResult = + { done?: false; } & + (TReturn extends undefined ? { value?: undefined; } : { value: TReturn; }); + +type IteratorReturnResult = + { done: true } & + (TReturn extends undefined ? { value?: undefined; } : { value: TReturn; }); + +type IteratorResult = IteratorYieldResult | IteratorReturnResult; + +interface Thenable { + then(this: Promise, onFulfilled: PromiseThenFunc, onRejected?: PromiseRejectFunc): Promise>; + then(this: Promise, onFulfilled: undefined, onRejected?: PromiseRejectFunc): Promise; +} + +interface RegExpResultIndices extends Array<[number, number]> { + groups?: { [name: string]: [number, number]; }; +} +interface RegExpResult extends Array { + groups?: { [name: string]: string; }; + index: number; + input: string; + indices?: RegExpResultIndices; + escape(raw: string, flags: string): RegExp; +} + +interface Matcher { + [Symbol.match](target: string): RegExpResult | string[] | null; + [Symbol.matchAll](target: string): IterableIterator; +} +interface Splitter { + [Symbol.split](target: string, limit?: number, sensible?: boolean): string[]; +} +interface Replacer { + [Symbol.replace](target: string, replacement: string | ReplaceFunc): string; +} +interface Searcher { + [Symbol.search](target: string, reverse?: boolean, start?: number): number; +} + +type FlatArray = { + "done": Arr, + "recur": Arr extends Array + ? FlatArray + : Arr +}[Depth extends -1 ? "done" : "recur"]; + +interface IArguments { + [i: number]: any; + length: number; +} + +interface Iterator { + next(...args: [] | [TNext]): IteratorResult; + return?(value?: TReturn): IteratorResult; + throw?(e?: any): IteratorResult; +} +interface Iterable { + [Symbol.iterator](): Iterator; +} +interface IterableIterator extends Iterator { + [Symbol.iterator](): IterableIterator; +} + +interface AsyncIterator { + // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places. + next(...args: [] | [TNext]): Promise>; + return?(value?: TReturn | Thenable): Promise>; + throw?(e?: any): Promise>; +} +interface AsyncIterable { + [Symbol.asyncIterator](): AsyncIterator; +} +interface AsyncIterableIterator extends AsyncIterator { + [Symbol.asyncIterator](): AsyncIterableIterator; +} + +interface Generator extends Iterator { + [Symbol.iterator](): Generator; + return(value?: TReturn): IteratorResult; + throw(e?: any): IteratorResult; +} +interface GeneratorFunction { + /** + * Creates a new Generator object. + * @param args A list of arguments the function accepts. + */ + new (...args: any[]): Generator; + /** + * Creates a new Generator object. + * @param args A list of arguments the function accepts. + */ + (...args: any[]): Generator; + /** + * The length of the arguments. + */ + readonly length: number; + /** + * Returns the name of the function. + */ + readonly name: string; + /** + * A reference to the prototype. + */ + readonly prototype: Generator; +} + +interface AsyncGenerator extends AsyncIterator { + // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places. + next(...args: [] | [TNext]): Promise>; + return(value: TReturn | Thenable): Promise>; + throw(e: any): Promise>; + [Symbol.asyncIterator](): AsyncGenerator; +} +interface AsyncGeneratorFunction { + /** + * Creates a new AsyncGenerator object. + * @param args A list of arguments the function accepts. + */ + new (...args: any[]): AsyncGenerator; + /** + * Creates a new AsyncGenerator object. + * @param args A list of arguments the function accepts. + */ + (...args: any[]): AsyncGenerator; + /** + * The length of the arguments. + */ + readonly length: number; + /** + * Returns the name of the function. + */ + readonly name: string; + /** + * A reference to the prototype. + */ + readonly prototype: AsyncGenerator; +} + + +interface MathObject { + readonly E: number; + readonly PI: number; + readonly SQRT2: number; + readonly SQRT1_2: number; + readonly LN2: number; + readonly LN10: number; + readonly LOG2E: number; + readonly LOG10E: number; + + asin(x: number): number; + acos(x: number): number; + atan(x: number): number; + atan2(y: number, x: number): number; + asinh(x: number): number; + acosh(x: number): number; + atanh(x: number): number; + sin(x: number): number; + cos(x: number): number; + tan(x: number): number; + sinh(x: number): number; + cosh(x: number): number; + tanh(x: number): number; + sqrt(x: number): number; + cbrt(x: number): number; + hypot(...vals: number[]): number; + imul(a: number, b: number): number; + exp(x: number): number; + expm1(x: number): number; + pow(x: number, y: number): number; + log(x: number): number; + log10(x: number): number; + log1p(x: number): number; + log2(x: number): number; + ceil(x: number): number; + floor(x: number): number; + round(x: number): number; + fround(x: number): number; + trunc(x: number): number; + abs(x: number): number; + max(...vals: number[]): number; + min(...vals: number[]): number; + sign(x: number): number; + random(): number; + clz32(x: number): number; +} + +interface Array extends IterableIterator { + [i: number]: T; + + constructor: ArrayConstructor; + length: number; + + toString(): string; + // toLocaleString(): string; + join(separator?: string): string; + fill(val: T, start?: number, end?: number): T[]; + pop(): T | undefined; + push(...items: T[]): number; + concat(...items: (T | T[])[]): T[]; + concat(...items: (T | T[])[]): T[]; + join(separator?: string): string; + reverse(): T[]; + shift(): T | undefined; + slice(start?: number, end?: number): T[]; + sort(compareFn?: (a: T, b: T) => number): this; + splice(start: number, deleteCount?: number | undefined, ...items: T[]): T[]; + unshift(...items: T[]): number; + indexOf(searchElement: T, fromIndex?: number): number; + lastIndexOf(searchElement: T, fromIndex?: number): number; + every(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean; + some(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean; + forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void; + includes(el: any, start?: number): boolean; + + map(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[]; + filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[]; + find(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[]; + findIndex(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): number; + findLast(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[]; + findLastIndex(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): number; + + flat(depth?: D): FlatArray; + flatMap(func: (val: T, i: number, arr: T[]) => T | T[], thisAarg?: any): FlatArray; + sort(func?: (a: T, b: T) => number): this; + + reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T; + reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T; + reduce(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; + reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T; + reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T; + reduceRight(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; + + entries(): IterableIterator<[number, T]>; + values(): IterableIterator; + keys(): IterableIterator; +} +interface ArrayConstructor { + new (arrayLength?: number): T[]; + new (...items: T[]): T[]; + (arrayLength?: number): T[]; + (...items: T[]): T[]; + isArray(arg: any): arg is any[]; + prototype: Array; +} + +interface Boolean { + valueOf(): boolean; + constructor: BooleanConstructor; +} +interface BooleanConstructor { + (val: any): boolean; + new (val: any): Boolean; + prototype: Boolean; +} + +interface Error { + constructor: ErrorConstructor; + name: string; + message: string; + stack: string[]; +} +interface ErrorConstructor { + (msg?: any): Error; + new (msg?: any): Error; + prototype: Error; +} + +interface TypeErrorConstructor extends ErrorConstructor { + (msg?: any): TypeError; + new (msg?: any): TypeError; + prototype: Error; +} +interface TypeError extends Error { + constructor: TypeErrorConstructor; + name: 'TypeError'; +} + +interface RangeErrorConstructor extends ErrorConstructor { + (msg?: any): RangeError; + new (msg?: any): RangeError; + prototype: Error; +} +interface RangeError extends Error { + constructor: RangeErrorConstructor; + name: 'RangeError'; +} + +interface SyntaxErrorConstructor extends ErrorConstructor { + (msg?: any): RangeError; + new (msg?: any): RangeError; + prototype: Error; +} +interface SyntaxError extends Error { + constructor: SyntaxErrorConstructor; + name: 'SyntaxError'; +} + +interface Function { + apply(this: Function, thisArg: any, argArray?: any): any; + call(this: Function, thisArg: any, ...argArray: any[]): any; + bind(this: Function, thisArg: any, ...argArray: any[]): Function; + + toString(): string; + + prototype: any; + constructor: FunctionConstructor; + readonly length: number; + name: string; +} +interface CallableFunction extends Function { + (...args: any[]): any; + apply(this: (this: ThisArg, ...args: Args) => RetT, thisArg: ThisArg, argArray?: Args): RetT; + call(this: (this: ThisArg, ...args: Args) => RetT, thisArg: ThisArg, ...argArray: Args): RetT; + bind(this: (this: ThisArg, ...args: [ ...Args, ...Rest ]) => RetT, thisArg: ThisArg, ...argArray: Args): (this: void, ...args: Rest) => RetT; +} +interface NewableFunction extends Function { + new(...args: any[]): any; + apply(this: new (...args: Args) => RetT, thisArg: any, argArray?: Args): RetT; + call(this: new (...args: Args) => RetT, thisArg: any, ...argArray: Args): RetT; + bind(this: new (...args: Args) => RetT, thisArg: any, ...argArray: Args): new (...args: Args) => RetT; +} +interface FunctionConstructor extends Function { + (...args: string[]): (...args: any[]) => any; + new (...args: string[]): (...args: any[]) => any; + prototype: Function; + async( + func: (await: (val: T) => Awaited) => (...args: ArgsT) => RetT + ): (...args: ArgsT) => Promise; + asyncGenerator( + func: (await: (val: T) => Awaited, _yield: (val: T) => void) => (...args: ArgsT) => RetT + ): (...args: ArgsT) => AsyncGenerator; + generator( + func: (_yield: (val: T) => TNext) => (...args: ArgsT) => RetT + ): (...args: ArgsT) => Generator; +} + +interface Number { + toString(): string; + valueOf(): number; + constructor: NumberConstructor; +} +interface NumberConstructor { + (val: any): number; + new (val: any): Number; + prototype: Number; + parseInt(val: unknown): number; + parseFloat(val: unknown): number; +} + +interface Object { + constructor: NewableFunction; + [Symbol.typeName]: string; + + valueOf(): this; + toString(): string; + hasOwnProperty(key: any): boolean; +} +interface ObjectConstructor extends Function { + (arg: string): String; + (arg: number): Number; + (arg: boolean): Boolean; + (arg?: undefined | null): {}; + (arg: T): T; + + new (arg: string): String; + new (arg: number): Number; + new (arg: boolean): Boolean; + new (arg?: undefined | null): {}; + new (arg: T): T; + + prototype: Object; + + assign(target: T, ...src: object[]): T; + create(proto: T, props?: { [key: string]: PropertyDescriptor }): T; + + keys(obj: T, onlyString?: true): (keyof T)[]; + keys(obj: T, onlyString: false): any[]; + entries(obj: T, onlyString?: true): [keyof T, T[keyof T]][]; + entries(obj: T, onlyString: false): [any, any][]; + values(obj: T, onlyString?: true): (T[keyof T])[]; + values(obj: T, onlyString: false): any[]; + + fromEntries(entries: Iterable<[any, any]>): object; + + defineProperty(obj: ThisT, key: any, desc: PropertyDescriptor): ThisT; + defineProperties(obj: ThisT, desc: { [key: string]: PropertyDescriptor }): ThisT; + + getOwnPropertyNames(obj: T): (keyof T)[]; + getOwnPropertySymbols(obj: T): (keyof T)[]; + hasOwn(obj: T, key: KeyT): boolean; + + getOwnPropertyDescriptor(obj: T, key: KeyT): PropertyDescriptor; + getOwnPropertyDescriptors(obj: T): { [x in keyof T]: PropertyDescriptor }; + + getPrototypeOf(obj: any): object | null; + setPrototypeOf(obj: T, proto: object | null): T; + + preventExtensions(obj: T): T; + seal(obj: T): T; + freeze(obj: T): T; + + isExtensible(obj: object): boolean; + isSealed(obj: object): boolean; + isFrozen(obj: object): boolean; +} + +interface String { + [i: number]: string; + + toString(): string; + valueOf(): string; + + charAt(pos: number): string; + charCodeAt(pos: number): number; + substring(start?: number, end?: number): string; + slice(start?: number, end?: number): string; + substr(start?: number, length?: number): string; + + startsWith(str: string, pos?: number): string; + endsWith(str: string, pos?: number): string; + + replace(pattern: string | Replacer, val: string | ReplaceFunc): string; + replaceAll(pattern: string | Replacer, val: string | ReplaceFunc): string; + + match(pattern: string | Matcher): RegExpResult | string[] | null; + matchAll(pattern: string | Matcher): IterableIterator; + + split(pattern: string | Splitter, limit?: number, sensible?: boolean): string; + + concat(...others: string[]): string; + indexOf(term: string | Searcher, start?: number): number; + lastIndexOf(term: string | Searcher, start?: number): number; + + toLowerCase(): string; + toUpperCase(): string; + + trim(): string; + + includes(term: string, start?: number): boolean; + + length: number; + + constructor: StringConstructor; +} +interface StringConstructor { + (val: any): string; + new (val: any): String; + + fromCharCode(val: number): string; + + prototype: String; +} + +interface Symbol { + valueOf(): symbol; + constructor: SymbolConstructor; +} +interface SymbolConstructor { + (val?: any): symbol; + new(...args: any[]): never; + prototype: Symbol; + for(key: string): symbol; + keyFor(sym: symbol): string; + + readonly typeName: unique symbol; + readonly match: unique symbol; + readonly matchAll: unique symbol; + readonly split: unique symbol; + readonly replace: unique symbol; + readonly search: unique symbol; + readonly iterator: unique symbol; + readonly asyncIterator: unique symbol; +} + +interface Promise extends Thenable { + constructor: PromiseConstructor; + catch(func: PromiseRejectFunc): Promise; + finally(func: () => void): Promise; +} +interface PromiseConstructor { + prototype: Promise; + + new (func: PromiseFunc): Promise>; + resolve(val: T): Promise>; + reject(val: any): Promise; + + any(promises: (Promise|T)[]): Promise; + race(promises: (Promise|T)[]): Promise; + all(promises: T): Promise<{ [Key in keyof T]: Awaited }>; + allSettled(...promises: T): Promise<[...{ [P in keyof T]: PromiseResult>}]>; +} + +declare var String: StringConstructor; +//@ts-ignore +declare const arguments: IArguments; +declare var NaN: number; +declare var Infinity: number; + +declare var setTimeout: (handle: (...args: [ ...T, ...any[] ]) => void, delay?: number, ...args: T) => number; +declare var setInterval: (handle: (...args: [ ...T, ...any[] ]) => void, delay?: number, ...args: T) => number; + +declare var clearTimeout: (id: number) => void; +declare var clearInterval: (id: number) => void; + +declare var parseInt: typeof Number.parseInt; +declare var parseFloat: typeof Number.parseFloat; + +declare function log(...vals: any[]): void; +declare function assert(condition: () => unknown, message?: string): boolean; + +declare var Array: ArrayConstructor; +declare var Boolean: BooleanConstructor; +declare var Promise: PromiseConstructor; +declare var Function: FunctionConstructor; +declare var Number: NumberConstructor; +declare var Object: ObjectConstructor; +declare var Symbol: SymbolConstructor; +declare var Promise: PromiseConstructor; +declare var Math: MathObject; + +declare var Error: ErrorConstructor; +declare var RangeError: RangeErrorConstructor; +declare var TypeError: TypeErrorConstructor; +declare var SyntaxError: SyntaxErrorConstructor; + +declare class Map { + public [Symbol.iterator](): IterableIterator<[KeyT, ValueT]>; + + public clear(): void; + public delete(key: KeyT): boolean; + + public entries(): IterableIterator<[KeyT, ValueT]>; + public keys(): IterableIterator; + public values(): IterableIterator; + + public get(key: KeyT): ValueT; + public set(key: KeyT, val: ValueT): this; + public has(key: KeyT): boolean; + + public get size(): number; + + public forEach(func: (key: KeyT, val: ValueT, map: Map) => void, thisArg?: any): void; + + public constructor(); +} +declare class Set { + public [Symbol.iterator](): IterableIterator; + + public entries(): IterableIterator<[T, T]>; + public keys(): IterableIterator; + public values(): IterableIterator; + + public clear(): void; + + public add(val: T): this; + public delete(val: T): boolean; + public has(key: T): boolean; + + public get size(): number; + + public forEach(func: (key: T, set: Set) => void, thisArg?: any): void; + + public constructor(); +} + +declare class RegExp implements Matcher, Splitter, Replacer, Searcher { + static escape(raw: any, flags?: string): RegExp; + + prototype: RegExp; + + exec(val: string): RegExpResult | null; + test(val: string): boolean; + toString(): string; + + [Symbol.match](target: string): RegExpResult | string[] | null; + [Symbol.matchAll](target: string): IterableIterator; + [Symbol.split](target: string, limit?: number, sensible?: boolean): string[]; + [Symbol.replace](target: string, replacement: string | ReplaceFunc): string; + [Symbol.search](target: string, reverse?: boolean, start?: number): number; + + readonly dotAll: boolean; + readonly global: boolean; + readonly hasIndices: boolean; + readonly ignoreCase: boolean; + readonly multiline: boolean; + readonly sticky: boolean; + readonly unicode: boolean; + + readonly source: string; + readonly flags: string; + + lastIndex: number; + + constructor(pattern?: string, flags?: string); + constructor(pattern?: RegExp, flags?: string); +} diff --git a/lib/map.ts b/lib/map.ts index f48c235..2c45e0c 100644 --- a/lib/map.ts +++ b/lib/map.ts @@ -1,44 +1,29 @@ -declare class Map { - public [Symbol.iterator](): IterableIterator<[KeyT, ValueT]>; +define("map", () => { + var Map = env.global.Map = env.internals.Map; - public clear(): void; - public delete(key: KeyT): boolean; + Map.prototype[Symbol.iterator] = function() { + return this.entries(); + }; - public entries(): IterableIterator<[KeyT, ValueT]>; - public keys(): IterableIterator; - public values(): IterableIterator; + var entries = Map.prototype.entries; + var keys = Map.prototype.keys; + var values = Map.prototype.values; - public get(key: KeyT): ValueT; - public set(key: KeyT, val: ValueT): this; - public has(key: KeyT): boolean; + Map.prototype.entries = function() { + var it = entries.call(this); + it[Symbol.iterator] = () => it; + return it; + }; + Map.prototype.keys = function() { + var it = keys.call(this); + it[Symbol.iterator] = () => it; + return it; + }; + Map.prototype.values = function() { + var it = values.call(this); + it[Symbol.iterator] = () => it; + return it; + }; - public get size(): number; - - public forEach(func: (key: KeyT, val: ValueT, map: Map) => void, thisArg?: any): void; - - public constructor(); -} - -Map.prototype[Symbol.iterator] = function() { - return this.entries(); -}; - -var entries = Map.prototype.entries; -var keys = Map.prototype.keys; -var values = Map.prototype.values; - -Map.prototype.entries = function() { - var it = entries.call(this); - it[Symbol.iterator] = () => it; - return it; -}; -Map.prototype.keys = function() { - var it = keys.call(this); - it[Symbol.iterator] = () => it; - return it; -}; -Map.prototype.values = function() { - var it = values.call(this); - it[Symbol.iterator] = () => it; - return it; -}; + env.global.Map = Map; +}); diff --git a/lib/modules.ts b/lib/modules.ts new file mode 100644 index 0000000..8eb68a0 --- /dev/null +++ b/lib/modules.ts @@ -0,0 +1,12 @@ +var { define, run } = (() => { + const modules: Record = {}; + + function define(name: string, func: Function) { + modules[name] = func; + } + function run(name: string) { + return modules[name](); + } + + return { define, run }; +})(); diff --git a/lib/promise.ts b/lib/promise.ts index 99fb463..9eb1404 100644 --- a/lib/promise.ts +++ b/lib/promise.ts @@ -1,43 +1,3 @@ -type PromiseFulfillFunc = (val: T) => void; -type PromiseThenFunc = (val: T) => NextT; -type PromiseRejectFunc = (err: unknown) => void; -type PromiseFunc = (resolve: PromiseFulfillFunc, reject: PromiseRejectFunc) => void; - -type PromiseResult ={ type: 'fulfilled'; value: T; } | { type: 'rejected'; reason: any; } - -interface Thenable { - then(this: Promise, onFulfilled: PromiseThenFunc, onRejected?: PromiseRejectFunc): Promise>; - then(this: Promise, onFulfilled: undefined, onRejected?: PromiseRejectFunc): Promise; -} - -// wippidy-wine, this code is now mine :D -type Awaited = - T extends null | undefined ? T : // special case for `null | undefined` when not in `--strictNullChecks` mode - T extends object & { then(onfulfilled: infer F, ...args: infer _): any } ? // `await` only unwraps object types with a callable `then`. Non-object types are not unwrapped - F extends ((value: infer V, ...args: infer _) => any) ? // if the argument to `then` is callable, extracts the first argument - Awaited : // recursively unwrap the value - never : // the argument to `then` was not callable - T; - -interface PromiseConstructor { - prototype: Promise; - - new (func: PromiseFunc): Promise>; - resolve(val: T): Promise>; - reject(val: any): Promise; - - any(promises: (Promise|T)[]): Promise; - race(promises: (Promise|T)[]): Promise; - all(promises: T): Promise<{ [Key in keyof T]: Awaited }>; - allSettled(...promises: T): Promise<[...{ [P in keyof T]: PromiseResult>}]>; -} - -interface Promise extends Thenable { - constructor: PromiseConstructor; - catch(func: PromiseRejectFunc): Promise; - finally(func: () => void): Promise; -} - -declare var Promise: PromiseConstructor; - -(Promise.prototype as any)[Symbol.typeName] = 'Promise'; +define("promise", () => { + (env.global.Promise = env.internals.Promise).prototype[Symbol.typeName] = 'Promise'; +}); diff --git a/lib/regex.ts b/lib/regex.ts index 4a1e6ff..cb1217f 100644 --- a/lib/regex.ts +++ b/lib/regex.ts @@ -1,211 +1,143 @@ -interface RegExpResultIndices extends Array<[number, number]> { - groups?: { [name: string]: [number, number]; }; -} -interface RegExpResult extends Array { - groups?: { [name: string]: string; }; - index: number; - input: string; - indices?: RegExpResultIndices; - escape(raw: string, flags: string): RegExp; -} -interface SymbolConstructor { - readonly match: unique symbol; - readonly matchAll: unique symbol; - readonly split: unique symbol; - readonly replace: unique symbol; - readonly search: unique symbol; -} +define("regex", () => { + var RegExp = env.global.RegExp = env.internals.RegExp; -type ReplaceFunc = (match: string, ...args: any[]) => string; - -interface Matcher { - [Symbol.match](target: string): RegExpResult | string[] | null; - [Symbol.matchAll](target: string): IterableIterator; -} -interface Splitter { - [Symbol.split](target: string, limit?: number, sensible?: boolean): string[]; -} -interface Replacer { - [Symbol.replace](target: string, replacement: string): string; -} -interface Searcher { - [Symbol.search](target: string, reverse?: boolean, start?: number): number; -} - -declare class RegExp implements Matcher, Splitter, Replacer, Searcher { - static escape(raw: any, flags?: string): RegExp; - - prototype: RegExp; - - exec(val: string): RegExpResult | null; - test(val: string): boolean; - toString(): string; - - [Symbol.match](target: string): RegExpResult | string[] | null; - [Symbol.matchAll](target: string): IterableIterator; - [Symbol.split](target: string, limit?: number, sensible?: boolean): string[]; - [Symbol.replace](target: string, replacement: string | ReplaceFunc): string; - [Symbol.search](target: string, reverse?: boolean, start?: number): number; - - readonly dotAll: boolean; - readonly global: boolean; - readonly hasIndices: boolean; - readonly ignoreCase: boolean; - readonly multiline: boolean; - readonly sticky: boolean; - readonly unicode: boolean; - - readonly source: string; - readonly flags: string; - - lastIndex: number; - - constructor(pattern?: string, flags?: string); - constructor(pattern?: RegExp, flags?: string); -} - -(Symbol as any).replace = Symbol('Symbol.replace'); -(Symbol as any).match = Symbol('Symbol.match'); -(Symbol as any).matchAll = Symbol('Symbol.matchAll'); -(Symbol as any).split = Symbol('Symbol.split'); -(Symbol as any).search = Symbol('Symbol.search'); - -setProps(RegExp.prototype, { - [Symbol.typeName]: 'RegExp', - - test(val) { - return !!this.exec(val); - }, - toString() { - return '/' + this.source + '/' + this.flags; - }, - - [Symbol.match](target) { - if (this.global) { + setProps(RegExp.prototype as RegExp, env, { + [Symbol.typeName]: 'RegExp', + + test(val) { + return !!this.exec(val); + }, + toString() { + return '/' + this.source + '/' + this.flags; + }, + + [Symbol.match](target) { + if (this.global) { + const res: string[] = []; + let val; + while (val = this.exec(target)) { + res.push(val[0]); + } + this.lastIndex = 0; + return res; + } + else { + const res = this.exec(target); + if (!this.sticky) this.lastIndex = 0; + return res; + } + }, + [Symbol.matchAll](target) { + let pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp; + + return { + next: (): IteratorResult => { + const val = pattern?.exec(target); + + if (val === null || val === undefined) { + pattern = undefined; + return { done: true }; + } + else return { value: val }; + }, + [Symbol.iterator]() { return this; } + } + }, + [Symbol.split](target, limit, sensible) { + const pattern = new this.constructor(this, this.flags + "g") as RegExp; + let match: RegExpResult | null; + let lastEnd = 0; const res: string[] = []; - let val; - while (val = this.exec(target)) { - res.push(val[0]); + + while ((match = pattern.exec(target)) !== null) { + let added: string[] = []; + + if (match.index >= target.length) break; + + if (match[0].length === 0) { + added = [ target.substring(lastEnd, pattern.lastIndex), ]; + if (pattern.lastIndex < target.length) added.push(...match.slice(1)); + } + else if (match.index - lastEnd > 0) { + added = [ target.substring(lastEnd, match.index), ...match.slice(1) ]; + } + else { + for (let i = 1; i < match.length; i++) { + res[res.length - match.length + i] = match[i]; + } + } + + if (sensible) { + if (limit !== undefined && res.length + added.length >= limit) break; + else res.push(...added); + } + else { + for (let i = 0; i < added.length; i++) { + if (limit !== undefined && res.length >= limit) return res; + else res.push(added[i]); + } + } + + lastEnd = pattern.lastIndex; } - this.lastIndex = 0; + + if (lastEnd < target.length) { + res.push(target.substring(lastEnd)); + } + return res; - } - else { - const res = this.exec(target); - if (!this.sticky) this.lastIndex = 0; - return res; - } - }, - [Symbol.matchAll](target) { - let pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp; - - return { - next: (): IteratorResult => { - const val = pattern?.exec(target); - - if (val === null || val === undefined) { - pattern = undefined; - return { done: true }; + }, + [Symbol.replace](target, replacement) { + const pattern = new this.constructor(this, this.flags + "d") as RegExp; + let match: RegExpResult | null; + let lastEnd = 0; + const res: string[] = []; + + // log(pattern.toString()); + + while ((match = pattern.exec(target)) !== null) { + const indices = match.indices![0]; + res.push(target.substring(lastEnd, indices[0])); + if (replacement instanceof Function) { + res.push(replacement(target.substring(indices[0], indices[1]), ...match.slice(1), indices[0], target)); } - else return { value: val }; - }, - [Symbol.iterator]() { return this; } - } - }, - [Symbol.split](target, limit, sensible) { - const pattern = new this.constructor(this, this.flags + "g") as RegExp; - let match: RegExpResult | null; - let lastEnd = 0; - const res: string[] = []; - - while ((match = pattern.exec(target)) !== null) { - let added: string[] = []; - - if (match.index >= target.length) break; - - if (match[0].length === 0) { - added = [ target.substring(lastEnd, pattern.lastIndex), ]; - if (pattern.lastIndex < target.length) added.push(...match.slice(1)); + else { + res.push(replacement); + } + lastEnd = indices[1]; + if (!pattern.global) break; } - else if (match.index - lastEnd > 0) { - added = [ target.substring(lastEnd, match.index), ...match.slice(1) ]; + + if (lastEnd < target.length) { + res.push(target.substring(lastEnd)); + } + + return res.join(''); + }, + [Symbol.search](target, reverse, start) { + const pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp; + + + if (!reverse) { + pattern.lastIndex = (start as any) | 0; + const res = pattern.exec(target); + if (res) return res.index; + else return -1; } else { - for (let i = 1; i < match.length; i++) { - res[res.length - match.length + i] = match[i]; + start ??= target.length; + start |= 0; + let res: RegExpResult | null = null; + + while (true) { + const tmp = pattern.exec(target); + if (tmp === null || tmp.index > start) break; + res = tmp; } + + if (res && res.index <= start) return res.index; + else return -1; } - - if (sensible) { - if (limit !== undefined && res.length + added.length >= limit) break; - else res.push(...added); - } - else { - for (let i = 0; i < added.length; i++) { - if (limit !== undefined && res.length >= limit) return res; - else res.push(added[i]); - } - } - - lastEnd = pattern.lastIndex; - } - - if (lastEnd < target.length) { - res.push(target.substring(lastEnd)); - } - - return res; - }, - [Symbol.replace](target, replacement) { - const pattern = new this.constructor(this, this.flags + "d") as RegExp; - let match: RegExpResult | null; - let lastEnd = 0; - const res: string[] = []; - - // log(pattern.toString()); - - while ((match = pattern.exec(target)) !== null) { - const indices = match.indices![0]; - res.push(target.substring(lastEnd, indices[0])); - if (replacement instanceof Function) { - res.push(replacement(target.substring(indices[0], indices[1]), ...match.slice(1), indices[0], target)); - } - else { - res.push(replacement); - } - lastEnd = indices[1]; - if (!pattern.global) break; - } - - if (lastEnd < target.length) { - res.push(target.substring(lastEnd)); - } - - return res.join(''); - }, - [Symbol.search](target, reverse, start) { - const pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp; - - - if (!reverse) { - pattern.lastIndex = (start as any) | 0; - const res = pattern.exec(target); - if (res) return res.index; - else return -1; - } - else { - start ??= target.length; - start |= 0; - let res: RegExpResult | null = null; - - while (true) { - const tmp = pattern.exec(target); - if (tmp === null || tmp.index > start) break; - res = tmp; - } - - if (res && res.index <= start) return res.index; - else return -1; - } - }, -}); + }, + }); +}); \ No newline at end of file diff --git a/lib/require.ts b/lib/require.ts deleted file mode 100644 index 811d6a5..0000000 --- a/lib/require.ts +++ /dev/null @@ -1,15 +0,0 @@ -type RequireFunc = (path: string) => any; - -interface Module { - exports: any; - name: string; -} - -declare var require: RequireFunc; -declare var exports: any; -declare var module: Module; - -gt.require = function(path: string) { - if (typeof path !== 'string') path = path + ''; - return internals.require(path); -}; diff --git a/lib/set.ts b/lib/set.ts index 4ef9ab6..3a21671 100644 --- a/lib/set.ts +++ b/lib/set.ts @@ -1,28 +1,9 @@ -declare class Set { - public [Symbol.iterator](): IterableIterator; +define("set", () => { + var Set = env.global.Set = env.internals.Set; + Set.prototype[Symbol.iterator] = function() { + return this.values(); + }; - public entries(): IterableIterator<[T, T]>; - public keys(): IterableIterator; - public values(): IterableIterator; - - public clear(): void; - - public add(val: T): this; - public delete(val: T): boolean; - public has(key: T): boolean; - - public get size(): number; - - public forEach(func: (key: T, set: Set) => void, thisArg?: any): void; - - public constructor(); -} - -Set.prototype[Symbol.iterator] = function() { - return this.values(); -}; - -(() => { var entries = Set.prototype.entries; var keys = Set.prototype.keys; var values = Set.prototype.values; @@ -42,4 +23,4 @@ Set.prototype[Symbol.iterator] = function() { it[Symbol.iterator] = () => it; return it; }; -})(); \ No newline at end of file +}); diff --git a/lib/utils.ts b/lib/utils.ts new file mode 100644 index 0000000..90fdba4 --- /dev/null +++ b/lib/utils.ts @@ -0,0 +1,44 @@ +interface Environment { + global: typeof globalThis & Record; + captureErr: boolean; + internals: any; +} + +function setProps< + TargetT extends object, + DescT extends { + [x in Exclude ]?: TargetT[x] extends ((...args: infer ArgsT) => infer RetT) ? + ((this: TargetT, ...args: ArgsT) => RetT) : + TargetT[x] + } +>(target: TargetT, env: Environment, desc: DescT) { + var props = env.internals.keys(desc, false); + for (var i = 0; i < props.length; i++) { + var key = props[i]; + env.internals.defineField( + target, key, (desc as any)[key], + true, // writable + false, // enumerable + true // configurable + ); + } +} +function setConstr(target: T, constr: ConstrT, env: Environment) { + env.internals.defineField( + target, 'constructor', constr, + true, // writable + false, // enumerable + true // configurable + ); +} + +function wrapI(max: number, i: number) { + i |= 0; + if (i < 0) i = max + i; + return i; +} +function clampI(max: number, i: number) { + if (i < 0) i = 0; + if (i > max) i = max; + return i; +} \ No newline at end of file diff --git a/lib/values/array.ts b/lib/values/array.ts index dc3eceb..43290b4 100644 --- a/lib/values/array.ts +++ b/lib/values/array.ts @@ -1,369 +1,335 @@ -// god this is awful -type FlatArray = { - "done": Arr, - "recur": Arr extends Array - ? FlatArray - : Arr -}[Depth extends -1 ? "done" : "recur"]; - -interface Array { - [i: number]: T; - - constructor: ArrayConstructor; - length: number; - - toString(): string; - // toLocaleString(): string; - join(separator?: string): string; - fill(val: T, start?: number, end?: number): T[]; - pop(): T | undefined; - push(...items: T[]): number; - concat(...items: (T | T[])[]): T[]; - concat(...items: (T | T[])[]): T[]; - join(separator?: string): string; - reverse(): T[]; - shift(): T | undefined; - slice(start?: number, end?: number): T[]; - sort(compareFn?: (a: T, b: T) => number): this; - splice(start: number, deleteCount?: number | undefined, ...items: T[]): T[]; - unshift(...items: T[]): number; - indexOf(searchElement: T, fromIndex?: number): number; - lastIndexOf(searchElement: T, fromIndex?: number): number; - every(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean; - some(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean; - forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void; - includes(el: any, start?: number): boolean; - - map(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[]; - filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[]; - find(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[]; - findIndex(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): number; - findLast(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[]; - findLastIndex(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): number; - - flat(depth?: D): FlatArray; - flatMap(func: (val: T, i: number, arr: T[]) => T | T[], thisAarg?: any): FlatArray; - sort(func?: (a: T, b: T) => number): this; - - reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T; - reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T; - reduce(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; - reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T; - reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T; - reduceRight(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; -} -interface ArrayConstructor { - new (arrayLength?: number): T[]; - new (...items: T[]): T[]; - (arrayLength?: number): T[]; - (...items: T[]): T[]; - isArray(arg: any): arg is any[]; - prototype: Array; -} - -declare var Array: ArrayConstructor; - -gt.Array = function(len?: number) { - var res = []; - - if (typeof len === 'number' && arguments.length === 1) { - if (len < 0) throw 'Invalid array length.'; - res.length = len; - } - else { - for (var i = 0; i < arguments.length; i++) { - res[i] = arguments[i]; - } - } - - return res; -} as ArrayConstructor; - -Array.prototype = ([] as any).__proto__ as Array; -setConstr(Array.prototype, Array); - -function wrapI(max: number, i: number) { - i |= 0; - if (i < 0) i = max + i; - return i; -} -function clampI(max: number, i: number) { - if (i < 0) i = 0; - if (i > max) i = max; - return i; -} - -lgt.wrapI = wrapI; -lgt.clampI = clampI; - -(Array.prototype as any)[Symbol.typeName] = "Array"; - -setProps(Array.prototype, { - concat() { - var res = [] as any[]; - res.push.apply(res, this); - - for (var i = 0; i < arguments.length; i++) { - var arg = arguments[i]; - if (arg instanceof Array) { - res.push.apply(res, arg); - } - else { - res.push(arg); - } - } - - return res; - }, - every(func, thisArg) { - if (typeof func !== 'function') throw new TypeError("Given argument not a function."); - func = func.bind(thisArg); - - for (var i = 0; i < this.length; i++) { - if (!func(this[i], i, this)) return false; - } - - return true; - }, - some(func, thisArg) { - if (typeof func !== 'function') throw new TypeError("Given argument not a function."); - func = func.bind(thisArg); - - for (var i = 0; i < this.length; i++) { - if (func(this[i], i, this)) return true; - } - - return false; - }, - fill(val, start, end) { - if (arguments.length < 3) end = this.length; - if (arguments.length < 2) start = 0; - - start = clampI(this.length, wrapI(this.length + 1, start ?? 0)); - end = clampI(this.length, wrapI(this.length + 1, end ?? this.length)); - - for (; start < end; start++) { - this[start] = val; - } - - return this; - }, - filter(func, thisArg) { - if (typeof func !== 'function') throw new TypeError("Given argument is not a function."); - +define("values/array", () => { + var Array = env.global.Array = function(len?: number) { var res = []; - for (var i = 0; i < this.length; i++) { - if (i in this && func.call(thisArg, this[i], i, this)) res.push(this[i]); + + if (typeof len === 'number' && arguments.length === 1) { + if (len < 0) throw 'Invalid array length.'; + res.length = len; } + else { + for (var i = 0; i < arguments.length; i++) { + res[i] = arguments[i]; + } + } + return res; - }, - find(func, thisArg) { - if (typeof func !== 'function') throw new TypeError("Given argument is not a function."); - - for (var i = 0; i < this.length; i++) { - if (i in this && func.call(thisArg, this[i], i, this)) return this[i]; - } - - return undefined; - }, - findIndex(func, thisArg) { - if (typeof func !== 'function') throw new TypeError("Given argument is not a function."); - - for (var i = 0; i < this.length; i++) { - if (i in this && func.call(thisArg, this[i], i, this)) return i; - } - - return -1; - }, - findLast(func, thisArg) { - if (typeof func !== 'function') throw new TypeError("Given argument is not a function."); + } as ArrayConstructor; - for (var i = this.length - 1; i >= 0; i--) { - if (i in this && func.call(thisArg, this[i], i, this)) return this[i]; - } + Array.prototype = ([] as any).__proto__ as Array; + setConstr(Array.prototype, Array, env); - return undefined; - }, - findLastIndex(func, thisArg) { - if (typeof func !== 'function') throw new TypeError("Given argument is not a function."); + (Array.prototype as any)[Symbol.typeName] = "Array"; - for (var i = this.length - 1; i >= 0; i--) { - if (i in this && func.call(thisArg, this[i], i, this)) return i; - } + setProps(Array.prototype, env, { + [Symbol.iterator]: function() { + return this.values(); + }, - return -1; - }, - flat(depth) { - var res = [] as any[]; - var buff = []; - res.push(...this); + values() { + var i = 0; - for (var i = 0; i < (depth ?? 1); i++) { - var anyArrays = false; - for (var el of res) { - if (el instanceof Array) { - buff.push(...el); - anyArrays = true; + return { + next: () => { + while (i < this.length) { + if (i++ in this) return { done: false, value: this[i - 1] }; + } + return { done: true, value: undefined }; + }, + [Symbol.iterator]() { return this; } + }; + }, + keys() { + var i = 0; + + return { + next: () => { + while (i < this.length) { + if (i++ in this) return { done: false, value: i - 1 }; + } + return { done: true, value: undefined }; + }, + [Symbol.iterator]() { return this; } + }; + }, + entries() { + var i = 0; + + return { + next: () => { + while (i < this.length) { + if (i++ in this) return { done: false, value: [i - 1, this[i - 1]] }; + } + return { done: true, value: undefined }; + }, + [Symbol.iterator]() { return this; } + }; + }, + concat() { + var res = [] as any[]; + res.push.apply(res, this); + + for (var i = 0; i < arguments.length; i++) { + var arg = arguments[i]; + if (arg instanceof Array) { + res.push.apply(res, arg); + } + else { + res.push(arg); } - else buff.push(el); } - res = buff; - buff = []; - if (!anyArrays) break; - } + return res; + }, + every(func, thisArg) { + if (typeof func !== 'function') throw new TypeError("Given argument not a function."); + func = func.bind(thisArg); - return res; - }, - flatMap(func, th) { - return this.map(func, th).flat(); - }, - forEach(func, thisArg) { - for (var i = 0; i < this.length; i++) { - if (i in this) func.call(thisArg, this[i], i, this); - } - }, - map(func, thisArg) { - if (typeof func !== 'function') throw new TypeError("Given argument is not a function."); - - var res = []; - for (var i = 0; i < this.length; i++) { - if (i in this) res[i] = func.call(thisArg, this[i], i, this); - } - return res; - }, - pop() { - if (this.length === 0) return undefined; - var val = this[this.length - 1]; - this.length--; - return val; - }, - push() { - for (var i = 0; i < arguments.length; i++) { - this[this.length] = arguments[i]; - } - return arguments.length; - }, - shift() { - if (this.length === 0) return undefined; - var res = this[0]; - - for (var i = 0; i < this.length - 1; i++) { - this[i] = this[i + 1]; - } - - this.length--; - - return res; - }, - unshift() { - for (var i = this.length - 1; i >= 0; i--) { - this[i + arguments.length] = this[i]; - } - for (var i = 0; i < arguments.length; i++) { - this[i] = arguments[i]; - } - - return arguments.length; - }, - slice(start, end) { - start = clampI(this.length, wrapI(this.length + 1, start ?? 0)); - end = clampI(this.length, wrapI(this.length + 1, end ?? this.length)); - - var res: any[] = []; - var n = end - start; - if (n <= 0) return res; - - for (var i = 0; i < n; i++) { - res[i] = this[start + i]; - } - - return res; - }, - toString() { - let res = ''; - for (let i = 0; i < this.length; i++) { - if (i > 0) res += ','; - if (i in this && this[i] !== undefined && this[i] !== null) res += this[i]; - } - - return res; - }, - indexOf(el, start) { - start = start! | 0; - for (var i = Math.max(0, start); i < this.length; i++) { - if (i in this && this[i] == el) return i; - } - - return -1; - }, - lastIndexOf(el, start) { - start = start! | 0; - for (var i = this.length; i >= start; i--) { - if (i in this && this[i] == el) return i; - } - - return -1; - }, - includes(el, start) { - return this.indexOf(el, start) >= 0; - }, - join(val = ',') { - let res = '', first = true; - - for (let i = 0; i < this.length; i++) { - if (!(i in this)) continue; - if (!first) res += val; - first = false; - res += this[i]; - } - return res; - }, - sort(func) { - func ??= (a, b) => { - const _a = a + ''; - const _b = b + ''; - - if (_a > _b) return 1; - if (_a < _b) return -1; - return 0; - }; - - if (typeof func !== 'function') throw new TypeError('Expected func to be undefined or a function.'); - - internals.sort(this, func); - return this; - }, - splice(start, deleteCount, ...items) { - start = clampI(this.length, wrapI(this.length, start ?? 0)); - deleteCount = (deleteCount ?? Infinity | 0); - if (start + deleteCount >= this.length) deleteCount = this.length - start; - - const res = this.slice(start, start + deleteCount); - const moveN = items.length - deleteCount; - const len = this.length; - - if (moveN < 0) { - for (let i = start - moveN; i < len; i++) { - this[i + moveN] = this[i]; + for (var i = 0; i < this.length; i++) { + if (!func(this[i], i, this)) return false; } - } - else if (moveN > 0) { - for (let i = len - 1; i >= start; i--) { - this[i + moveN] = this[i]; + + return true; + }, + some(func, thisArg) { + if (typeof func !== 'function') throw new TypeError("Given argument not a function."); + func = func.bind(thisArg); + + for (var i = 0; i < this.length; i++) { + if (func(this[i], i, this)) return true; } + + return false; + }, + fill(val, start, end) { + if (arguments.length < 3) end = this.length; + if (arguments.length < 2) start = 0; + + start = clampI(this.length, wrapI(this.length + 1, start ?? 0)); + end = clampI(this.length, wrapI(this.length + 1, end ?? this.length)); + + for (; start < end; start++) { + this[start] = val; + } + + return this; + }, + filter(func, thisArg) { + if (typeof func !== 'function') throw new TypeError("Given argument is not a function."); + + var res = []; + for (var i = 0; i < this.length; i++) { + if (i in this && func.call(thisArg, this[i], i, this)) res.push(this[i]); + } + return res; + }, + find(func, thisArg) { + if (typeof func !== 'function') throw new TypeError("Given argument is not a function."); + + for (var i = 0; i < this.length; i++) { + if (i in this && func.call(thisArg, this[i], i, this)) return this[i]; + } + + return undefined; + }, + findIndex(func, thisArg) { + if (typeof func !== 'function') throw new TypeError("Given argument is not a function."); + + for (var i = 0; i < this.length; i++) { + if (i in this && func.call(thisArg, this[i], i, this)) return i; + } + + return -1; + }, + findLast(func, thisArg) { + if (typeof func !== 'function') throw new TypeError("Given argument is not a function."); + + for (var i = this.length - 1; i >= 0; i--) { + if (i in this && func.call(thisArg, this[i], i, this)) return this[i]; + } + + return undefined; + }, + findLastIndex(func, thisArg) { + if (typeof func !== 'function') throw new TypeError("Given argument is not a function."); + + for (var i = this.length - 1; i >= 0; i--) { + if (i in this && func.call(thisArg, this[i], i, this)) return i; + } + + return -1; + }, + flat(depth) { + var res = [] as any[]; + var buff = []; + res.push(...this); + + for (var i = 0; i < (depth ?? 1); i++) { + var anyArrays = false; + for (var el of res) { + if (el instanceof Array) { + buff.push(...el); + anyArrays = true; + } + else buff.push(el); + } + + res = buff; + buff = []; + if (!anyArrays) break; + } + + return res; + }, + flatMap(func, th) { + return this.map(func, th).flat(); + }, + forEach(func, thisArg) { + for (var i = 0; i < this.length; i++) { + if (i in this) func.call(thisArg, this[i], i, this); + } + }, + map(func, thisArg) { + if (typeof func !== 'function') throw new TypeError("Given argument is not a function."); + + var res = []; + for (var i = 0; i < this.length; i++) { + if (i in this) res[i] = func.call(thisArg, this[i], i, this); + } + return res; + }, + pop() { + if (this.length === 0) return undefined; + var val = this[this.length - 1]; + this.length--; + return val; + }, + push() { + for (var i = 0; i < arguments.length; i++) { + this[this.length] = arguments[i]; + } + return arguments.length; + }, + shift() { + if (this.length === 0) return undefined; + var res = this[0]; + + for (var i = 0; i < this.length - 1; i++) { + this[i] = this[i + 1]; + } + + this.length--; + + return res; + }, + unshift() { + for (var i = this.length - 1; i >= 0; i--) { + this[i + arguments.length] = this[i]; + } + for (var i = 0; i < arguments.length; i++) { + this[i] = arguments[i]; + } + + return arguments.length; + }, + slice(start, end) { + start = clampI(this.length, wrapI(this.length + 1, start ?? 0)); + end = clampI(this.length, wrapI(this.length + 1, end ?? this.length)); + + var res: any[] = []; + var n = end - start; + if (n <= 0) return res; + + for (var i = 0; i < n; i++) { + res[i] = this[start + i]; + } + + return res; + }, + toString() { + let res = ''; + for (let i = 0; i < this.length; i++) { + if (i > 0) res += ','; + if (i in this && this[i] !== undefined && this[i] !== null) res += this[i]; + } + + return res; + }, + indexOf(el, start) { + start = start! | 0; + for (var i = Math.max(0, start); i < this.length; i++) { + if (i in this && this[i] == el) return i; + } + + return -1; + }, + lastIndexOf(el, start) { + start = start! | 0; + for (var i = this.length; i >= start; i--) { + if (i in this && this[i] == el) return i; + } + + return -1; + }, + includes(el, start) { + return this.indexOf(el, start) >= 0; + }, + join(val = ',') { + let res = '', first = true; + + for (let i = 0; i < this.length; i++) { + if (!(i in this)) continue; + if (!first) res += val; + first = false; + res += this[i]; + } + return res; + }, + sort(func) { + func ??= (a, b) => { + const _a = a + ''; + const _b = b + ''; + + if (_a > _b) return 1; + if (_a < _b) return -1; + return 0; + }; + + if (typeof func !== 'function') throw new TypeError('Expected func to be undefined or a function.'); + + env.internals.sort(this, func); + return this; + }, + splice(start, deleteCount, ...items) { + start = clampI(this.length, wrapI(this.length, start ?? 0)); + deleteCount = (deleteCount ?? Infinity | 0); + if (start + deleteCount >= this.length) deleteCount = this.length - start; + + const res = this.slice(start, start + deleteCount); + const moveN = items.length - deleteCount; + const len = this.length; + + if (moveN < 0) { + for (let i = start - moveN; i < len; i++) { + this[i + moveN] = this[i]; + } + } + else if (moveN > 0) { + for (let i = len - 1; i >= start; i--) { + this[i + moveN] = this[i]; + } + } + + for (let i = 0; i < items.length; i++) { + this[i + start] = items[i]; + } + + this.length = len + moveN; + + return res; } - - for (let i = 0; i < items.length; i++) { - this[i + start] = items[i]; - } - - this.length = len + moveN; - - return res; - } -}); - -setProps(Array, { - isArray(val: any) { return internals.isArr(val); } -}); + }); + + setProps(Array, env, { + isArray(val: any) { return env.internals.isArr(val); } + }); +}); \ No newline at end of file diff --git a/lib/values/boolean.ts b/lib/values/boolean.ts index 104d194..a8868a4 100644 --- a/lib/values/boolean.ts +++ b/lib/values/boolean.ts @@ -1,22 +1,12 @@ -interface Boolean { - valueOf(): boolean; - constructor: BooleanConstructor; -} -interface BooleanConstructor { - (val: any): boolean; - new (val: any): Boolean; - prototype: Boolean; -} - -declare var Boolean: BooleanConstructor; - -gt.Boolean = function (this: Boolean | undefined, arg) { - var val; - if (arguments.length === 0) val = false; - else val = !!arg; - if (this === undefined || this === null) return val; - else (this as any).value = val; -} as BooleanConstructor; - -Boolean.prototype = (false as any).__proto__ as Boolean; -setConstr(Boolean.prototype, Boolean); \ No newline at end of file +define("values/boolean", () => { + var Boolean = env.global.Boolean = function (this: Boolean | undefined, arg) { + var val; + if (arguments.length === 0) val = false; + else val = !!arg; + if (this === undefined || this === null) return val; + else (this as any).value = val; + } as BooleanConstructor; + + Boolean.prototype = (false as any).__proto__ as Boolean; + setConstr(Boolean.prototype, Boolean, env); +}); diff --git a/lib/values/errors.ts b/lib/values/errors.ts index 0f16197..95ad33f 100644 --- a/lib/values/errors.ts +++ b/lib/values/errors.ts @@ -1,89 +1,43 @@ -interface Error { - constructor: ErrorConstructor; - name: string; - message: string; - stack: string[]; -} -interface ErrorConstructor { - (msg?: any): Error; - new (msg?: any): Error; - prototype: Error; -} +define("values/errors", () => { + var Error = env.global.Error = function Error(msg: string) { + if (msg === undefined) msg = ''; + else msg += ''; + + return Object.setPrototypeOf({ + message: msg, + stack: [] as string[], + }, Error.prototype); + } as ErrorConstructor; + + Error.prototype = env.internals.err ?? {}; + Error.prototype.name = 'Error'; + setConstr(Error.prototype, Error, env); -interface TypeErrorConstructor extends ErrorConstructor { - (msg?: any): TypeError; - new (msg?: any): TypeError; - prototype: Error; -} -interface TypeError extends Error { - constructor: TypeErrorConstructor; - name: 'TypeError'; -} + Error.prototype.toString = function() { + if (!(this instanceof Error)) return ''; + + if (this.message === '') return this.name; + else return this.name + ': ' + this.message; + }; -interface RangeErrorConstructor extends ErrorConstructor { - (msg?: any): RangeError; - new (msg?: any): RangeError; - prototype: Error; -} -interface RangeError extends Error { - constructor: RangeErrorConstructor; - name: 'RangeError'; -} + function makeError(name: string, proto: any): T { + var err = function (msg: string) { + var res = new Error(msg); + (res as any).__proto__ = err.prototype; + return res; + } as T; -interface SyntaxErrorConstructor extends ErrorConstructor { - (msg?: any): RangeError; - new (msg?: any): RangeError; - prototype: Error; -} -interface SyntaxError extends Error { - constructor: SyntaxErrorConstructor; - name: 'SyntaxError'; -} + err.prototype = proto; + err.prototype.name = name; + setConstr(err.prototype, err as ErrorConstructor, env); + (err.prototype as any).__proto__ = Error.prototype; + (err as any).__proto__ = Error; + env.internals.special(err); + return err; + } -declare var Error: ErrorConstructor; -declare var RangeError: RangeErrorConstructor; -declare var TypeError: TypeErrorConstructor; -declare var SyntaxError: SyntaxErrorConstructor; - -gt.Error = function Error(msg: string) { - if (msg === undefined) msg = ''; - else msg += ''; - - return Object.setPrototypeOf({ - message: msg, - stack: [] as string[], - }, Error.prototype); -} as ErrorConstructor; - -Error.prototype = internals.err ?? {}; -Error.prototype.name = 'Error'; -setConstr(Error.prototype, Error); - -Error.prototype.toString = function() { - if (!(this instanceof Error)) return ''; - - if (this.message === '') return this.name; - else return this.name + ': ' + this.message; -}; - -function makeError(name: string, proto: any): T { - var err = function (msg: string) { - var res = new Error(msg); - (res as any).__proto__ = err.prototype; - return res; - } as T; - - err.prototype = proto; - err.prototype.name = name; - setConstr(err.prototype, err as ErrorConstructor); - (err.prototype as any).__proto__ = Error.prototype; - (err as any).__proto__ = Error; - internals.special(err); - - return err; -} - -gt.RangeError = makeError('RangeError', internals.range ?? {}); -gt.TypeError = makeError('TypeError', internals.type ?? {}); -gt.SyntaxError = makeError('SyntaxError', internals.syntax ?? {}); \ No newline at end of file + env.global.RangeError = makeError('RangeError', env.internals.range ?? {}); + env.global.TypeError = makeError('TypeError', env.internals.type ?? {}); + env.global.SyntaxError = makeError('SyntaxError', env.internals.syntax ?? {}); +}); \ No newline at end of file diff --git a/lib/values/function.ts b/lib/values/function.ts index 7bf7178..e00f5c9 100644 --- a/lib/values/function.ts +++ b/lib/values/function.ts @@ -1,181 +1,140 @@ -interface Function { - apply(this: Function, thisArg: any, argArray?: any): any; - call(this: Function, thisArg: any, ...argArray: any[]): any; - bind(this: Function, thisArg: any, ...argArray: any[]): Function; +define("values/function", () => { + var Function = env.global.Function = function() { + throw 'Using the constructor Function() is forbidden.'; + } as unknown as FunctionConstructor; - toString(): string; + Function.prototype = (Function as any).__proto__ as Function; + setConstr(Function.prototype, Function, env); - prototype: any; - constructor: FunctionConstructor; - readonly length: number; - name: string; -} -interface FunctionConstructor extends Function { - (...args: string[]): (...args: any[]) => any; - new (...args: string[]): (...args: any[]) => any; - prototype: Function; - async( - func: (await: (val: T) => Awaited) => (...args: ArgsT) => RetT - ): (...args: ArgsT) => Promise; - asyncGenerator( - func: (await: (val: T) => Awaited, _yield: (val: T) => void) => (...args: ArgsT) => RetT - ): (...args: ArgsT) => AsyncGenerator; - generator( - func: (_yield: (val: T) => TNext) => (...args: ArgsT) => RetT - ): (...args: ArgsT) => Generator; -} + setProps(Function.prototype, env, { + apply(thisArg, args) { + if (typeof args !== 'object') throw 'Expected arguments to be an array-like object.'; + var len = args.length - 0; + let newArgs: any[]; + if (Array.isArray(args)) newArgs = args; + else { + newArgs = []; -interface CallableFunction extends Function { - (...args: any[]): any; - apply(this: (this: ThisArg, ...args: Args) => RetT, thisArg: ThisArg, argArray?: Args): RetT; - call(this: (this: ThisArg, ...args: Args) => RetT, thisArg: ThisArg, ...argArray: Args): RetT; - bind(this: (this: ThisArg, ...args: [ ...Args, ...Rest ]) => RetT, thisArg: ThisArg, ...argArray: Args): (this: void, ...args: Rest) => RetT; -} -interface NewableFunction extends Function { - new(...args: any[]): any; - apply(this: new (...args: Args) => RetT, thisArg: any, argArray?: Args): RetT; - call(this: new (...args: Args) => RetT, thisArg: any, ...argArray: Args): RetT; - bind(this: new (...args: Args) => RetT, thisArg: any, ...argArray: Args): new (...args: Args) => RetT; -} - -declare var Function: FunctionConstructor; - -gt.Function = function() { - throw 'Using the constructor Function() is forbidden.'; -} as unknown as FunctionConstructor; - -Function.prototype = (Function as any).__proto__ as Function; -setConstr(Function.prototype, Function); - -setProps(Function.prototype, { - apply(thisArg, args) { - if (typeof args !== 'object') throw 'Expected arguments to be an array-like object.'; - var len = args.length - 0; - let newArgs: any[]; - if (Array.isArray(args)) newArgs = args; - else { - newArgs = []; - - while (len >= 0) { - len--; - newArgs[len] = args[len]; - } - } - - return internals.apply(this, thisArg, newArgs); - }, - call(thisArg, ...args) { - return this.apply(thisArg, args); - }, - bind(thisArg, ...args) { - var func = this; - - var res = function() { - var resArgs = []; - - for (var i = 0; i < args.length; i++) { - resArgs[i] = args[i]; - } - for (var i = 0; i < arguments.length; i++) { - resArgs[i + args.length] = arguments[i]; - } - - return func.apply(thisArg, resArgs); - }; - res.name = " " + func.name; - return res; - }, - toString() { - return 'function (...) { ... }'; - }, -}); -setProps(Function, { - async(func) { - if (typeof func !== 'function') throw new TypeError('Expected func to be function.'); - - return function (this: any) { - const args = arguments; - - return new Promise((res, rej) => { - const gen = Function.generator(func as any).apply(this, args as any); - - (function next(type: 'none' | 'err' | 'ret', val?: any) { - try { - let result; - - switch (type) { - case 'err': result = gen.throw(val); break; - case 'ret': result = gen.next(val); break; - case 'none': result = gen.next(); break; - } - if (result.done) res(result.value); - else Promise.resolve(result.value).then( - v => next('ret', v), - v => next('err', v) - ) - } - catch (e) { - rej(e); - } - })('none'); - }); - }; - }, - asyncGenerator(func) { - if (typeof func !== 'function') throw new TypeError('Expected func to be function.'); - - - return function(this: any) { - const gen = Function.generator((_yield) => func( - val => _yield(['await', val]) as any, - val => _yield(['yield', val]) - )).apply(this, arguments as any); - - const next = (resolve: Function, reject: Function, type: 'none' | 'val' | 'ret' | 'err', val?: any) => { - let res; - - try { - switch (type) { - case 'val': res = gen.next(val); break; - case 'ret': res = gen.return(val); break; - case 'err': res = gen.throw(val); break; - default: res = gen.next(); break; - } + while (len >= 0) { + len--; + newArgs[len] = args[len]; } - catch (e) { return reject(e); } + } - if (res.done) return { done: true, res: res }; - else if (res.value[0] === 'await') Promise.resolve(res.value[1]).then( - v => next(resolve, reject, 'val', v), - v => next(resolve, reject, 'err', v), - ) - else resolve({ done: false, value: res.value[1] }); + return env.internals.apply(this, thisArg, newArgs); + }, + call(thisArg, ...args) { + return this.apply(thisArg, args); + }, + bind(thisArg, ...args) { + var func = this; + + var res = function() { + var resArgs = []; + + for (var i = 0; i < args.length; i++) { + resArgs[i] = args[i]; + } + for (var i = 0; i < arguments.length; i++) { + resArgs[i + args.length] = arguments[i]; + } + + return func.apply(thisArg, resArgs); }; + res.name = " " + func.name; + return res; + }, + toString() { + return 'function (...) { ... }'; + }, + }); + setProps(Function, env, { + async(func) { + if (typeof func !== 'function') throw new TypeError('Expected func to be function.'); - return { - next() { - const args = arguments; - if (arguments.length === 0) return new Promise((res, rej) => next(res, rej, 'none')); - else return new Promise((res, rej) => next(res, rej, 'val', args[0])); - }, - return: (value) => new Promise((res, rej) => next(res, rej, 'ret', value)), - throw: (value) => new Promise((res, rej) => next(res, rej, 'err', value)), - [Symbol.asyncIterator]() { return this; } + return function (this: any) { + const args = arguments; + + return new Promise((res, rej) => { + const gen = Function.generator(func as any).apply(this, args as any); + + (function next(type: 'none' | 'err' | 'ret', val?: any) { + try { + let result; + + switch (type) { + case 'err': result = gen.throw(val); break; + case 'ret': result = gen.next(val); break; + case 'none': result = gen.next(); break; + } + if (result.done) res(result.value); + else Promise.resolve(result.value).then( + v => next('ret', v), + v => next('err', v) + ) + } + catch (e) { + rej(e); + } + })('none'); + }); + }; + }, + asyncGenerator(func) { + if (typeof func !== 'function') throw new TypeError('Expected func to be function.'); + + return function(this: any) { + const gen = Function.generator((_yield) => func( + val => _yield(['await', val]) as any, + val => _yield(['yield', val]) + )).apply(this, arguments as any); + + const next = (resolve: Function, reject: Function, type: 'none' | 'val' | 'ret' | 'err', val?: any) => { + let res; + + try { + switch (type) { + case 'val': res = gen.next(val); break; + case 'ret': res = gen.return(val); break; + case 'err': res = gen.throw(val); break; + default: res = gen.next(); break; + } + } + catch (e) { return reject(e); } + + if (res.done) return { done: true, res: res }; + else if (res.value[0] === 'await') Promise.resolve(res.value[1]).then( + v => next(resolve, reject, 'val', v), + v => next(resolve, reject, 'err', v), + ) + else resolve({ done: false, value: res.value[1] }); + }; + + return { + next() { + const args = arguments; + if (arguments.length === 0) return new Promise((res, rej) => next(res, rej, 'none')); + else return new Promise((res, rej) => next(res, rej, 'val', args[0])); + }, + return: (value) => new Promise((res, rej) => next(res, rej, 'ret', value)), + throw: (value) => new Promise((res, rej) => next(res, rej, 'err', value)), + [Symbol.asyncIterator]() { return this; } + } + } + }, + generator(func) { + if (typeof func !== 'function') throw new TypeError('Expected func to be function.'); + const gen = env.internals.makeGenerator(func); + return (...args: any[]) => { + const it = gen(args); + + return { + next: it.next, + return: it.return, + throw: it.throw, + [Symbol.iterator]() { return this; } + } } } - }, - generator(func) { - if (typeof func !== 'function') throw new TypeError('Expected func to be function.'); - const gen = internals.makeGenerator(func); - return (...args: any[]) => { - const it = gen(args); - - return { - next: it.next, - return: it.return, - throw: it.throw, - [Symbol.iterator]() { return this; } - } - } - } -}) \ No newline at end of file + }) +}); \ No newline at end of file diff --git a/lib/values/number.ts b/lib/values/number.ts index 6b1dda6..11366a4 100644 --- a/lib/values/number.ts +++ b/lib/values/number.ts @@ -1,50 +1,33 @@ -interface Number { - toString(): string; - valueOf(): number; - constructor: NumberConstructor; -} -interface NumberConstructor { - (val: any): number; - new (val: any): Number; - prototype: Number; - parseInt(val: unknown): number; - parseFloat(val: unknown): number; -} +define("values/number", () => { + var Number = env.global.Number = function(this: Number | undefined, arg: any) { + var val; + if (arguments.length === 0) val = 0; + else val = arg - 0; + if (this === undefined || this === null) return val; + else (this as any).value = val; + } as NumberConstructor; -declare var Number: NumberConstructor; -declare var parseInt: typeof Number.parseInt; -declare var parseFloat: typeof Number.parseFloat; -declare var NaN: number; -declare var Infinity: number; + Number.prototype = (0 as any).__proto__ as Number; + setConstr(Number.prototype, Number, env); -gt.Number = function(this: Number | undefined, arg: any) { - var val; - if (arguments.length === 0) val = 0; - else val = arg - 0; - if (this === undefined || this === null) return val; - else (this as any).value = val; -} as NumberConstructor; + setProps(Number.prototype, env, { + valueOf() { + if (typeof this === 'number') return this; + else return (this as any).value; + }, + toString() { + if (typeof this === 'number') return this + ''; + else return (this as any).value + ''; + } + }); -Number.prototype = (0 as any).__proto__ as Number; -setConstr(Number.prototype, Number); + setProps(Number, env, { + parseInt(val) { return Math.trunc(Number.parseFloat(val)); }, + parseFloat(val) { return env.internals.parseFloat(val); }, + }); -setProps(Number.prototype, { - valueOf() { - if (typeof this === 'number') return this; - else return (this as any).value; - }, - toString() { - if (typeof this === 'number') return this + ''; - else return (this as any).value + ''; - } -}); - -setProps(Number, { - parseInt(val) { return Math.trunc(Number.parseFloat(val)); }, - parseFloat(val) { return internals.parseFloat(val); }, -}); - -Object.defineProperty(gt, 'parseInt', { value: Number.parseInt, writable: false }); -Object.defineProperty(gt, 'parseFloat', { value: Number.parseFloat, writable: false }); -Object.defineProperty(gt, 'NaN', { value: 0 / 0, writable: false }); -Object.defineProperty(gt, 'Infinity', { value: 1 / 0, writable: false }); + env.global.Object.defineProperty(env.global, 'parseInt', { value: Number.parseInt, writable: false }); + env.global.Object.defineProperty(env.global, 'parseFloat', { value: Number.parseFloat, writable: false }); + env.global.Object.defineProperty(env.global, 'NaN', { value: 0 / 0, writable: false }); + env.global.Object.defineProperty(env.global, 'Infinity', { value: 1 / 0, writable: false }); +}); \ No newline at end of file diff --git a/lib/values/object.ts b/lib/values/object.ts index 6d33f03..4e63a39 100644 --- a/lib/values/object.ts +++ b/lib/values/object.ts @@ -1,234 +1,178 @@ - interface Object { - constructor: NewableFunction; - [Symbol.typeName]: string; +/** @internal */ +define("values/object", () => { + var Object = env.global.Object = function(arg: any) { + if (arg === undefined || arg === null) return {}; + else if (typeof arg === 'boolean') return new Boolean(arg); + else if (typeof arg === 'number') return new Number(arg); + else if (typeof arg === 'string') return new String(arg); + return arg; + } as ObjectConstructor; - valueOf(): this; - toString(): string; - hasOwnProperty(key: any): boolean; -} -interface ObjectConstructor extends Function { - (arg: string): String; - (arg: number): Number; - (arg: boolean): Boolean; - (arg?: undefined | null): {}; - (arg: T): T; + Object.prototype = ({} as any).__proto__ as Object; + setConstr(Object.prototype, Object as any, env); - new (arg: string): String; - new (arg: number): Number; - new (arg: boolean): Boolean; - new (arg?: undefined | null): {}; - new (arg: T): T; - - prototype: Object; - - assign(target: T, ...src: object[]): T; - create(proto: T, props?: { [key: string]: PropertyDescriptor }): T; - - keys(obj: T, onlyString?: true): (keyof T)[]; - keys(obj: T, onlyString: false): any[]; - entries(obj: T, onlyString?: true): [keyof T, T[keyof T]][]; - entries(obj: T, onlyString: false): [any, any][]; - values(obj: T, onlyString?: true): (T[keyof T])[]; - values(obj: T, onlyString: false): any[]; - - fromEntries(entries: Iterable<[any, any]>): object; - - defineProperty(obj: ThisT, key: any, desc: PropertyDescriptor): ThisT; - defineProperties(obj: ThisT, desc: { [key: string]: PropertyDescriptor }): ThisT; - - getOwnPropertyNames(obj: T): (keyof T)[]; - getOwnPropertySymbols(obj: T): (keyof T)[]; - hasOwn(obj: T, key: KeyT): boolean; - - getOwnPropertyDescriptor(obj: T, key: KeyT): PropertyDescriptor; - getOwnPropertyDescriptors(obj: T): { [x in keyof T]: PropertyDescriptor }; - - getPrototypeOf(obj: any): object | null; - setPrototypeOf(obj: T, proto: object | null): T; - - preventExtensions(obj: T): T; - seal(obj: T): T; - freeze(obj: T): T; - - isExtensible(obj: object): boolean; - isSealed(obj: object): boolean; - isFrozen(obj: object): boolean; -} - -declare var Object: ObjectConstructor; - -gt.Object = function(arg: any) { - if (arg === undefined || arg === null) return {}; - else if (typeof arg === 'boolean') return new Boolean(arg); - else if (typeof arg === 'number') return new Number(arg); - else if (typeof arg === 'string') return new String(arg); - return arg; -} as ObjectConstructor; - -Object.prototype = ({} as any).__proto__ as Object; -setConstr(Object.prototype, Object as any); - -function throwNotObject(obj: any, name: string) { - if (obj === null || typeof obj !== 'object' && typeof obj !== 'function') { - throw new TypeError(`Object.${name} may only be used for objects.`); + function throwNotObject(obj: any, name: string) { + if (obj === null || typeof obj !== 'object' && typeof obj !== 'function') { + throw new TypeError(`Object.${name} may only be used for objects.`); + } + } + function check(obj: any) { + return typeof obj === 'object' && obj !== null || typeof obj === 'function'; } -} -function check(obj: any) { - return typeof obj === 'object' && obj !== null || typeof obj === 'function'; -} -setProps(Object, { - assign: function(dst, ...src) { - throwNotObject(dst, 'assign'); - for (let i = 0; i < src.length; i++) { - const obj = src[i]; - throwNotObject(obj, 'assign'); - for (const key of Object.keys(obj)) { - (dst as any)[key] = (obj as any)[key]; + setProps(Object, env, { + assign: function(dst, ...src) { + throwNotObject(dst, 'assign'); + for (let i = 0; i < src.length; i++) { + const obj = src[i]; + throwNotObject(obj, 'assign'); + for (const key of Object.keys(obj)) { + (dst as any)[key] = (obj as any)[key]; + } } + return dst; + }, + create(obj, props) { + props ??= {}; + return Object.defineProperties({ __proto__: obj }, props as any) as any; + }, + + defineProperty(obj, key, attrib) { + throwNotObject(obj, 'defineProperty'); + if (typeof attrib !== 'object') throw new TypeError('Expected attributes to be an object.'); + + if ('value' in attrib) { + if ('get' in attrib || 'set' in attrib) throw new TypeError('Cannot specify a value and accessors for a property.'); + if (!env.internals.defineField( + obj, key, + attrib.value, + !!attrib.writable, + !!attrib.enumerable, + !!attrib.configurable + )) throw new TypeError('Can\'t define property \'' + key + '\'.'); + } + else { + if (typeof attrib.get !== 'function' && attrib.get !== undefined) throw new TypeError('Get accessor must be a function.'); + if (typeof attrib.set !== 'function' && attrib.set !== undefined) throw new TypeError('Set accessor must be a function.'); + + if (!env.internals.defineProp( + obj, key, + attrib.get, + attrib.set, + !!attrib.enumerable, + !!attrib.configurable + )) throw new TypeError('Can\'t define property \'' + key + '\'.'); + } + + return obj; + }, + defineProperties(obj, attrib) { + throwNotObject(obj, 'defineProperties'); + if (typeof attrib !== 'object' && typeof attrib !== 'function') throw 'Expected second argument to be an object.'; + + for (var key in attrib) { + Object.defineProperty(obj, key, attrib[key]); + } + + return obj; + }, + + keys(obj, onlyString) { + onlyString = !!(onlyString ?? true); + return env.internals.keys(obj, onlyString); + }, + entries(obj, onlyString) { + return Object.keys(obj, onlyString).map(v => [ v, (obj as any)[v] ]); + }, + values(obj, onlyString) { + return Object.keys(obj, onlyString).map(v => (obj as any)[v]); + }, + + getOwnPropertyDescriptor(obj, key) { + return env.internals.ownProp(obj, key); + }, + getOwnPropertyDescriptors(obj) { + return Object.fromEntries([ + ...Object.getOwnPropertyNames(obj), + ...Object.getOwnPropertySymbols(obj) + ].map(v => [ v, Object.getOwnPropertyDescriptor(obj, v) ])) as any; + }, + + getOwnPropertyNames(obj) { + return env.internals.ownPropKeys(obj, false); + }, + getOwnPropertySymbols(obj) { + return env.internals.ownPropKeys(obj, true); + }, + hasOwn(obj, key) { + if (Object.getOwnPropertyNames(obj).includes(key)) return true; + if (Object.getOwnPropertySymbols(obj).includes(key)) return true; + return false; + }, + + getPrototypeOf(obj) { + return obj.__proto__; + }, + setPrototypeOf(obj, proto) { + (obj as any).__proto__ = proto; + return obj; + }, + + fromEntries(iterable) { + const res = {} as any; + + for (const el of iterable) { + res[el[0]] = el[1]; + } + + return res; + }, + + preventExtensions(obj) { + throwNotObject(obj, 'preventExtensions'); + env.internals.preventExtensions(obj); + return obj; + }, + seal(obj) { + throwNotObject(obj, 'seal'); + env.internals.seal(obj); + return obj; + }, + freeze(obj) { + throwNotObject(obj, 'freeze'); + env.internals.freeze(obj); + return obj; + }, + + isExtensible(obj) { + if (!check(obj)) return false; + return env.internals.extensible(obj); + }, + isSealed(obj) { + if (!check(obj)) return true; + if (Object.isExtensible(obj)) return false; + return Object.getOwnPropertyNames(obj).every(v => !Object.getOwnPropertyDescriptor(obj, v).configurable); + }, + isFrozen(obj) { + if (!check(obj)) return true; + if (Object.isExtensible(obj)) return false; + return Object.getOwnPropertyNames(obj).every(v => { + var prop = Object.getOwnPropertyDescriptor(obj, v); + if ('writable' in prop && prop.writable) return false; + return !prop.configurable; + }); } - return dst; - }, - create(obj, props) { - props ??= {}; - return Object.defineProperties({ __proto__: obj }, props as any) as any; - }, + }); - defineProperty(obj, key, attrib) { - throwNotObject(obj, 'defineProperty'); - if (typeof attrib !== 'object') throw new TypeError('Expected attributes to be an object.'); - - if ('value' in attrib) { - if ('get' in attrib || 'set' in attrib) throw new TypeError('Cannot specify a value and accessors for a property.'); - if (!internals.defineField( - obj, key, - attrib.value, - !!attrib.writable, - !!attrib.enumerable, - !!attrib.configurable - )) throw new TypeError('Can\'t define property \'' + key + '\'.'); - } - else { - if (typeof attrib.get !== 'function' && attrib.get !== undefined) throw new TypeError('Get accessor must be a function.'); - if (typeof attrib.set !== 'function' && attrib.set !== undefined) throw new TypeError('Set accessor must be a function.'); - - if (!internals.defineProp( - obj, key, - attrib.get, - attrib.set, - !!attrib.enumerable, - !!attrib.configurable - )) throw new TypeError('Can\'t define property \'' + key + '\'.'); - } - - return obj; - }, - defineProperties(obj, attrib) { - throwNotObject(obj, 'defineProperties'); - if (typeof attrib !== 'object' && typeof attrib !== 'function') throw 'Expected second argument to be an object.'; - - for (var key in attrib) { - Object.defineProperty(obj, key, attrib[key]); - } - - return obj; - }, - - keys(obj, onlyString) { - onlyString = !!(onlyString ?? true); - return internals.keys(obj, onlyString); - }, - entries(obj, onlyString) { - return Object.keys(obj, onlyString).map(v => [ v, (obj as any)[v] ]); - }, - values(obj, onlyString) { - return Object.keys(obj, onlyString).map(v => (obj as any)[v]); - }, - - getOwnPropertyDescriptor(obj, key) { - return internals.ownProp(obj, key); - }, - getOwnPropertyDescriptors(obj) { - return Object.fromEntries([ - ...Object.getOwnPropertyNames(obj), - ...Object.getOwnPropertySymbols(obj) - ].map(v => [ v, Object.getOwnPropertyDescriptor(obj, v) ])) as any; - }, - - getOwnPropertyNames(obj) { - return internals.ownPropKeys(obj, false); - }, - getOwnPropertySymbols(obj) { - return internals.ownPropKeys(obj, true); - }, - hasOwn(obj, key) { - if (Object.getOwnPropertyNames(obj).includes(key)) return true; - if (Object.getOwnPropertySymbols(obj).includes(key)) return true; - return false; - }, - - getPrototypeOf(obj) { - return obj.__proto__; - }, - setPrototypeOf(obj, proto) { - (obj as any).__proto__ = proto; - return obj; - }, - - fromEntries(iterable) { - const res = {} as any; - - for (const el of iterable) { - res[el[0]] = el[1]; - } - - return res; - }, - - preventExtensions(obj) { - throwNotObject(obj, 'preventExtensions'); - internals.preventExtensions(obj); - return obj; - }, - seal(obj) { - throwNotObject(obj, 'seal'); - internals.seal(obj); - return obj; - }, - freeze(obj) { - throwNotObject(obj, 'freeze'); - internals.freeze(obj); - return obj; - }, - - isExtensible(obj) { - if (!check(obj)) return false; - return internals.extensible(obj); - }, - isSealed(obj) { - if (!check(obj)) return true; - if (Object.isExtensible(obj)) return false; - return Object.getOwnPropertyNames(obj).every(v => !Object.getOwnPropertyDescriptor(obj, v).configurable); - }, - isFrozen(obj) { - if (!check(obj)) return true; - if (Object.isExtensible(obj)) return false; - return Object.getOwnPropertyNames(obj).every(v => { - var prop = Object.getOwnPropertyDescriptor(obj, v); - if ('writable' in prop && prop.writable) return false; - return !prop.configurable; - }); - } -}); - -setProps(Object.prototype, { - valueOf() { - return this; - }, - toString() { - return '[object ' + (this[Symbol.typeName] ?? 'Unknown') + ']'; - }, - hasOwnProperty(key) { - return Object.hasOwn(this, key); - }, -}); + setProps(Object.prototype, env, { + valueOf() { + return this; + }, + toString() { + return '[object ' + (this[Symbol.typeName] ?? 'Unknown') + ']'; + }, + hasOwnProperty(key) { + return Object.hasOwn(this, key); + }, + }); +}); \ No newline at end of file diff --git a/lib/values/string.ts b/lib/values/string.ts index 8ec19e3..95023ff 100644 --- a/lib/values/string.ts +++ b/lib/values/string.ts @@ -1,261 +1,210 @@ -interface Replacer { - [Symbol.replace](target: string, val: string | ((match: string, ...args: any[]) => string)): string; -} +define("values/string", () => { + var String = env.global.String = function(this: String | undefined, arg: any) { + var val; + if (arguments.length === 0) val = ''; + else val = arg + ''; + if (this === undefined || this === null) return val; + else (this as any).value = val; + } as StringConstructor; -interface String { - [i: number]: string; + String.prototype = ('' as any).__proto__ as String; + setConstr(String.prototype, String, env); - toString(): string; - valueOf(): string; + setProps(String.prototype, env, { + toString() { + if (typeof this === 'string') return this; + else return (this as any).value; + }, + valueOf() { + if (typeof this === 'string') return this; + else return (this as any).value; + }, - charAt(pos: number): string; - charCodeAt(pos: number): number; - substring(start?: number, end?: number): string; - slice(start?: number, end?: number): string; - substr(start?: number, length?: number): string; + substring(start, end) { + if (typeof this !== 'string') { + if (this instanceof String) return (this as any).value.substring(start, end); + else throw new Error('This function may be used only with primitive or object strings.'); + } + start = start ?? 0 | 0; + end = (end ?? this.length) | 0; + return env.internals.substring(this, start, end); + }, + substr(start, length) { + start = start ?? 0 | 0; - startsWith(str: string, pos?: number): string; - endsWith(str: string, pos?: number): string; + if (start >= this.length) start = this.length - 1; + if (start < 0) start = 0; - replace(pattern: string | Replacer, val: string): string; - replaceAll(pattern: string | Replacer, val: string): string; + length = (length ?? this.length - start) | 0; + return this.substring(start, length + start); + }, - match(pattern: string | Matcher): RegExpResult | string[] | null; - matchAll(pattern: string | Matcher): IterableIterator; + toLowerCase() { + return env.internals.toLower(this + ''); + }, + toUpperCase() { + return env.internals.toUpper(this + ''); + }, - split(pattern: string | Splitter, limit?: number, sensible?: boolean): string; + charAt(pos) { + if (typeof this !== 'string') { + if (this instanceof String) return (this as any).value.charAt(pos); + else throw new Error('This function may be used only with primitive or object strings.'); + } - concat(...others: string[]): string; - indexOf(term: string | Searcher, start?: number): number; - lastIndexOf(term: string | Searcher, start?: number): number; + pos = pos | 0; + if (pos < 0 || pos >= this.length) return ''; + return this[pos]; + }, + charCodeAt(pos) { + var res = this.charAt(pos); + if (res === '') return NaN; + else return env.internals.toCharCode(res); + }, - toLowerCase(): string; - toUpperCase(): string; + startsWith(term, pos) { + if (typeof this !== 'string') { + if (this instanceof String) return (this as any).value.startsWith(term, pos); + else throw new Error('This function may be used only with primitive or object strings.'); + } + pos = pos! | 0; + return env.internals.startsWith(this, term + '', pos); + }, + endsWith(term, pos) { + if (typeof this !== 'string') { + if (this instanceof String) return (this as any).value.endsWith(term, pos); + else throw new Error('This function may be used only with primitive or object strings.'); + } + pos = (pos ?? this.length) | 0; + return env.internals.endsWith(this, term + '', pos); + }, - trim(): string; + indexOf(term: any, start) { + if (typeof this !== 'string') { + if (this instanceof String) return (this as any).value.indexOf(term, start); + else throw new Error('This function may be used only with primitive or object strings.'); + } - includes(term: string, start?: number): boolean; + if (typeof term[Symbol.search] !== 'function') term = RegExp.escape(term); - length: number; + return term[Symbol.search](this, false, start); + }, + lastIndexOf(term: any, start) { + if (typeof this !== 'string') { + if (this instanceof String) return (this as any).value.indexOf(term, start); + else throw new Error('This function may be used only with primitive or object strings.'); + } - constructor: StringConstructor; -} -interface StringConstructor { - (val: any): string; - new (val: any): String; + if (typeof term[Symbol.search] !== 'function') term = RegExp.escape(term); - fromCharCode(val: number): string; + return term[Symbol.search](this, true, start); + }, + includes(term, start) { + return this.indexOf(term, start) >= 0; + }, - prototype: String; -} + replace(pattern: any, val) { + if (typeof this !== 'string') { + if (this instanceof String) return (this as any).value.replace(pattern, val); + else throw new Error('This function may be used only with primitive or object strings.'); + } -declare var String: StringConstructor; + if (typeof pattern[Symbol.replace] !== 'function') pattern = RegExp.escape(pattern); -gt.String = function(this: String | undefined, arg: any) { - var val; - if (arguments.length === 0) val = ''; - else val = arg + ''; - if (this === undefined || this === null) return val; - else (this as any).value = val; -} as StringConstructor; + return pattern[Symbol.replace](this, val); + }, + replaceAll(pattern: any, val) { + if (typeof this !== 'string') { + if (this instanceof String) return (this as any).value.replace(pattern, val); + else throw new Error('This function may be used only with primitive or object strings.'); + } -String.prototype = ('' as any).__proto__ as String; -setConstr(String.prototype, String); + if (typeof pattern[Symbol.replace] !== 'function') pattern = RegExp.escape(pattern, "g"); + if (pattern instanceof RegExp && !pattern.global) pattern = new pattern.constructor(pattern.source, pattern.flags + "g"); -setProps(String.prototype, { - toString() { - if (typeof this === 'string') return this; - else return (this as any).value; - }, - valueOf() { - if (typeof this === 'string') return this; - else return (this as any).value; - }, + return pattern[Symbol.replace](this, val); + }, - substring(start, end) { - if (typeof this !== 'string') { - if (this instanceof String) return (this as any).value.substring(start, end); - else throw new Error('This function may be used only with primitive or object strings.'); + match(pattern: any) { + if (typeof this !== 'string') { + if (this instanceof String) return (this as any).value.match(pattern); + else throw new Error('This function may be used only with primitive or object strings.'); + } + + if (typeof pattern[Symbol.match] !== 'function') pattern = RegExp.escape(pattern); + + return pattern[Symbol.match](this); + }, + matchAll(pattern: any) { + if (typeof this !== 'string') { + if (this instanceof String) return (this as any).value.matchAll(pattern); + else throw new Error('This function may be used only with primitive or object strings.'); + } + + if (typeof pattern[Symbol.match] !== 'function') pattern = RegExp.escape(pattern, "g"); + if (pattern instanceof RegExp && !pattern.global) pattern = new pattern.constructor(pattern.source, pattern.flags + "g"); + + return pattern[Symbol.match](this); + }, + + split(pattern: any, lim, sensible) { + if (typeof this !== 'string') { + if (this instanceof String) return (this as any).value.split(pattern, lim, sensible); + else throw new Error('This function may be used only with primitive or object strings.'); + } + + if (typeof pattern[Symbol.split] !== 'function') pattern = RegExp.escape(pattern, "g"); + + return pattern[Symbol.split](this, lim, sensible); + }, + slice(start, end) { + if (typeof this !== 'string') { + if (this instanceof String) return (this as any).value.slice(start, end); + else throw new Error('This function may be used only with primitive or object strings.'); + } + + start = wrapI(this.length, start ?? 0 | 0); + end = wrapI(this.length, end ?? this.length | 0); + + if (start > end) return ''; + + return this.substring(start, end); + }, + + concat(...args) { + if (typeof this !== 'string') { + if (this instanceof String) return (this as any).value.concat(...args); + else throw new Error('This function may be used only with primitive or object strings.'); + } + + var res = this; + for (var arg of args) res += arg; + return res; + }, + + trim() { + return this + .replace(/^\s+/g, '') + .replace(/\s+$/g, ''); } - start = start ?? 0 | 0; - end = (end ?? this.length) | 0; - return internals.substring(this, start, end); - }, - substr(start, length) { - start = start ?? 0 | 0; + }); - if (start >= this.length) start = this.length - 1; - if (start < 0) start = 0; + setProps(String, env, { + fromCharCode(val) { + return env.internals.fromCharCode(val | 0); + }, + }) - length = (length ?? this.length - start) | 0; - return this.substring(start, length + start); - }, + env.global.Object.defineProperty(String.prototype, 'length', { + get() { + if (typeof this !== 'string') { + if (this instanceof String) return (this as any).value.length; + else throw new Error('This function may be used only with primitive or object strings.'); + } - toLowerCase() { - return internals.toLower(this + ''); - }, - toUpperCase() { - return internals.toUpper(this + ''); - }, - - charAt(pos) { - if (typeof this !== 'string') { - if (this instanceof String) return (this as any).value.charAt(pos); - else throw new Error('This function may be used only with primitive or object strings.'); - } - - pos = pos | 0; - if (pos < 0 || pos >= this.length) return ''; - return this[pos]; - }, - charCodeAt(pos) { - var res = this.charAt(pos); - if (res === '') return NaN; - else return internals.toCharCode(res); - }, - - startsWith(term, pos) { - if (typeof this !== 'string') { - if (this instanceof String) return (this as any).value.startsWith(term, pos); - else throw new Error('This function may be used only with primitive or object strings.'); - } - pos = pos! | 0; - return internals.startsWith(this, term + '', pos); - }, - endsWith(term, pos) { - if (typeof this !== 'string') { - if (this instanceof String) return (this as any).value.endsWith(term, pos); - else throw new Error('This function may be used only with primitive or object strings.'); - } - pos = (pos ?? this.length) | 0; - return internals.endsWith(this, term + '', pos); - }, - - indexOf(term: any, start) { - if (typeof this !== 'string') { - if (this instanceof String) return (this as any).value.indexOf(term, start); - else throw new Error('This function may be used only with primitive or object strings.'); - } - - if (typeof term[Symbol.search] !== 'function') term = RegExp.escape(term); - - return term[Symbol.search](this, false, start); - }, - lastIndexOf(term: any, start) { - if (typeof this !== 'string') { - if (this instanceof String) return (this as any).value.indexOf(term, start); - else throw new Error('This function may be used only with primitive or object strings.'); - } - - if (typeof term[Symbol.search] !== 'function') term = RegExp.escape(term); - - return term[Symbol.search](this, true, start); - }, - includes(term, start) { - return this.indexOf(term, start) >= 0; - }, - - replace(pattern: any, val) { - if (typeof this !== 'string') { - if (this instanceof String) return (this as any).value.replace(pattern, val); - else throw new Error('This function may be used only with primitive or object strings.'); - } - - if (typeof pattern[Symbol.replace] !== 'function') pattern = RegExp.escape(pattern); - - return pattern[Symbol.replace](this, val); - }, - replaceAll(pattern: any, val) { - if (typeof this !== 'string') { - if (this instanceof String) return (this as any).value.replace(pattern, val); - else throw new Error('This function may be used only with primitive or object strings.'); - } - - if (typeof pattern[Symbol.replace] !== 'function') pattern = RegExp.escape(pattern, "g"); - if (pattern instanceof RegExp && !pattern.global) pattern = new pattern.constructor(pattern.source, pattern.flags + "g"); - - return pattern[Symbol.replace](this, val); - }, - - match(pattern: any) { - if (typeof this !== 'string') { - if (this instanceof String) return (this as any).value.match(pattern); - else throw new Error('This function may be used only with primitive or object strings.'); - } - - if (typeof pattern[Symbol.match] !== 'function') pattern = RegExp.escape(pattern); - - return pattern[Symbol.match](this); - }, - matchAll(pattern: any) { - if (typeof this !== 'string') { - if (this instanceof String) return (this as any).value.matchAll(pattern); - else throw new Error('This function may be used only with primitive or object strings.'); - } - - if (typeof pattern[Symbol.match] !== 'function') pattern = RegExp.escape(pattern, "g"); - if (pattern instanceof RegExp && !pattern.global) pattern = new pattern.constructor(pattern.source, pattern.flags + "g"); - - return pattern[Symbol.match](this); - }, - - split(pattern: any, lim, sensible) { - if (typeof this !== 'string') { - if (this instanceof String) return (this as any).value.split(pattern, lim, sensible); - else throw new Error('This function may be used only with primitive or object strings.'); - } - - if (typeof pattern[Symbol.split] !== 'function') pattern = RegExp.escape(pattern, "g"); - - return pattern[Symbol.split](this, lim, sensible); - }, - slice(start, end) { - if (typeof this !== 'string') { - if (this instanceof String) return (this as any).value.slice(start, end); - else throw new Error('This function may be used only with primitive or object strings.'); - } - - start = wrapI(this.length, start ?? 0 | 0); - end = wrapI(this.length, end ?? this.length | 0); - - if (start > end) return ''; - - return this.substring(start, end); - }, - - concat(...args) { - if (typeof this !== 'string') { - if (this instanceof String) return (this as any).value.concat(...args); - else throw new Error('This function may be used only with primitive or object strings.'); - } - - var res = this; - for (var arg of args) res += arg; - return res; - }, - - trim() { - return this - .replace(/^\s+/g, '') - .replace(/\s+$/g, ''); - } -}); - -setProps(String, { - fromCharCode(val) { - return internals.fromCharCode(val | 0); - }, -}) - -Object.defineProperty(String.prototype, 'length', { - get() { - if (typeof this !== 'string') { - if (this instanceof String) return (this as any).value.length; - else throw new Error('This function may be used only with primitive or object strings.'); - } - - return internals.strlen(this); - }, - configurable: true, - enumerable: false, -}); + return env.internals.strlen(this); + }, + configurable: true, + enumerable: false, + }); +}); \ No newline at end of file diff --git a/lib/values/symbol.ts b/lib/values/symbol.ts index ef0a02c..0258b75 100644 --- a/lib/values/symbol.ts +++ b/lib/values/symbol.ts @@ -1,38 +1,33 @@ -interface Symbol { - valueOf(): symbol; - constructor: SymbolConstructor; -} -interface SymbolConstructor { - (val?: any): symbol; - prototype: Symbol; - for(key: string): symbol; - keyFor(sym: symbol): string; - readonly typeName: unique symbol; -} +define("values/symbol", () => { + var Symbol = env.global.Symbol = function(this: any, val?: string) { + if (this !== undefined && this !== null) throw new TypeError("Symbol may not be called with 'new'."); + if (typeof val !== 'string' && val !== undefined) throw new TypeError('val must be a string or undefined.'); + return env.internals.symbol(val, true); + } as SymbolConstructor; -declare var Symbol: SymbolConstructor; + Symbol.prototype = env.internals.symbolProto; + setConstr(Symbol.prototype, Symbol, env); + (Symbol as any).typeName = Symbol("Symbol.name"); + (Symbol as any).replace = Symbol('Symbol.replace'); + (Symbol as any).match = Symbol('Symbol.match'); + (Symbol as any).matchAll = Symbol('Symbol.matchAll'); + (Symbol as any).split = Symbol('Symbol.split'); + (Symbol as any).search = Symbol('Symbol.search'); + (Symbol as any).iterator = Symbol('Symbol.iterator'); + (Symbol as any).asyncIterator = Symbol('Symbol.asyncIterator'); -gt.Symbol = function(this: any, val?: string) { - if (this !== undefined && this !== null) throw new TypeError("Symbol may not be called with 'new'."); - if (typeof val !== 'string' && val !== undefined) throw new TypeError('val must be a string or undefined.'); - return internals.symbol(val, true); -} as SymbolConstructor; + setProps(Symbol, env, { + for(key) { + if (typeof key !== 'string' && key !== undefined) throw new TypeError('key must be a string or undefined.'); + return env.internals.symbol(key, false); + }, + keyFor(sym) { + if (typeof sym !== 'symbol') throw new TypeError('sym must be a symbol.'); + return env.internals.symStr(sym); + }, + typeName: Symbol('Symbol.name') as any, + }); -Symbol.prototype = internals.symbolProto; -setConstr(Symbol.prototype, Symbol); -(Symbol as any).typeName = Symbol("Symbol.name"); - -setProps(Symbol, { - for(key) { - if (typeof key !== 'string' && key !== undefined) throw new TypeError('key must be a string or undefined.'); - return internals.symbol(key, false); - }, - keyFor(sym) { - if (typeof sym !== 'symbol') throw new TypeError('sym must be a symbol.'); - return internals.symStr(sym); - }, - typeName: Symbol('Symbol.name') as any, -}); - -Object.defineProperty(Object.prototype, Symbol.typeName, { value: 'Object' }); -Object.defineProperty(gt, Symbol.typeName, { value: 'Window' }); + env.global.Object.defineProperty(Object.prototype, Symbol.typeName, { value: 'Object' }); + env.global.Object.defineProperty(env.global, Symbol.typeName, { value: 'Window' }); +}); \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/Main.java b/src/me/topchetoeu/jscript/Main.java index 34a2426..95f00f2 100644 --- a/src/me/topchetoeu/jscript/Main.java +++ b/src/me/topchetoeu/jscript/Main.java @@ -1,7 +1,6 @@ package me.topchetoeu.jscript; import java.io.BufferedReader; -import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.nio.file.Files; @@ -13,8 +12,6 @@ import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.events.Observer; import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.exceptions.SyntaxException; -import me.topchetoeu.jscript.polyfills.PolyfillEngine; -import me.topchetoeu.jscript.polyfills.TypescriptEngine; public class Main { static Thread task; @@ -54,7 +51,7 @@ public class Main { public static void main(String args[]) { var in = new BufferedReader(new InputStreamReader(System.in)); - engine = new TypescriptEngine(new File(".")); + engine = new Engine(); var scope = engine.global().globalChild(); var exited = new boolean[1]; diff --git a/src/me/topchetoeu/jscript/compilation/values/OperationStatement.java b/src/me/topchetoeu/jscript/compilation/values/OperationStatement.java index 114f7b6..ab56757 100644 --- a/src/me/topchetoeu/jscript/compilation/values/OperationStatement.java +++ b/src/me/topchetoeu/jscript/compilation/values/OperationStatement.java @@ -6,7 +6,6 @@ import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.control.ThrowStatement; -import me.topchetoeu.jscript.engine.CallContext; import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.values.Values; @@ -52,40 +51,7 @@ public class OperationStatement extends Statement { } try { - var ctx = new CallContext(null); - - switch (operation) { - case ADD: return new ConstantStatement(loc(), Values.add(ctx, vals[0], vals[1])); - case SUBTRACT: return new ConstantStatement(loc(), Values.subtract(ctx, vals[0], vals[1])); - case DIVIDE: return new ConstantStatement(loc(), Values.divide(ctx, vals[0], vals[1])); - case MULTIPLY: return new ConstantStatement(loc(), Values.multiply(ctx, vals[0], vals[1])); - case MODULO: return new ConstantStatement(loc(), Values.modulo(ctx, vals[0], vals[1])); - - case AND: return new ConstantStatement(loc(), Values.and(ctx, vals[0], vals[1])); - case OR: return new ConstantStatement(loc(), Values.or(ctx, vals[0], vals[1])); - case XOR: return new ConstantStatement(loc(), Values.xor(ctx, vals[0], vals[1])); - - case EQUALS: return new ConstantStatement(loc(), Values.strictEquals(vals[0], vals[1])); - case NOT_EQUALS: return new ConstantStatement(loc(), !Values.strictEquals(vals[0], vals[1])); - case LOOSE_EQUALS: return new ConstantStatement(loc(), Values.looseEqual(ctx, vals[0], vals[1])); - case LOOSE_NOT_EQUALS: return new ConstantStatement(loc(), !Values.looseEqual(ctx, vals[0], vals[1])); - - case GREATER: return new ConstantStatement(loc(), Values.compare(ctx, vals[0], vals[1]) < 0); - case GREATER_EQUALS: return new ConstantStatement(loc(), Values.compare(ctx, vals[0], vals[1]) <= 0); - case LESS: return new ConstantStatement(loc(), Values.compare(ctx, vals[0], vals[1]) > 0); - case LESS_EQUALS: return new ConstantStatement(loc(), Values.compare(ctx, vals[0], vals[1]) >= 0); - - case INVERSE: return new ConstantStatement(loc(), Values.bitwiseNot(ctx, vals[0])); - case NOT: return new ConstantStatement(loc(), Values.not(vals[0])); - case POS: return new ConstantStatement(loc(), Values.toNumber(ctx, vals[0])); - case NEG: return new ConstantStatement(loc(), Values.negative(ctx, vals[0])); - - case SHIFT_LEFT: return new ConstantStatement(loc(), Values.shiftLeft(ctx, vals[0], vals[1])); - case SHIFT_RIGHT: return new ConstantStatement(loc(), Values.shiftRight(ctx, vals[0], vals[1])); - case USHIFT_RIGHT: return new ConstantStatement(loc(), Values.unsignedShiftRight(ctx, vals[0], vals[1])); - - default: break; - } + return new ConstantStatement(loc(), Values.operation(null, operation, vals)); } catch (EngineException e) { return new ThrowStatement(loc(), new ConstantStatement(loc(), e.value)); diff --git a/src/me/topchetoeu/jscript/engine/Engine.java b/src/me/topchetoeu/jscript/engine/Engine.java index cb21212..111e66a 100644 --- a/src/me/topchetoeu/jscript/engine/Engine.java +++ b/src/me/topchetoeu/jscript/engine/Engine.java @@ -142,13 +142,6 @@ public class Engine { } } - public void exposeClass(String name, Class clazz) { - global.define(name, true, typeRegister.getConstr(clazz)); - } - public void exposeNamespace(String name, Class clazz) { - global.define(name, true, NativeTypeRegister.makeNamespace(clazz)); - } - public Thread start() { if (this.thread == null) { this.thread = new Thread(this::run, "JavaScript Runner #" + id); @@ -176,6 +169,9 @@ public class Engine { public ObjectValue getPrototype(Class clazz) { return typeRegister.getProto(clazz); } + public FunctionValue getConstructor(Class clazz) { + return typeRegister.getConstr(clazz); + } public CallContext context() { return new CallContext(this).mergeData(callCtxVals); } public Awaitable pushMsg(boolean micro, FunctionValue func, Map, Object> data, Object thisArg, Object... args) { diff --git a/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java b/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java index 1101c25..702a9a4 100644 --- a/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java +++ b/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java @@ -93,13 +93,13 @@ public class CodeFrame { return res; } - public void push(Object val) { + public void push(CallContext ctx, Object val) { if (stack.length <= stackPtr) { var newStack = new Object[stack.length * 2]; System.arraycopy(stack, 0, newStack, 0, stack.length); stack = newStack; } - stack[stackPtr++] = Values.normalize(val); + stack[stackPtr++] = Values.normalize(ctx, val); } public void start(CallContext ctx) { @@ -150,7 +150,7 @@ public class CodeFrame { try { this.jumpFlag = false; - return Runners.exec(debugCmd, instr, this, ctx); + return Runners.exec(ctx, debugCmd, instr, this); } catch (EngineException e) { throw e.add(function.name, prevLoc); @@ -307,13 +307,13 @@ public class CodeFrame { } } - public CodeFrame(Object thisArg, Object[] args, CodeFunction func) { + public CodeFrame(CallContext ctx, Object thisArg, Object[] args, CodeFunction func) { this.args = args.clone(); this.scope = new LocalScope(func.localsN, func.captures); this.scope.get(0).set(null, thisArg); var argsObj = new ArrayValue(); for (var i = 0; i < args.length; i++) { - argsObj.set(i, args[i]); + argsObj.set(ctx, i, args[i]); } this.scope.get(1).value = argsObj; diff --git a/src/me/topchetoeu/jscript/engine/frame/Runners.java b/src/me/topchetoeu/jscript/engine/frame/Runners.java index 1e0511f..c59cb0f 100644 --- a/src/me/topchetoeu/jscript/engine/frame/Runners.java +++ b/src/me/topchetoeu/jscript/engine/frame/Runners.java @@ -18,62 +18,62 @@ import me.topchetoeu.jscript.exceptions.EngineException; public class Runners { public static final Object NO_RETURN = new Object(); - public static Object execReturn(Instruction instr, CodeFrame frame, CallContext ctx) { + public static Object execReturn(CallContext ctx, Instruction instr, CodeFrame frame) { frame.codePtr++; return frame.pop(); } - public static Object execSignal(Instruction instr, CodeFrame frame, CallContext ctx) { + public static Object execSignal(CallContext ctx, Instruction instr, CodeFrame frame) { frame.codePtr++; return new SignalValue(instr.get(0)); } - public static Object execThrow(Instruction instr, CodeFrame frame, CallContext ctx) { + public static Object execThrow(CallContext ctx, Instruction instr, CodeFrame frame) { throw new EngineException(frame.pop()); } - public static Object execThrowSyntax(Instruction instr, CodeFrame frame, CallContext ctx) { + public static Object execThrowSyntax(CallContext ctx, Instruction instr, CodeFrame frame) { throw EngineException.ofSyntax((String)instr.get(0)); } - private static Object call(DebugCommand state, CallContext ctx, Object func, Object thisArg, Object... args) throws InterruptedException { + private static Object call(CallContext ctx, DebugCommand state, Object func, Object thisArg, Object... args) throws InterruptedException { ctx.setData(CodeFrame.STOP_AT_START_KEY, state == DebugCommand.STEP_INTO); return Values.call(ctx, func, thisArg, args); } - public static Object execCall(DebugCommand state, Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { + public static Object execCall(CallContext ctx, DebugCommand state, Instruction instr, CodeFrame frame) throws InterruptedException { var callArgs = frame.take(instr.get(0)); var func = frame.pop(); var thisArg = frame.pop(); - frame.push(call(state, ctx, func, thisArg, callArgs)); + frame.push(ctx, call(ctx, state, func, thisArg, callArgs)); frame.codePtr++; return NO_RETURN; } - public static Object execCallNew(DebugCommand state, Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { + public static Object execCallNew(CallContext ctx, DebugCommand state, Instruction instr, CodeFrame frame) throws InterruptedException { var callArgs = frame.take(instr.get(0)); var funcObj = frame.pop(); if (Values.isFunction(funcObj) && Values.function(funcObj).special) { - frame.push(call(state, ctx, funcObj, null, callArgs)); + frame.push(ctx, call(ctx, state, funcObj, null, callArgs)); } else { var proto = Values.getMember(ctx, funcObj, "prototype"); var obj = new ObjectValue(); obj.setPrototype(ctx, proto); - call(state, ctx, funcObj, obj, callArgs); - frame.push(obj); + call(ctx, state, funcObj, obj, callArgs); + frame.push(ctx, obj); } frame.codePtr++; return NO_RETURN; } - public static Object execMakeVar(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { + public static Object execMakeVar(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var name = (String)instr.get(0); frame.function.globals.define(name); frame.codePtr++; return NO_RETURN; } - public static Object execDefProp(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { + public static Object execDefProp(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var setter = frame.pop(); var getter = frame.pop(); var name = frame.pop(); @@ -82,28 +82,28 @@ public class Runners { if (getter != null && !Values.isFunction(getter)) throw EngineException.ofType("Getter must be a function or undefined."); if (setter != null && !Values.isFunction(setter)) throw EngineException.ofType("Setter must be a function or undefined."); if (!Values.isObject(obj)) throw EngineException.ofType("Property apply target must be an object."); - Values.object(obj).defineProperty(name, Values.function(getter), Values.function(setter), false, false); + Values.object(obj).defineProperty(ctx, name, Values.function(getter), Values.function(setter), false, false); - frame.push(obj); + frame.push(ctx, obj); frame.codePtr++; return NO_RETURN; } - public static Object execInstanceof(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { + public static Object execInstanceof(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var type = frame.pop(); var obj = frame.pop(); if (!Values.isPrimitive(type)) { var proto = Values.getMember(ctx, type, "prototype"); - frame.push(Values.isInstanceOf(ctx, obj, proto)); + frame.push(ctx, Values.isInstanceOf(ctx, obj, proto)); } else { - frame.push(false); + frame.push(ctx, false); } frame.codePtr++; return NO_RETURN; } - public static Object execKeys(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { + public static Object execKeys(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var val = frame.pop(); var arr = new ObjectValue(); @@ -113,81 +113,81 @@ public class Runners { Collections.reverse(members); for (var el : members) { if (el instanceof Symbol) continue; - arr.defineProperty(i++, el); + arr.defineProperty(ctx, i++, el); } - arr.defineProperty("length", i); + arr.defineProperty(ctx, "length", i); - frame.push(arr); + frame.push(ctx, arr); frame.codePtr++; return NO_RETURN; } - public static Object execTry(DebugCommand state, Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { + public static Object execTry(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { frame.addTry(instr.get(0), instr.get(1), instr.get(2)); frame.codePtr++; return NO_RETURN; } - public static Object execDup(Instruction instr, CodeFrame frame, CallContext ctx) { + public static Object execDup(CallContext ctx, Instruction instr, CodeFrame frame) { int offset = instr.get(0), count = instr.get(1); for (var i = 0; i < count; i++) { - frame.push(frame.peek(offset + count - 1)); + frame.push(ctx, frame.peek(offset + count - 1)); } frame.codePtr++; return NO_RETURN; } - public static Object execMove(Instruction instr, CodeFrame frame, CallContext ctx) { + public static Object execMove(CallContext ctx, Instruction instr, CodeFrame frame) { int offset = instr.get(0), count = instr.get(1); var tmp = frame.take(offset); var res = frame.take(count); - for (var i = 0; i < offset; i++) frame.push(tmp[i]); - for (var i = 0; i < count; i++) frame.push(res[i]); + for (var i = 0; i < offset; i++) frame.push(ctx, tmp[i]); + for (var i = 0; i < count; i++) frame.push(ctx, res[i]); frame.codePtr++; return NO_RETURN; } - public static Object execLoadUndefined(Instruction instr, CodeFrame frame, CallContext ctx) { - frame.push(null); + public static Object execLoadUndefined(CallContext ctx, Instruction instr, CodeFrame frame) { + frame.push(ctx, null); frame.codePtr++; return NO_RETURN; } - public static Object execLoadValue(Instruction instr, CodeFrame frame, CallContext ctx) { - frame.push(instr.get(0)); + public static Object execLoadValue(CallContext ctx, Instruction instr, CodeFrame frame) { + frame.push(ctx, instr.get(0)); frame.codePtr++; return NO_RETURN; } - public static Object execLoadVar(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { + public static Object execLoadVar(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var i = instr.get(0); - if (i instanceof String) frame.push(frame.function.globals.get(ctx, (String)i)); - else frame.push(frame.scope.get((int)i).get(ctx)); + if (i instanceof String) frame.push(ctx, frame.function.globals.get(ctx, (String)i)); + else frame.push(ctx, frame.scope.get((int)i).get(ctx)); frame.codePtr++; return NO_RETURN; } - public static Object execLoadObj(Instruction instr, CodeFrame frame, CallContext ctx) { - frame.push(new ObjectValue()); + public static Object execLoadObj(CallContext ctx, Instruction instr, CodeFrame frame) { + frame.push(ctx, new ObjectValue()); frame.codePtr++; return NO_RETURN; } - public static Object execLoadGlob(Instruction instr, CodeFrame frame, CallContext ctx) { - frame.push(frame.function.globals.obj); + public static Object execLoadGlob(CallContext ctx, Instruction instr, CodeFrame frame) { + frame.push(ctx, frame.function.globals.obj); frame.codePtr++; return NO_RETURN; } - public static Object execLoadArr(Instruction instr, CodeFrame frame, CallContext ctx) { + public static Object execLoadArr(CallContext ctx, Instruction instr, CodeFrame frame) { var res = new ArrayValue(); res.setSize(instr.get(0)); - frame.push(res); + frame.push(ctx, res); frame.codePtr++; return NO_RETURN; } - public static Object execLoadFunc(Instruction instr, CodeFrame frame, CallContext ctx) { + public static Object execLoadFunc(CallContext ctx, Instruction instr, CodeFrame frame) { int n = (Integer)instr.get(0); int localsN = (Integer)instr.get(1); int len = (Integer)instr.get(2); @@ -203,18 +203,18 @@ public class Runners { System.arraycopy(frame.function.body, start, body, 0, end - start); var func = new CodeFunction("", localsN, len, frame.function.globals, captures, body); - frame.push(func); + frame.push(ctx, func); frame.codePtr += n; return NO_RETURN; } - public static Object execLoadMember(DebugCommand state, Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { + public static Object execLoadMember(CallContext ctx, DebugCommand state, Instruction instr, CodeFrame frame) throws InterruptedException { var key = frame.pop(); var obj = frame.pop(); try { ctx.setData(CodeFrame.STOP_AT_START_KEY, state == DebugCommand.STEP_INTO); - frame.push(Values.getMember(ctx, obj, key)); + frame.push(ctx, Values.getMember(ctx, obj, key)); } catch (IllegalArgumentException e) { throw EngineException.ofType(e.getMessage()); @@ -222,33 +222,33 @@ public class Runners { frame.codePtr++; return NO_RETURN; } - public static Object execLoadKeyMember(DebugCommand state, Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { - frame.push(instr.get(0)); - return execLoadMember(state, instr, frame, ctx); + public static Object execLoadKeyMember(CallContext ctx, DebugCommand state, Instruction instr, CodeFrame frame) throws InterruptedException { + frame.push(ctx, instr.get(0)); + return execLoadMember(ctx, state, instr, frame); } - public static Object execLoadRegEx(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { - frame.push(ctx.engine().makeRegex(instr.get(0), instr.get(1))); + public static Object execLoadRegEx(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { + frame.push(ctx, ctx.engine().makeRegex(instr.get(0), instr.get(1))); frame.codePtr++; return NO_RETURN; } - public static Object execDiscard(Instruction instr, CodeFrame frame, CallContext ctx) { + public static Object execDiscard(CallContext ctx, Instruction instr, CodeFrame frame) { frame.pop(); frame.codePtr++; return NO_RETURN; } - public static Object execStoreMember(DebugCommand state, Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { + public static Object execStoreMember(CallContext ctx, DebugCommand state, Instruction instr, CodeFrame frame) throws InterruptedException { var val = frame.pop(); var key = frame.pop(); var obj = frame.pop(); ctx.setData(CodeFrame.STOP_AT_START_KEY, state == DebugCommand.STEP_INTO); if (!Values.setMember(ctx, obj, key, val)) throw EngineException.ofSyntax("Can't set member '" + key + "'."); - if ((boolean)instr.get(0)) frame.push(val); + if ((boolean)instr.get(0)) frame.push(ctx, val); frame.codePtr++; return NO_RETURN; } - public static Object execStoreVar(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { + public static Object execStoreVar(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var val = (boolean)instr.get(1) ? frame.peek() : frame.pop(); var i = instr.get(0); @@ -258,18 +258,18 @@ public class Runners { frame.codePtr++; return NO_RETURN; } - public static Object execStoreSelfFunc(Instruction instr, CodeFrame frame, CallContext ctx) { + public static Object execStoreSelfFunc(CallContext ctx, Instruction instr, CodeFrame frame) { frame.scope.locals[(int)instr.get(0)].set(ctx, frame.function); frame.codePtr++; return NO_RETURN; } - public static Object execJmp(Instruction instr, CodeFrame frame, CallContext ctx) { + public static Object execJmp(CallContext ctx, Instruction instr, CodeFrame frame) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; return NO_RETURN; } - public static Object execJmpIf(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { + public static Object execJmpIf(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { if (Values.toBoolean(frame.pop())) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; @@ -277,7 +277,7 @@ public class Runners { else frame.codePtr ++; return NO_RETURN; } - public static Object execJmpIfNot(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { + public static Object execJmpIfNot(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { if (Values.not(frame.pop())) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; @@ -286,15 +286,15 @@ public class Runners { return NO_RETURN; } - public static Object execIn(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { + public static Object execIn(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var obj = frame.pop(); var index = frame.pop(); - frame.push(Values.hasMember(ctx, obj, index, false)); + frame.push(ctx, Values.hasMember(ctx, obj, index, false)); frame.codePtr++; return NO_RETURN; } - public static Object execTypeof(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { + public static Object execTypeof(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { String name = instr.get(0); Object obj; @@ -306,12 +306,12 @@ public class Runners { } else obj = frame.pop(); - frame.push(Values.type(obj)); + frame.push(ctx, Values.type(obj)); frame.codePtr++; return NO_RETURN; } - public static Object execNop(Instruction instr, CodeFrame frame, CallContext ctx) { + public static Object execNop(CallContext ctx, Instruction instr, CodeFrame frame) { if (instr.is(0, "dbg_names")) { var names = new String[instr.params.length - 1]; for (var i = 0; i < instr.params.length - 1; i++) { @@ -325,67 +325,67 @@ public class Runners { return NO_RETURN; } - public static Object execDelete(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { + public static Object execDelete(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var key = frame.pop(); var val = frame.pop(); if (!Values.deleteMember(ctx, val, key)) throw EngineException.ofSyntax("Can't delete member '" + key + "'."); - frame.push(true); + frame.push(ctx, true); frame.codePtr++; return NO_RETURN; } - public static Object execOperation(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { + public static Object execOperation(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { Operation op = instr.get(0); var args = new Object[op.operands]; for (var i = op.operands - 1; i >= 0; i--) args[i] = frame.pop(); - frame.push(Values.operation(ctx, op, args)); + frame.push(ctx, Values.operation(ctx, op, args)); frame.codePtr++; return NO_RETURN; } - public static Object exec(DebugCommand state, Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { + public static Object exec(CallContext ctx, DebugCommand state, Instruction instr, CodeFrame frame) throws InterruptedException { // System.out.println(instr + "@" + instr.location); switch (instr.type) { - case NOP: return execNop(instr, frame, ctx); - case RETURN: return execReturn(instr, frame, ctx); - case SIGNAL: return execSignal(instr, frame, ctx); - case THROW: return execThrow(instr, frame, ctx); - case THROW_SYNTAX: return execThrowSyntax(instr, frame, ctx); - case CALL: return execCall(state, instr, frame, ctx); - case CALL_NEW: return execCallNew(state, instr, frame, ctx); - case TRY: return execTry(state, instr, frame, ctx); + case NOP: return execNop(ctx, instr, frame); + case RETURN: return execReturn(ctx, instr, frame); + case SIGNAL: return execSignal(ctx, instr, frame); + case THROW: return execThrow(ctx, instr, frame); + case THROW_SYNTAX: return execThrowSyntax(ctx, instr, frame); + case CALL: return execCall(ctx, state, instr, frame); + case CALL_NEW: return execCallNew(ctx, state, instr, frame); + case TRY: return execTry(ctx, instr, frame); - case DUP: return execDup(instr, frame, ctx); - case MOVE: return execMove(instr, frame, ctx); - case LOAD_VALUE: return execLoadValue(instr, frame, ctx); - case LOAD_VAR: return execLoadVar(instr, frame, ctx); - case LOAD_OBJ: return execLoadObj(instr, frame, ctx); - case LOAD_ARR: return execLoadArr(instr, frame, ctx); - case LOAD_FUNC: return execLoadFunc(instr, frame, ctx); - case LOAD_MEMBER: return execLoadMember(state, instr, frame, ctx); - case LOAD_VAL_MEMBER: return execLoadKeyMember(state, instr, frame, ctx); - case LOAD_REGEX: return execLoadRegEx(instr, frame, ctx); - case LOAD_GLOB: return execLoadGlob(instr, frame, ctx); + case DUP: return execDup(ctx, instr, frame); + case MOVE: return execMove(ctx, instr, frame); + case LOAD_VALUE: return execLoadValue(ctx, instr, frame); + case LOAD_VAR: return execLoadVar(ctx, instr, frame); + case LOAD_OBJ: return execLoadObj(ctx, instr, frame); + case LOAD_ARR: return execLoadArr(ctx, instr, frame); + case LOAD_FUNC: return execLoadFunc(ctx, instr, frame); + case LOAD_MEMBER: return execLoadMember(ctx, state, instr, frame); + case LOAD_VAL_MEMBER: return execLoadKeyMember(ctx, state, instr, frame); + case LOAD_REGEX: return execLoadRegEx(ctx, instr, frame); + case LOAD_GLOB: return execLoadGlob(ctx, instr, frame); - case DISCARD: return execDiscard(instr, frame, ctx); - case STORE_MEMBER: return execStoreMember(state, instr, frame, ctx); - case STORE_VAR: return execStoreVar(instr, frame, ctx); - case STORE_SELF_FUNC: return execStoreSelfFunc(instr, frame, ctx); - case MAKE_VAR: return execMakeVar(instr, frame, ctx); + case DISCARD: return execDiscard(ctx, instr, frame); + case STORE_MEMBER: return execStoreMember(ctx, state, instr, frame); + case STORE_VAR: return execStoreVar(ctx, instr, frame); + case STORE_SELF_FUNC: return execStoreSelfFunc(ctx, instr, frame); + case MAKE_VAR: return execMakeVar(ctx, instr, frame); - case KEYS: return execKeys(instr, frame, ctx); - case DEF_PROP: return execDefProp(instr, frame, ctx); - case TYPEOF: return execTypeof(instr, frame, ctx); - case DELETE: return execDelete(instr, frame, ctx); + case KEYS: return execKeys(ctx, instr, frame); + case DEF_PROP: return execDefProp(ctx, instr, frame); + case TYPEOF: return execTypeof(ctx, instr, frame); + case DELETE: return execDelete(ctx, instr, frame); - case JMP: return execJmp(instr, frame, ctx); - case JMP_IF: return execJmpIf(instr, frame, ctx); - case JMP_IFN: return execJmpIfNot(instr, frame, ctx); + case JMP: return execJmp(ctx, instr, frame); + case JMP_IF: return execJmpIf(ctx, instr, frame); + case JMP_IFN: return execJmpIfNot(ctx, instr, frame); - case OPERATION: return execOperation(instr, frame, ctx); + case OPERATION: return execOperation(ctx, instr, frame); default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + "."); } diff --git a/src/me/topchetoeu/jscript/engine/modules/Module.java b/src/me/topchetoeu/jscript/engine/modules/Module.java index 3959739..44bd681 100644 --- a/src/me/topchetoeu/jscript/engine/modules/Module.java +++ b/src/me/topchetoeu/jscript/engine/modules/Module.java @@ -39,7 +39,7 @@ public class Module { executing = true; var scope = ctx.engine().global().globalChild(); - scope.define("module", true, this); + scope.define(null, "module", true, this); scope.define("exports", new ExportsVariable()); var parent = new File(filename).getParentFile(); diff --git a/src/me/topchetoeu/jscript/engine/scope/GlobalScope.java b/src/me/topchetoeu/jscript/engine/scope/GlobalScope.java index 384ceb8..e318c24 100644 --- a/src/me/topchetoeu/jscript/engine/scope/GlobalScope.java +++ b/src/me/topchetoeu/jscript/engine/scope/GlobalScope.java @@ -38,24 +38,24 @@ public class GlobalScope implements ScopeRecord { Thread.currentThread().interrupt(); return name; } - obj.defineProperty(name, null); + obj.defineProperty(null, name, null); return name; } public void define(String name, Variable val) { - obj.defineProperty(name, + obj.defineProperty(null, name, new NativeFunction("get " + name, (ctx, th, a) -> val.get(ctx)), new NativeFunction("set " + name, (ctx, th, args) -> { val.set(ctx, args.length > 0 ? args[0] : null); return null; }), true, true ); } - public void define(String name, boolean readonly, Object val) { - obj.defineProperty(name, val, readonly, true, true); + public void define(CallContext ctx, String name, boolean readonly, Object val) { + obj.defineProperty(ctx, name, val, readonly, true, true); } public void define(String... names) { for (var n : names) define(n); } public void define(boolean readonly, FunctionValue val) { - define(val.name, readonly, val); + define(null, val.name, readonly, val); } public Object get(CallContext ctx, String name) throws InterruptedException { diff --git a/src/me/topchetoeu/jscript/engine/scope/ValueVariable.java b/src/me/topchetoeu/jscript/engine/scope/ValueVariable.java index ce34b9f..2d68066 100644 --- a/src/me/topchetoeu/jscript/engine/scope/ValueVariable.java +++ b/src/me/topchetoeu/jscript/engine/scope/ValueVariable.java @@ -18,7 +18,7 @@ public class ValueVariable implements Variable { @Override public void set(CallContext ctx, Object val) { if (readonly) return; - this.value = Values.normalize(val); + this.value = Values.normalize(ctx, val); } public ValueVariable(boolean readonly, Object val) { diff --git a/src/me/topchetoeu/jscript/engine/values/ArrayValue.java b/src/me/topchetoeu/jscript/engine/values/ArrayValue.java index b0e8e02..9278ed4 100644 --- a/src/me/topchetoeu/jscript/engine/values/ArrayValue.java +++ b/src/me/topchetoeu/jscript/engine/values/ArrayValue.java @@ -29,14 +29,14 @@ public class ArrayValue extends ObjectValue { if (res == EMPTY) return null; else return res; } - public void set(int i, Object val) { + public void set(CallContext ctx, int i, Object val) { if (i < 0) return; while (values.size() <= i) { values.add(EMPTY); } - values.set(i, Values.normalize(val)); + values.set(i, Values.normalize(ctx, val)); } public boolean has(int i) { return i >= 0 && i < values.size() && values.get(i) != EMPTY; @@ -102,7 +102,7 @@ public class ArrayValue extends ObjectValue { if (key instanceof Number) { var i = Values.number(key); if (i >= 0 && i - Math.floor(i) == 0) { - set((int)i, val); + set(ctx, (int)i, val); return true; } } @@ -149,12 +149,12 @@ public class ArrayValue extends ObjectValue { nonEnumerableSet.add("length"); nonConfigurableSet.add("length"); } - public ArrayValue(Object ...values) { + public ArrayValue(CallContext ctx, Object ...values) { this(); - for (var i = 0; i < values.length; i++) this.values.add(values[i]); + for (var i = 0; i < values.length; i++) this.values.add(Values.normalize(ctx, values[i])); } - public static ArrayValue of(Collection values) { - return new ArrayValue(values.toArray(Object[]::new)); + public static ArrayValue of(CallContext ctx, Collection values) { + return new ArrayValue(ctx, values.toArray(Object[]::new)); } } diff --git a/src/me/topchetoeu/jscript/engine/values/CodeFunction.java b/src/me/topchetoeu/jscript/engine/values/CodeFunction.java index e543728..48a430e 100644 --- a/src/me/topchetoeu/jscript/engine/values/CodeFunction.java +++ b/src/me/topchetoeu/jscript/engine/values/CodeFunction.java @@ -34,7 +34,7 @@ public class CodeFunction extends FunctionValue { @Override public Object call(CallContext ctx, Object thisArg, Object... args) throws InterruptedException { - return new CodeFrame(thisArg, args, this).run(ctx); + return new CodeFrame(ctx, thisArg, args, this).run(ctx); } public CodeFunction(String name, int localsN, int length, GlobalScope globals, ValueVariable[] captures, Instruction[] body) { diff --git a/src/me/topchetoeu/jscript/engine/values/FunctionValue.java b/src/me/topchetoeu/jscript/engine/values/FunctionValue.java index 8a20164..aaf1061 100644 --- a/src/me/topchetoeu/jscript/engine/values/FunctionValue.java +++ b/src/me/topchetoeu/jscript/engine/values/FunctionValue.java @@ -63,8 +63,8 @@ public abstract class FunctionValue extends ObjectValue { nonEnumerableSet.add("length"); var proto = new ObjectValue(); - proto.defineProperty("constructor", this, true, false, false); - this.defineProperty("prototype", proto, true, false, false); + proto.defineProperty(null, "constructor", this, true, false, false); + this.defineProperty(null, "prototype", proto, true, false, false); } } diff --git a/src/me/topchetoeu/jscript/engine/values/ObjectValue.java b/src/me/topchetoeu/jscript/engine/values/ObjectValue.java index de84126..4899448 100644 --- a/src/me/topchetoeu/jscript/engine/values/ObjectValue.java +++ b/src/me/topchetoeu/jscript/engine/values/ObjectValue.java @@ -78,8 +78,8 @@ public class ObjectValue { state = State.FROZEN; } - public final boolean defineProperty(Object key, Object val, boolean writable, boolean configurable, boolean enumerable) { - key = Values.normalize(key); val = Values.normalize(val); + public final boolean defineProperty(CallContext ctx, Object key, Object val, boolean writable, boolean configurable, boolean enumerable) { + key = Values.normalize(ctx, key); val = Values.normalize(ctx, val); boolean reconfigured = writable != memberWritable(key) || configurable != memberConfigurable(key) || @@ -118,11 +118,11 @@ public class ObjectValue { values.put(key, val); return true; } - public final boolean defineProperty(Object key, Object val) { - return defineProperty(Values.normalize(key), Values.normalize(val), true, true, true); + public final boolean defineProperty(CallContext ctx, Object key, Object val) { + return defineProperty(ctx, key, val, true, true, true); } - public final boolean defineProperty(Object key, FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) { - key = Values.normalize(key); + public final boolean defineProperty(CallContext ctx, Object key, FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) { + key = Values.normalize(ctx, key); if ( properties.containsKey(key) && properties.get(key).getter == getter && @@ -162,7 +162,7 @@ public class ObjectValue { return (ObjectValue)prototype; } public final boolean setPrototype(CallContext ctx, Object val) { - val = Values.normalize(val); + val = Values.normalize(ctx, val); if (!extensible()) return false; if (val == null || val == Values.NULL) prototype = null; @@ -228,7 +228,7 @@ public class ObjectValue { } public final Object getMember(CallContext ctx, Object key, Object thisArg) throws InterruptedException { - key = Values.normalize(key); + key = Values.normalize(ctx, key); if (key.equals("__proto__")) { var res = getPrototype(ctx); @@ -239,7 +239,7 @@ public class ObjectValue { if (prop != null) { if (prop.getter == null) return null; - else return prop.getter.call(ctx, Values.normalize(thisArg)); + else return prop.getter.call(ctx, Values.normalize(ctx, thisArg)); } else return getField(ctx, key); } @@ -248,12 +248,12 @@ public class ObjectValue { } public final boolean setMember(CallContext ctx, Object key, Object val, Object thisArg, boolean onlyProps) throws InterruptedException { - key = Values.normalize(key); val = Values.normalize(val); + key = Values.normalize(ctx, key); val = Values.normalize(ctx, val); var prop = getProperty(ctx, key); if (prop != null) { if (prop.setter == null) return false; - prop.setter.call(ctx, Values.normalize(thisArg), val); + prop.setter.call(ctx, Values.normalize(ctx, thisArg), val); return true; } else if (onlyProps) return false; @@ -267,11 +267,11 @@ public class ObjectValue { else return setField(ctx, key, val); } public final boolean setMember(CallContext ctx, Object key, Object val, boolean onlyProps) throws InterruptedException { - return setMember(ctx, Values.normalize(key), Values.normalize(val), this, onlyProps); + return setMember(ctx, Values.normalize(ctx, key), Values.normalize(ctx, val), this, onlyProps); } public final boolean hasMember(CallContext ctx, Object key, boolean own) throws InterruptedException { - key = Values.normalize(key); + key = Values.normalize(ctx, key); if (key != null && key.equals("__proto__")) return true; if (hasField(ctx, key)) return true; @@ -280,7 +280,7 @@ public class ObjectValue { return prototype != null && getPrototype(ctx).hasMember(ctx, key, own); } public final boolean deleteMember(CallContext ctx, Object key) throws InterruptedException { - key = Values.normalize(key); + key = Values.normalize(ctx, key); if (!memberConfigurable(key)) return false; properties.remove(key); @@ -291,21 +291,21 @@ public class ObjectValue { } public final ObjectValue getMemberDescriptor(CallContext ctx, Object key) throws InterruptedException { - key = Values.normalize(key); + key = Values.normalize(ctx, key); var prop = properties.get(key); var res = new ObjectValue(); - res.defineProperty("configurable", memberConfigurable(key)); - res.defineProperty("enumerable", memberEnumerable(key)); + res.defineProperty(ctx, "configurable", memberConfigurable(key)); + res.defineProperty(ctx, "enumerable", memberEnumerable(key)); if (prop != null) { - res.defineProperty("get", prop.getter); - res.defineProperty("set", prop.setter); + res.defineProperty(ctx, "get", prop.getter); + res.defineProperty(ctx, "set", prop.setter); } else if (hasField(ctx, key)) { - res.defineProperty("value", values.get(key)); - res.defineProperty("writable", memberWritable(key)); + res.defineProperty(ctx, "value", values.get(key)); + res.defineProperty(ctx, "writable", memberWritable(key)); } else return null; return res; @@ -326,10 +326,10 @@ public class ObjectValue { return res; } - public ObjectValue(Map values) { + public ObjectValue(CallContext ctx, Map values) { this(PlaceholderProto.OBJECT); for (var el : values.entrySet()) { - defineProperty(el.getKey(), el.getValue()); + defineProperty(ctx, el.getKey(), el.getValue()); } } public ObjectValue(PlaceholderProto proto) { diff --git a/src/me/topchetoeu/jscript/engine/values/Values.java b/src/me/topchetoeu/jscript/engine/values/Values.java index f340f5d..22d4654 100644 --- a/src/me/topchetoeu/jscript/engine/values/Values.java +++ b/src/me/topchetoeu/jscript/engine/values/Values.java @@ -88,7 +88,7 @@ public class Values { } public static Object toPrimitive(CallContext ctx, Object obj, ConvertHint hint) throws InterruptedException { - obj = normalize(obj); + obj = normalize(ctx, obj); if (isPrimitive(obj)) return obj; var first = hint == ConvertHint.VALUEOF ? "valueOf" : "toString"; @@ -231,8 +231,8 @@ public class Values { case OR: return or(ctx, args[0], args[1]); case XOR: return xor(ctx, args[0], args[1]); - case EQUALS: return strictEquals(args[0], args[1]); - case NOT_EQUALS: return !strictEquals(args[0], args[1]); + case EQUALS: return strictEquals(ctx, args[0], args[1]); + case NOT_EQUALS: return !strictEquals(ctx, args[0], args[1]); case LOOSE_EQUALS: return looseEqual(ctx, args[0], args[1]); case LOOSE_NOT_EQUALS: return !looseEqual(ctx, args[0], args[1]); @@ -261,7 +261,7 @@ public class Values { } public static Object getMember(CallContext ctx, Object obj, Object key) throws InterruptedException { - obj = normalize(obj); key = normalize(key); + obj = normalize(ctx, obj); key = normalize(ctx, key); if (obj == null) throw new IllegalArgumentException("Tried to access member of undefined."); if (obj == NULL) throw new IllegalArgumentException("Tried to access member of null."); if (isObject(obj)) return object(obj).getMember(ctx, key); @@ -281,7 +281,7 @@ public class Values { else return proto.getMember(ctx, key, obj); } public static boolean setMember(CallContext ctx, Object obj, Object key, Object val) throws InterruptedException { - obj = normalize(obj); key = normalize(key); val = normalize(val); + obj = normalize(ctx, obj); key = normalize(ctx, key); val = normalize(ctx, val); if (obj == null) throw EngineException.ofType("Tried to access member of undefined."); if (obj == NULL) throw EngineException.ofType("Tried to access member of null."); if (key.equals("__proto__")) return setPrototype(ctx, obj, val); @@ -292,7 +292,7 @@ public class Values { } public static boolean hasMember(CallContext ctx, Object obj, Object key, boolean own) throws InterruptedException { if (obj == null || obj == NULL) return false; - obj = normalize(obj); key = normalize(key); + obj = normalize(ctx, obj); key = normalize(ctx, key); if (key.equals("__proto__")) return true; if (isObject(obj)) return object(obj).hasMember(ctx, key, own); @@ -310,14 +310,14 @@ public class Values { } public static boolean deleteMember(CallContext ctx, Object obj, Object key) throws InterruptedException { if (obj == null || obj == NULL) return false; - obj = normalize(obj); key = normalize(key); + obj = normalize(ctx, obj); key = normalize(ctx, key); if (isObject(obj)) return object(obj).deleteMember(ctx, key); else return false; } public static ObjectValue getPrototype(CallContext ctx, Object obj) throws InterruptedException { if (obj == null || obj == NULL) return null; - obj = normalize(obj); + obj = normalize(ctx, obj); if (isObject(obj)) return object(obj).getPrototype(ctx); if (ctx == null) return null; @@ -329,7 +329,7 @@ public class Values { return null; } public static boolean setPrototype(CallContext ctx, Object obj, Object proto) throws InterruptedException { - obj = normalize(obj); proto = normalize(proto); + obj = normalize(ctx, obj); proto = normalize(ctx, proto); return isObject(obj) && object(obj).setPrototype(ctx, proto); } public static List getMembers(CallContext ctx, Object obj, boolean own, boolean includeNonEnumerable) throws InterruptedException { @@ -359,8 +359,8 @@ public class Values { return function(func).call(ctx, thisArg, args); } - public static boolean strictEquals(Object a, Object b) { - a = normalize(a); b = normalize(b); + public static boolean strictEquals(CallContext ctx, Object a, Object b) { + a = normalize(ctx, a); b = normalize(ctx, b); if (a == null || b == null) return a == null && b == null; if (isNan(a) || isNan(b)) return false; @@ -370,7 +370,7 @@ public class Values { return a == b || a.equals(b); } public static boolean looseEqual(CallContext ctx, Object a, Object b) throws InterruptedException { - a = normalize(a); b = normalize(b); + a = normalize(ctx, a); b = normalize(ctx, b); // In loose equality, null is equivalent to undefined if (a == NULL) a = null; @@ -387,13 +387,13 @@ public class Values { // Compare symbols by reference if (a instanceof Symbol || b instanceof Symbol) return a == b; if (a instanceof Boolean || b instanceof Boolean) return toBoolean(a) == toBoolean(b); - if (a instanceof Number || b instanceof Number) return strictEquals(toNumber(ctx, a), toNumber(ctx, b)); + if (a instanceof Number || b instanceof Number) return strictEquals(ctx, toNumber(ctx, a), toNumber(ctx, b)); // Default to strings return toString(ctx, a).equals(toString(ctx, b)); } - public static Object normalize(Object val) { + public static Object normalize(CallContext ctx, Object val) { if (val instanceof Number) return number(val); if (isPrimitive(val) || val instanceof ObjectValue) return val; if (val instanceof Character) return val + ""; @@ -402,7 +402,7 @@ public class Values { var res = new ObjectValue(); for (var entry : ((Map)val).entrySet()) { - res.defineProperty(entry.getKey(), entry.getValue()); + res.defineProperty(ctx, entry.getKey(), entry.getValue()); } return res; @@ -412,12 +412,17 @@ public class Values { var res = new ArrayValue(); for (var entry : ((Iterable)val)) { - res.set(res.size(), entry); + res.set(ctx, res.size(), entry); } return res; } + if (val instanceof Class) { + if (ctx == null) return null; + else return ctx.engine.getConstructor((Class)val); + } + return new NativeWrapper(val); } @@ -562,14 +567,14 @@ public class Values { var it = iterable.iterator(); try { - var key = getMember(ctx, getMember(ctx, ctx.engine().symbolProto(), "constructor"), "iterable"); - res.defineProperty(key, new NativeFunction("", (_ctx, thisArg, args) -> fromJavaIterable(ctx, iterable))); + var key = getMember(ctx, getMember(ctx, ctx.engine().symbolProto(), "constructor"), "iterator"); + res.defineProperty(ctx, key, new NativeFunction("", (_ctx, thisArg, args) -> thisArg)); } catch (IllegalArgumentException | NullPointerException e) { } - res.defineProperty("next", new NativeFunction("", (_ctx, _th, _args) -> { - if (!it.hasNext()) return new ObjectValue(Map.of("done", true)); - else return new ObjectValue(Map.of("value", it.next())); + res.defineProperty(ctx, "next", new NativeFunction("", (_ctx, _th, _args) -> { + if (!it.hasNext()) return new ObjectValue(ctx, Map.of("done", true)); + else return new ObjectValue(ctx, Map.of("value", it.next())); })); return res; diff --git a/src/me/topchetoeu/jscript/exceptions/EngineException.java b/src/me/topchetoeu/jscript/exceptions/EngineException.java index fcc7cfc..ccdece6 100644 --- a/src/me/topchetoeu/jscript/exceptions/EngineException.java +++ b/src/me/topchetoeu/jscript/exceptions/EngineException.java @@ -41,7 +41,7 @@ public class EngineException extends RuntimeException { private static Object err(String msg, PlaceholderProto proto) { var res = new ObjectValue(proto); - res.defineProperty("message", msg); + res.defineProperty(null, "message", msg); return res; } diff --git a/src/me/topchetoeu/jscript/interop/NativeTypeRegister.java b/src/me/topchetoeu/jscript/interop/NativeTypeRegister.java index c1ed7ea..aceaa79 100644 --- a/src/me/topchetoeu/jscript/interop/NativeTypeRegister.java +++ b/src/me/topchetoeu/jscript/interop/NativeTypeRegister.java @@ -25,7 +25,7 @@ public class NativeTypeRegister { var val = target.values.get(name); if (name.equals("")) name = method.getName(); - if (!(val instanceof OverloadFunction)) target.defineProperty(name, val = new OverloadFunction(name)); + if (!(val instanceof OverloadFunction)) target.defineProperty(null, name, val = new OverloadFunction(name)); ((OverloadFunction)val).overloads.add(Overload.fromMethod(method)); } @@ -40,7 +40,7 @@ public class NativeTypeRegister { else getter = new OverloadFunction("get " + name); getter.overloads.add(Overload.fromMethod(method)); - target.defineProperty(name, getter, setter, true, true); + target.defineProperty(null, name, getter, setter, true, true); } if (set != null) { var name = set.value(); @@ -52,7 +52,7 @@ public class NativeTypeRegister { else setter = new OverloadFunction("set " + name); setter.overloads.add(Overload.fromMethod(method)); - target.defineProperty(name, getter, setter, true, true); + target.defineProperty(null, name, getter, setter, true, true); } } } @@ -67,7 +67,7 @@ public class NativeTypeRegister { if (name.equals("")) name = field.getName(); var getter = new OverloadFunction("get " + name).add(Overload.getterFromField(field)); var setter = new OverloadFunction("set " + name).add(Overload.setterFromField(field)); - target.defineProperty(name, getter, setter, true, false); + target.defineProperty(null, name, getter, setter, true, false); } } } @@ -84,7 +84,7 @@ public class NativeTypeRegister { return ctx.engine().typeRegister().getConstr(cl); })); - target.defineProperty(name, getter, null, true, false); + target.defineProperty(null, name, getter, null, true, false); } } } diff --git a/src/me/topchetoeu/jscript/interop/OverloadFunction.java b/src/me/topchetoeu/jscript/interop/OverloadFunction.java index 38a80ac..e8c78d2 100644 --- a/src/me/topchetoeu/jscript/interop/OverloadFunction.java +++ b/src/me/topchetoeu/jscript/interop/OverloadFunction.java @@ -47,7 +47,7 @@ public class OverloadFunction extends FunctionValue { Object _this = overload.thisArg == null ? null : Values.convert(ctx, thisArg, overload.thisArg); try { - return Values.normalize(overload.runner.run(ctx, _this, newArgs)); + return Values.normalize(ctx, overload.runner.run(ctx, _this, newArgs)); } catch (InstantiationException e) { throw EngineException.ofError("The class may not be instantiated."); diff --git a/src/me/topchetoeu/jscript/polyfills/GeneratorFunction.java b/src/me/topchetoeu/jscript/polyfills/GeneratorFunction.java index b59940b..f27dba7 100644 --- a/src/me/topchetoeu/jscript/polyfills/GeneratorFunction.java +++ b/src/me/topchetoeu/jscript/polyfills/GeneratorFunction.java @@ -22,13 +22,13 @@ public class GeneratorFunction extends FunctionValue { if (done) { if (inducedError != Runners.NO_RETURN) throw new EngineException(inducedError); var res = new ObjectValue(); - res.defineProperty("done", true); - res.defineProperty("value", inducedReturn == Runners.NO_RETURN ? null : inducedReturn); + res.defineProperty(ctx, "done", true); + res.defineProperty(ctx, "value", inducedReturn == Runners.NO_RETURN ? null : inducedReturn); return res; } Object res = null; - if (inducedValue != Runners.NO_RETURN) frame.push(inducedValue); + if (inducedValue != Runners.NO_RETURN) frame.push(ctx, inducedValue); frame.start(ctx); yielding = false; while (!yielding) { @@ -51,8 +51,8 @@ public class GeneratorFunction extends FunctionValue { else res = frame.pop(); var obj = new ObjectValue(); - obj.defineProperty("done", done); - obj.defineProperty("value", res); + obj.defineProperty(ctx, "done", done); + obj.defineProperty(ctx, "value", res); return obj; } @@ -84,11 +84,11 @@ public class GeneratorFunction extends FunctionValue { } @Override - public Object call(CallContext _ctx, Object thisArg, Object... args) throws InterruptedException { + public Object call(CallContext ctx, Object thisArg, Object... args) throws InterruptedException { var handler = new Generator(); - var func = factory.call(_ctx, thisArg, new NativeFunction("yield", handler::yield)); + var func = factory.call(ctx, thisArg, new NativeFunction("yield", handler::yield)); if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function."); - handler.frame = new CodeFrame(thisArg, args, (CodeFunction)func); + handler.frame = new CodeFrame(ctx, thisArg, args, (CodeFunction)func); return handler; } diff --git a/src/me/topchetoeu/jscript/polyfills/Internals.java b/src/me/topchetoeu/jscript/polyfills/Internals.java index 02ed113..da6628a 100644 --- a/src/me/topchetoeu/jscript/polyfills/Internals.java +++ b/src/me/topchetoeu/jscript/polyfills/Internals.java @@ -38,12 +38,12 @@ public class Internals { return func.call(ctx, th, args.toArray()); } @Native - public boolean defineProp(ObjectValue obj, Object key, FunctionValue getter, FunctionValue setter, boolean enumerable, boolean configurable) { - return obj.defineProperty(key, getter, setter, configurable, enumerable); + public boolean defineProp(CallContext ctx, ObjectValue obj, Object key, FunctionValue getter, FunctionValue setter, boolean enumerable, boolean configurable) { + return obj.defineProperty(ctx, key, getter, setter, configurable, enumerable); } @Native - public boolean defineField(ObjectValue obj, Object key, Object val, boolean writable, boolean enumerable, boolean configurable) { - return obj.defineProperty(key, val, writable, configurable, enumerable); + public boolean defineField(CallContext ctx, ObjectValue obj, Object key, Object val, boolean writable, boolean enumerable, boolean configurable) { + return obj.defineProperty(ctx, key, val, writable, configurable, enumerable); } @Native @@ -209,7 +209,7 @@ public class Internals { for (var el : list) { if (el instanceof Symbol && onlyString) continue; - res.set(i++, el); + res.set(ctx, i++, el); } return res; @@ -223,7 +223,7 @@ public class Internals { var list = Values.object(obj).keys(true); for (var el : list) { - if (el instanceof Symbol == symbols) res.set(i++, el); + if (el instanceof Symbol == symbols) res.set(ctx, i++, el); } } diff --git a/src/me/topchetoeu/jscript/polyfills/JSON.java b/src/me/topchetoeu/jscript/polyfills/JSON.java index b9aa27f..755c633 100644 --- a/src/me/topchetoeu/jscript/polyfills/JSON.java +++ b/src/me/topchetoeu/jscript/polyfills/JSON.java @@ -19,11 +19,11 @@ public class JSON { if (val.isBoolean()) return val.bool(); if (val.isString()) return val.string(); if (val.isNumber()) return val.number(); - if (val.isList()) return ArrayValue.of(val.list().stream().map(JSON::toJS).collect(Collectors.toList())); + if (val.isList()) return ArrayValue.of(null, val.list().stream().map(JSON::toJS).collect(Collectors.toList())); if (val.isMap()) { var res = new ObjectValue(); for (var el : val.map().entrySet()) { - res.defineProperty(el.getKey(), toJS(el.getValue())); + res.defineProperty(null, el.getKey(), toJS(el.getValue())); } return res; } diff --git a/src/me/topchetoeu/jscript/polyfills/Map.java b/src/me/topchetoeu/jscript/polyfills/Map.java index e7b22d5..cbb8a2e 100644 --- a/src/me/topchetoeu/jscript/polyfills/Map.java +++ b/src/me/topchetoeu/jscript/polyfills/Map.java @@ -53,7 +53,7 @@ public class Map { return Values.fromJavaIterable(ctx, objs .entrySet() .stream() - .map(v -> new ArrayValue(v.getKey(), v.getValue())) + .map(v -> new ArrayValue(ctx, v.getKey(), v.getValue())) .collect(Collectors.toList()) ); } diff --git a/src/me/topchetoeu/jscript/polyfills/PolyfillEngine.java b/src/me/topchetoeu/jscript/polyfills/PolyfillEngine.java index 07fa886..f3a7706 100644 --- a/src/me/topchetoeu/jscript/polyfills/PolyfillEngine.java +++ b/src/me/topchetoeu/jscript/polyfills/PolyfillEngine.java @@ -8,10 +8,6 @@ import java.io.InputStreamReader; import me.topchetoeu.jscript.engine.Engine; import me.topchetoeu.jscript.engine.modules.ModuleManager; -import me.topchetoeu.jscript.engine.values.FunctionValue; -import me.topchetoeu.jscript.engine.values.NativeFunction; -import me.topchetoeu.jscript.engine.values.Values; -import me.topchetoeu.jscript.parsing.Parsing; public class PolyfillEngine extends Engine { public static String streamToString(InputStream in) { @@ -52,59 +48,58 @@ public class PolyfillEngine extends Engine { this.modules = new ModuleManager(root); - exposeNamespace("Math", Math.class); - exposeNamespace("JSON", JSON.class); - exposeClass("Promise", Promise.class); - exposeClass("RegExp", RegExp.class); - exposeClass("Date", Date.class); - exposeClass("Map", Map.class); - exposeClass("Set", Set.class); + // exposeNamespace("Math", Math.class); + // exposeNamespace("JSON", JSON.class); + // exposeClass("Promise", Promise.class); + // exposeClass("RegExp", RegExp.class); + // exposeClass("Date", Date.class); + // exposeClass("Map", Map.class); + // exposeClass("Set", Set.class); - global().define("Object", "Function", "String", "Number", "Boolean", "Symbol"); - global().define("Array", "require"); - global().define("Error", "SyntaxError", "TypeError", "RangeError"); - global().define("setTimeout", "setInterval", "clearTimeout", "clearInterval"); - // global().define("process", true, "trololo"); - global().define("debugger"); + // global().define("Object", "Function", "String", "Number", "Boolean", "Symbol"); + // global().define("Array", "require"); + // global().define("Error", "SyntaxError", "TypeError", "RangeError"); + // global().define("setTimeout", "setInterval", "clearTimeout", "clearInterval"); + // global().define("debugger"); - global().define(true, new NativeFunction("measure", (ctx, thisArg, values) -> { - var start = System.nanoTime(); - try { - return Values.call(ctx, values[0], ctx); - } - finally { - System.out.println(String.format("Function took %s ms", (System.nanoTime() - start) / 1000000.)); - } - })); - global().define(true, new NativeFunction("isNaN", (ctx, thisArg, args) -> { - if (args.length == 0) return true; - else return Double.isNaN(Values.toNumber(ctx, args[0])); - })); - global().define(true, new NativeFunction("log", (el, t, args) -> { - for (var obj : args) Values.printValue(el, obj); - System.out.println(); - return null; - })); + // global().define(true, new NativeFunction("measure", (ctx, thisArg, values) -> { + // var start = System.nanoTime(); + // try { + // return Values.call(ctx, values[0], ctx); + // } + // finally { + // System.out.println(String.format("Function took %s ms", (System.nanoTime() - start) / 1000000.)); + // } + // })); + // global().define(true, new NativeFunction("isNaN", (ctx, thisArg, args) -> { + // if (args.length == 0) return true; + // else return Double.isNaN(Values.toNumber(ctx, args[0])); + // })); + // global().define(true, new NativeFunction("log", (el, t, args) -> { + // for (var obj : args) Values.printValue(el, obj); + // System.out.println(); + // return null; + // })); - var scope = global().globalChild(); - scope.define("gt", true, global().obj); - scope.define("lgt", true, scope.obj); - scope.define("setProps", "setConstr"); - scope.define("internals", true, new Internals()); - scope.define(true, new NativeFunction("run", (ctx, thisArg, args) -> { - var filename = (String)args[0]; - boolean pollute = args.length > 1 && args[1].equals(true); - FunctionValue func; - var src = resourceToString("js/" + filename); - if (src == null) throw new RuntimeException("The source '" + filename + "' doesn't exist."); + // var scope = global().globalChild(); + // scope.define("gt", true, global().obj); + // scope.define("lgt", true, scope.obj); + // scope.define("setProps", "setConstr"); + // scope.define("internals", true, new Internals()); + // scope.define(true, new NativeFunction("run", (ctx, thisArg, args) -> { + // var filename = (String)args[0]; + // boolean pollute = args.length > 1 && args[1].equals(true); + // FunctionValue func; + // var src = resourceToString("js/" + filename); + // if (src == null) throw new RuntimeException("The source '" + filename + "' doesn't exist."); - if (pollute) func = Parsing.compile(global(), filename, src); - else func = Parsing.compile(scope.globalChild(), filename, src); + // if (pollute) func = Parsing.compile(global(), filename, src); + // else func = Parsing.compile(scope.globalChild(), filename, src); - func.call(ctx); - return null; - })); + // func.call(ctx); + // return null; + // })); - pushMsg(false, scope.globalChild(), java.util.Map.of(), "core.js", resourceToString("js/core.js"), null); + // pushMsg(false, scope.globalChild(), java.util.Map.of(), "core.js", resourceToString("js/core.js"), null); } } diff --git a/src/me/topchetoeu/jscript/polyfills/Promise.java b/src/me/topchetoeu/jscript/polyfills/Promise.java index 91f36ed..6a4481e 100644 --- a/src/me/topchetoeu/jscript/polyfills/Promise.java +++ b/src/me/topchetoeu/jscript/polyfills/Promise.java @@ -27,10 +27,10 @@ public class Promise { } @Native("resolve") - public static Promise ofResolved(CallContext engine, Object val) { + public static Promise ofResolved(CallContext ctx, Object val) { if (Values.isWrapper(val, Promise.class)) return Values.wrapper(val, Promise.class); var res = new Promise(); - res.fulfill(engine, val); + res.fulfill(ctx, val); return res; } public static Promise ofResolved(Object val) { @@ -41,9 +41,9 @@ public class Promise { } @Native("reject") - public static Promise ofRejected(CallContext engine, Object val) { + public static Promise ofRejected(CallContext ctx, Object val) { var res = new Promise(); - res.reject(engine, val); + res.reject(ctx, val); return res; } public static Promise ofRejected(Object val) { @@ -53,7 +53,7 @@ public class Promise { } @Native - public static Promise any(CallContext engine, Object _promises) { + public static Promise any(CallContext ctx, Object _promises) { if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); var promises = Values.array(_promises); if (promises.size() == 0) return ofResolved(new ArrayValue()); @@ -66,17 +66,17 @@ public class Promise { var index = i; var val = promises.get(i); if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then( - engine, + ctx, new NativeFunction(null, (e, th, args) -> { res.fulfill(e, args[0]); return null; }), new NativeFunction(null, (e, th, args) -> { - errors.set(index, args[0]); + errors.set(ctx, index, args[0]); n[0]--; if (n[0] <= 0) res.reject(e, errors); return null; }) ); else { - res.fulfill(engine, val); + res.fulfill(ctx, val); break; } } @@ -84,7 +84,7 @@ public class Promise { return res; } @Native - public static Promise race(CallContext engine, Object _promises) { + public static Promise race(CallContext ctx, Object _promises) { if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); var promises = Values.array(_promises); if (promises.size() == 0) return ofResolved(new ArrayValue()); @@ -93,7 +93,7 @@ public class Promise { for (var i = 0; i < promises.size(); i++) { var val = promises.get(i); if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then( - engine, + ctx, new NativeFunction(null, (e, th, args) -> { res.fulfill(e, args[0]); return null; }), new NativeFunction(null, (e, th, args) -> { res.reject(e, args[0]); return null; }) ); @@ -106,7 +106,7 @@ public class Promise { return res; } @Native - public static Promise all(CallContext engine, Object _promises) { + public static Promise all(CallContext ctx, Object _promises) { if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); var promises = Values.array(_promises); if (promises.size() == 0) return ofResolved(new ArrayValue()); @@ -119,9 +119,9 @@ public class Promise { var index = i; var val = promises.get(i); if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then( - engine, + ctx, new NativeFunction(null, (e, th, args) -> { - result.set(index, args[0]); + result.set(ctx, index, args[0]); n[0]--; if (n[0] <= 0) res.fulfill(e, result); return null; @@ -129,17 +129,17 @@ public class Promise { new NativeFunction(null, (e, th, args) -> { res.reject(e, args[0]); return null; }) ); else { - result.set(i, val); + result.set(ctx, i, val); break; } } - if (n[0] <= 0) res.fulfill(engine, result); + if (n[0] <= 0) res.fulfill(ctx, result); return res; } @Native - public static Promise allSettled(CallContext engine, Object _promises) { + public static Promise allSettled(CallContext ctx, Object _promises) { if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); var promises = Values.array(_promises); if (promises.size() == 0) return ofResolved(new ArrayValue()); @@ -152,9 +152,9 @@ public class Promise { var index = i; var val = promises.get(i); if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then( - engine, + ctx, new NativeFunction(null, (e, th, args) -> { - result.set(index, new ObjectValue(Map.of( + result.set(ctx, index, new ObjectValue(ctx, Map.of( "status", "fulfilled", "value", args[0] ))); @@ -163,7 +163,7 @@ public class Promise { return null; }), new NativeFunction(null, (e, th, args) -> { - result.set(index, new ObjectValue(Map.of( + result.set(ctx, index, new ObjectValue(ctx, Map.of( "status", "rejected", "reason", args[0] ))); @@ -173,7 +173,7 @@ public class Promise { }) ); else { - result.set(i, new ObjectValue(Map.of( + result.set(ctx, i, new ObjectValue(ctx, Map.of( "status", "fulfilled", "value", val ))); @@ -181,7 +181,7 @@ public class Promise { } } - if (n[0] <= 0) res.fulfill(engine, result); + if (n[0] <= 0) res.fulfill(ctx, result); return res; } diff --git a/src/me/topchetoeu/jscript/polyfills/RegExp.java b/src/me/topchetoeu/jscript/polyfills/RegExp.java index 752f24a..5f7dca6 100644 --- a/src/me/topchetoeu/jscript/polyfills/RegExp.java +++ b/src/me/topchetoeu/jscript/polyfills/RegExp.java @@ -119,7 +119,7 @@ public class RegExp { for (var el : namedGroups) { try { - groups.defineProperty(el, matcher.group(el)); + groups.defineProperty(null, el, matcher.group(el)); } catch (IllegalArgumentException e) { } } @@ -127,23 +127,23 @@ public class RegExp { for (int i = 0; i < matcher.groupCount() + 1; i++) { - obj.set(i, matcher.group(i)); + obj.set(null, i, matcher.group(i)); } - obj.defineProperty("groups", groups); - obj.defineProperty("index", matcher.start()); - obj.defineProperty("input", str); + obj.defineProperty(null, "groups", groups); + obj.defineProperty(null, "index", matcher.start()); + obj.defineProperty(null, "input", str); if (hasIndices) { var indices = new ArrayValue(); for (int i = 0; i < matcher.groupCount() + 1; i++) { - indices.set(i, new ArrayValue(matcher.start(i), matcher.end(i))); + indices.set(null, i, new ArrayValue(null, matcher.start(i), matcher.end(i))); } var groupIndices = new ObjectValue(); for (var el : namedGroups) { - groupIndices.defineProperty(el, new ArrayValue(matcher.start(el), matcher.end(el))); + groupIndices.defineProperty(null, el, new ArrayValue(null, matcher.start(el), matcher.end(el))); } - indices.defineProperty("groups", groupIndices); - obj.defineProperty("indices", indices); + indices.defineProperty(null, "groups", groupIndices); + obj.defineProperty(null, "indices", indices); } return obj; diff --git a/src/me/topchetoeu/jscript/polyfills/Set.java b/src/me/topchetoeu/jscript/polyfills/Set.java index f432e5e..c87b30b 100644 --- a/src/me/topchetoeu/jscript/polyfills/Set.java +++ b/src/me/topchetoeu/jscript/polyfills/Set.java @@ -6,7 +6,6 @@ import java.util.stream.Collectors; import me.topchetoeu.jscript.engine.CallContext; import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.FunctionValue; -import me.topchetoeu.jscript.engine.values.NativeFunction; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.exceptions.EngineException; @@ -44,55 +43,20 @@ public class Set { } @Native - public ObjectValue entries() { - var it = objs.stream().collect(Collectors.toList()).iterator(); - - var next = new NativeFunction("next", (ctx, thisArg, args) -> { - if (it.hasNext()) { - var val = it.next(); - return new ObjectValue(java.util.Map.of( - "value", new ArrayValue(val, val), - "done", false - )); - } - else return new ObjectValue(java.util.Map.of("done", true)); - }); - - return new ObjectValue(java.util.Map.of("next", next)); + public ObjectValue entries(CallContext ctx) throws InterruptedException { + return Values.fromJavaIterable(ctx, objs + .stream() + .map(v -> new ArrayValue(ctx, v, v)) + .collect(Collectors.toList()) + ); } @Native - public ObjectValue keys() { - var it = objs.stream().collect(Collectors.toList()).iterator(); - - var next = new NativeFunction("next", (ctx, thisArg, args) -> { - if (it.hasNext()) { - var val = it.next(); - return new ObjectValue(java.util.Map.of( - "value", val, - "done", false - )); - } - else return new ObjectValue(java.util.Map.of("done", true)); - }); - - return new ObjectValue(java.util.Map.of("next", next)); + public ObjectValue keys(CallContext ctx) throws InterruptedException { + return Values.fromJavaIterable(ctx, objs); } @Native - public ObjectValue values() { - var it = objs.stream().collect(Collectors.toList()).iterator(); - - var next = new NativeFunction("next", (ctx, thisArg, args) -> { - if (it.hasNext()) { - var val = it.next(); - return new ObjectValue(java.util.Map.of( - "value", val, - "done", false - )); - } - else return new ObjectValue(java.util.Map.of("done", true)); - }); - - return new ObjectValue(java.util.Map.of("next", next)); + public ObjectValue values(CallContext ctx) throws InterruptedException { + return Values.fromJavaIterable(ctx, objs); } @NativeGetter("size") diff --git a/src/me/topchetoeu/jscript/polyfills/TypescriptEngine.java b/src/me/topchetoeu/jscript/polyfills/TypescriptEngine.java- similarity index 97% rename from src/me/topchetoeu/jscript/polyfills/TypescriptEngine.java rename to src/me/topchetoeu/jscript/polyfills/TypescriptEngine.java- index 54b677e..3d379ce 100644 --- a/src/me/topchetoeu/jscript/polyfills/TypescriptEngine.java +++ b/src/me/topchetoeu/jscript/polyfills/TypescriptEngine.java- @@ -1,61 +1,61 @@ -package me.topchetoeu.jscript.polyfills; - -import java.io.File; -import java.util.ArrayList; -import java.util.Map; - -import me.topchetoeu.jscript.engine.scope.GlobalScope; -import me.topchetoeu.jscript.engine.values.ArrayValue; -import me.topchetoeu.jscript.engine.values.FunctionValue; -import me.topchetoeu.jscript.engine.values.NativeFunction; -import me.topchetoeu.jscript.engine.values.Values; - -public class TypescriptEngine extends PolyfillEngine { - private FunctionValue ts; - - @Override - public FunctionValue compile(GlobalScope scope, String filename, String raw) throws InterruptedException { - if (ts != null) { - var res = Values.array(ts.call(context(), null, filename, raw)); - var src = Values.toString(context(), res.get(0)); - var func = Values.function(res.get(1)); - - var compiled = super.compile(scope, filename, src); - - return new NativeFunction(null, (ctx, thisArg, args) -> { - return func.call(context(), null, compiled, thisArg, new ArrayValue(args)); - }); - } - return super.compile(scope, filename, raw); - } - - public TypescriptEngine(File root) { - super(root); - var scope = global().globalChild(); - - var decls = new ArrayList(); - decls.add(resourceToString("dts/core.d.ts")); - decls.add(resourceToString("dts/iterators.d.ts")); - decls.add(resourceToString("dts/map.d.ts")); - decls.add(resourceToString("dts/promise.d.ts")); - decls.add(resourceToString("dts/regex.d.ts")); - decls.add(resourceToString("dts/require.d.ts")); - decls.add(resourceToString("dts/set.d.ts")); - decls.add(resourceToString("dts/values/array.d.ts")); - decls.add(resourceToString("dts/values/boolean.d.ts")); - decls.add(resourceToString("dts/values/number.d.ts")); - decls.add(resourceToString("dts/values/errors.d.ts")); - decls.add(resourceToString("dts/values/function.d.ts")); - decls.add(resourceToString("dts/values/object.d.ts")); - decls.add(resourceToString("dts/values/string.d.ts")); - decls.add(resourceToString("dts/values/symbol.d.ts")); - - scope.define("libs", true, ArrayValue.of(decls)); - scope.define(true, new NativeFunction("init", (el, t, args) -> { - ts = Values.function(args[0]); - return null; - })); - - pushMsg(false, scope, Map.of(), "bootstrap.js", resourceToString("js/bootstrap.js"), null); - } -} +package me.topchetoeu.jscript.polyfills; + +import java.io.File; +import java.util.ArrayList; +import java.util.Map; + +import me.topchetoeu.jscript.engine.scope.GlobalScope; +import me.topchetoeu.jscript.engine.values.ArrayValue; +import me.topchetoeu.jscript.engine.values.FunctionValue; +import me.topchetoeu.jscript.engine.values.NativeFunction; +import me.topchetoeu.jscript.engine.values.Values; + +public class TypescriptEngine extends PolyfillEngine { + private FunctionValue ts; + + @Override + public FunctionValue compile(GlobalScope scope, String filename, String raw) throws InterruptedException { + if (ts != null) { + var res = Values.array(ts.call(context(), null, filename, raw)); + var src = Values.toString(context(), res.get(0)); + var func = Values.function(res.get(1)); + + var compiled = super.compile(scope, filename, src); + + return new NativeFunction(null, (ctx, thisArg, args) -> { + return func.call(context(), null, compiled, thisArg, new ArrayValue(args)); + }); + } + return super.compile(scope, filename, raw); + } + + public TypescriptEngine(File root) { + super(root); + var scope = global().globalChild(); + + var decls = new ArrayList(); + decls.add(resourceToString("dts/core.d.ts")); + decls.add(resourceToString("dts/iterators.d.ts")); + decls.add(resourceToString("dts/map.d.ts")); + decls.add(resourceToString("dts/promise.d.ts")); + decls.add(resourceToString("dts/regex.d.ts")); + decls.add(resourceToString("dts/require.d.ts")); + decls.add(resourceToString("dts/set.d.ts")); + decls.add(resourceToString("dts/values/array.d.ts")); + decls.add(resourceToString("dts/values/boolean.d.ts")); + decls.add(resourceToString("dts/values/number.d.ts")); + decls.add(resourceToString("dts/values/errors.d.ts")); + decls.add(resourceToString("dts/values/function.d.ts")); + decls.add(resourceToString("dts/values/object.d.ts")); + decls.add(resourceToString("dts/values/string.d.ts")); + decls.add(resourceToString("dts/values/symbol.d.ts")); + + scope.define("libs", true, ArrayValue.of(decls)); + scope.define(true, new NativeFunction("init", (el, t, args) -> { + ts = Values.function(args[0]); + return null; + })); + + pushMsg(false, scope, Map.of(), "bootstrap.js", resourceToString("js/bootstrap.js"), null); + } +} diff --git a/tsconfig.json b/tsconfig.json index 199cb22..716b087 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,12 +1,29 @@ { - "include": [ "lib/**/*.ts" ], + "files": [ + "lib/lib.d.ts", + "lib/modules.ts", + "lib/utils.ts", + "lib/values/object.ts", + "lib/values/symbol.ts", + "lib/values/function.ts", + "lib/values/errors.ts", + "lib/values/string.ts", + "lib/values/number.ts", + "lib/values/boolean.ts", + "lib/values/array.ts", + "lib/promise.ts", + "lib/map.ts", + "lib/set.ts", + "lib/regex.ts", + "lib/core.ts" + ], "compilerOptions": { - "outDir": "bin/me/topchetoeu/jscript/js", - "declarationDir": "bin/me/topchetoeu/jscript/dts", + "outFile": "bin/me/topchetoeu/jscript/js/core.js", + // "declarationDir": "", + // "declarationDir": "bin/me/topchetoeu/jscript/dts", "target": "ES5", "lib": [], - "module": "CommonJS", - "declaration": true, + "module": "None", "stripInternal": true, "downlevelIteration": true, "esModuleInterop": true, -- 2.45.2 From d1b37074a64d67fb65c76498baebb48586e9a243 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Tue, 29 Aug 2023 11:49:27 +0300 Subject: [PATCH 02/14] fix: resturcture build pipeline --- .gitattributes | 9 -- .github/workflows/tagged-release.yml | 8 +- .gitignore | 3 +- build.js | 65 ++++++++++ lib/tsconfig.json | 33 +++++ meta.json | 5 + src/me/topchetoeu/jscript/Main.java | 1 + src/me/topchetoeu/jscript/Metadata.java | 7 + .../jscript/polyfills/TypescriptEngine.java- | 122 +++++++++--------- tsconfig.json | 33 ----- 10 files changed, 175 insertions(+), 111 deletions(-) delete mode 100644 .gitattributes create mode 100644 build.js create mode 100644 lib/tsconfig.json create mode 100644 meta.json create mode 100644 src/me/topchetoeu/jscript/Metadata.java delete mode 100644 tsconfig.json diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index c53344c..0000000 --- a/.gitattributes +++ /dev/null @@ -1,9 +0,0 @@ -# -# https://help.github.com/articles/dealing-with-line-endings/ -# -# Linux start script should use lf -/gradlew text eol=lf - -# These are Windows script files and should use crlf -*.bat text eol=crlf - diff --git a/.github/workflows/tagged-release.yml b/.github/workflows/tagged-release.yml index 3a43384..7983a85 100644 --- a/.github/workflows/tagged-release.yml +++ b/.github/workflows/tagged-release.yml @@ -18,13 +18,7 @@ jobs: repository: 'java-jscript' - name: "Build" run: | - cd java-jscript; - ls; - mkdir -p dst/classes; - rsync -av --exclude='*.java' src/ dst/classes; - tsc --outDir dst/classes/me/topchetoeu/jscript/js --declarationDir dst/classes/me/topchetoeu/jscript/dts; - find src -name "*.java" | xargs javac -d dst/classes; - jar -c -f dst/jscript.jar -e me.topchetoeu.jscript.Main -C dst/classes . + cd java-jscript; node ./build.js - uses: "marvinpinto/action-automatic-releases@latest" with: diff --git a/.gitignore b/.gitignore index 9297058..9e46f3f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ out build bin dst -/*.js \ No newline at end of file +/*.js +!/build.js \ No newline at end of file diff --git a/build.js b/build.js new file mode 100644 index 0000000..718963f --- /dev/null +++ b/build.js @@ -0,0 +1,65 @@ +const { spawn } = require('child_process'); +const fs = require('fs/promises'); +const pt = require('path'); +const conf = require('./meta'); +const { argv } = require('process'); + +async function* find(src, dst, wildcard) { + const stat = await fs.stat(src); + + if (stat.isDirectory()) { + for (const el of await fs.readdir(src)) { + for await (const res of find(pt.join(src, el), dst ? pt.join(dst, el) : undefined, wildcard)) yield res; + } + } + else if (stat.isFile() && wildcard(src)) yield dst ? { src, dst } : src; +} +async function copy(src, dst, wildcard) { + const promises = []; + + for await (const el of find(src, dst, wildcard)) { + promises.push((async () => { + await fs.mkdir(pt.dirname(el.dst), { recursive: true }); + await fs.copyFile(el.src, el.dst); + })()); + } + + await Promise.all(promises); +} + +function run(cmd, ...args) { + return new Promise((res, rej) => { + const proc = spawn(cmd, args, { stdio: 'inherit' }); + proc.once('exit', code => { + if (code === 0) res(code); + else rej(new Error(`Process ${cmd} exited with code ${code}.`)); + }); + }) +} + +async function compileJava() { + await fs.writeFile('Metadata.java', (await fs.readFile('src/me/topchetoeu/jscript/Metadata.java')).toString() + .replace('${VERSION}', conf.version) + .replace('${NAME}', conf.name) + .replace('${AUTHOR}', conf.author) + ); + + const args = ['-d', 'dst/classes', 'Metadata.java']; + for await (const path of find('src', undefined, v => v.endsWith('.java') && !v.endsWith('Metadata.java'))) args.push(path); + await run('javac', ...args); + await fs.rm('Metadata.java'); +} + +(async () => { + try { + fs.rm('dst', { recursive: true }); + await copy('src', 'dst/classes', v => !v.endsWith('.java')); + await run('tsc', '-p', 'lib/tsconfig.json', '--outFile', 'dst/classes/me/topchetoeu/jscript/js/core.js'), + await compileJava(); + await run('jar', '-c', '-f', 'dst/jscript.jar', '-e', 'me.topchetoeu.jscript.Main', '-C', 'dst/classes', '.'); + } + catch (e) { + if (argv.includes('debug')) throw e; + else console.log(e.toString()); + } +})(); diff --git a/lib/tsconfig.json b/lib/tsconfig.json new file mode 100644 index 0000000..5172e2d --- /dev/null +++ b/lib/tsconfig.json @@ -0,0 +1,33 @@ +{ + "files": [ + "lib.d.ts", + "modules.ts", + "utils.ts", + "values/object.ts", + "values/symbol.ts", + "values/function.ts", + "values/errors.ts", + "values/string.ts", + "values/number.ts", + "values/boolean.ts", + "values/array.ts", + "promise.ts", + "map.ts", + "set.ts", + "regex.ts", + "core.ts" + ], + "compilerOptions": { + "outFile": "../bin/me/topchetoeu/jscript/js/core.js", + // "declarationDir": "", + // "declarationDir": "bin/me/topchetoeu/jscript/dts", + "target": "ES5", + "lib": [], + "module": "None", + "stripInternal": true, + "downlevelIteration": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + } +} diff --git a/meta.json b/meta.json new file mode 100644 index 0000000..d888310 --- /dev/null +++ b/meta.json @@ -0,0 +1,5 @@ +{ + "version": "0.0.1-alpha", + "name": "java-jscript", + "author": "TopchetoEU" +} \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/Main.java b/src/me/topchetoeu/jscript/Main.java index 95f00f2..b72f080 100644 --- a/src/me/topchetoeu/jscript/Main.java +++ b/src/me/topchetoeu/jscript/Main.java @@ -50,6 +50,7 @@ public class Main { }; public static void main(String args[]) { + System.out.println(String.format("Running %s v%s by %s", Metadata.NAME, Metadata.VERSION, Metadata.AUTHOR)); var in = new BufferedReader(new InputStreamReader(System.in)); engine = new Engine(); var scope = engine.global().globalChild(); diff --git a/src/me/topchetoeu/jscript/Metadata.java b/src/me/topchetoeu/jscript/Metadata.java new file mode 100644 index 0000000..5a0bf0b --- /dev/null +++ b/src/me/topchetoeu/jscript/Metadata.java @@ -0,0 +1,7 @@ +package me.topchetoeu.jscript; + +public class Metadata { + public static final String VERSION = "${VERSION}"; + public static final String AUTHOR = "${AUTHOR}"; + public static final String NAME = "${NAME}"; +} diff --git a/src/me/topchetoeu/jscript/polyfills/TypescriptEngine.java- b/src/me/topchetoeu/jscript/polyfills/TypescriptEngine.java- index 3d379ce..54b677e 100644 --- a/src/me/topchetoeu/jscript/polyfills/TypescriptEngine.java- +++ b/src/me/topchetoeu/jscript/polyfills/TypescriptEngine.java- @@ -1,61 +1,61 @@ -package me.topchetoeu.jscript.polyfills; - -import java.io.File; -import java.util.ArrayList; -import java.util.Map; - -import me.topchetoeu.jscript.engine.scope.GlobalScope; -import me.topchetoeu.jscript.engine.values.ArrayValue; -import me.topchetoeu.jscript.engine.values.FunctionValue; -import me.topchetoeu.jscript.engine.values.NativeFunction; -import me.topchetoeu.jscript.engine.values.Values; - -public class TypescriptEngine extends PolyfillEngine { - private FunctionValue ts; - - @Override - public FunctionValue compile(GlobalScope scope, String filename, String raw) throws InterruptedException { - if (ts != null) { - var res = Values.array(ts.call(context(), null, filename, raw)); - var src = Values.toString(context(), res.get(0)); - var func = Values.function(res.get(1)); - - var compiled = super.compile(scope, filename, src); - - return new NativeFunction(null, (ctx, thisArg, args) -> { - return func.call(context(), null, compiled, thisArg, new ArrayValue(args)); - }); - } - return super.compile(scope, filename, raw); - } - - public TypescriptEngine(File root) { - super(root); - var scope = global().globalChild(); - - var decls = new ArrayList(); - decls.add(resourceToString("dts/core.d.ts")); - decls.add(resourceToString("dts/iterators.d.ts")); - decls.add(resourceToString("dts/map.d.ts")); - decls.add(resourceToString("dts/promise.d.ts")); - decls.add(resourceToString("dts/regex.d.ts")); - decls.add(resourceToString("dts/require.d.ts")); - decls.add(resourceToString("dts/set.d.ts")); - decls.add(resourceToString("dts/values/array.d.ts")); - decls.add(resourceToString("dts/values/boolean.d.ts")); - decls.add(resourceToString("dts/values/number.d.ts")); - decls.add(resourceToString("dts/values/errors.d.ts")); - decls.add(resourceToString("dts/values/function.d.ts")); - decls.add(resourceToString("dts/values/object.d.ts")); - decls.add(resourceToString("dts/values/string.d.ts")); - decls.add(resourceToString("dts/values/symbol.d.ts")); - - scope.define("libs", true, ArrayValue.of(decls)); - scope.define(true, new NativeFunction("init", (el, t, args) -> { - ts = Values.function(args[0]); - return null; - })); - - pushMsg(false, scope, Map.of(), "bootstrap.js", resourceToString("js/bootstrap.js"), null); - } -} +package me.topchetoeu.jscript.polyfills; + +import java.io.File; +import java.util.ArrayList; +import java.util.Map; + +import me.topchetoeu.jscript.engine.scope.GlobalScope; +import me.topchetoeu.jscript.engine.values.ArrayValue; +import me.topchetoeu.jscript.engine.values.FunctionValue; +import me.topchetoeu.jscript.engine.values.NativeFunction; +import me.topchetoeu.jscript.engine.values.Values; + +public class TypescriptEngine extends PolyfillEngine { + private FunctionValue ts; + + @Override + public FunctionValue compile(GlobalScope scope, String filename, String raw) throws InterruptedException { + if (ts != null) { + var res = Values.array(ts.call(context(), null, filename, raw)); + var src = Values.toString(context(), res.get(0)); + var func = Values.function(res.get(1)); + + var compiled = super.compile(scope, filename, src); + + return new NativeFunction(null, (ctx, thisArg, args) -> { + return func.call(context(), null, compiled, thisArg, new ArrayValue(args)); + }); + } + return super.compile(scope, filename, raw); + } + + public TypescriptEngine(File root) { + super(root); + var scope = global().globalChild(); + + var decls = new ArrayList(); + decls.add(resourceToString("dts/core.d.ts")); + decls.add(resourceToString("dts/iterators.d.ts")); + decls.add(resourceToString("dts/map.d.ts")); + decls.add(resourceToString("dts/promise.d.ts")); + decls.add(resourceToString("dts/regex.d.ts")); + decls.add(resourceToString("dts/require.d.ts")); + decls.add(resourceToString("dts/set.d.ts")); + decls.add(resourceToString("dts/values/array.d.ts")); + decls.add(resourceToString("dts/values/boolean.d.ts")); + decls.add(resourceToString("dts/values/number.d.ts")); + decls.add(resourceToString("dts/values/errors.d.ts")); + decls.add(resourceToString("dts/values/function.d.ts")); + decls.add(resourceToString("dts/values/object.d.ts")); + decls.add(resourceToString("dts/values/string.d.ts")); + decls.add(resourceToString("dts/values/symbol.d.ts")); + + scope.define("libs", true, ArrayValue.of(decls)); + scope.define(true, new NativeFunction("init", (el, t, args) -> { + ts = Values.function(args[0]); + return null; + })); + + pushMsg(false, scope, Map.of(), "bootstrap.js", resourceToString("js/bootstrap.js"), null); + } +} diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 716b087..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "files": [ - "lib/lib.d.ts", - "lib/modules.ts", - "lib/utils.ts", - "lib/values/object.ts", - "lib/values/symbol.ts", - "lib/values/function.ts", - "lib/values/errors.ts", - "lib/values/string.ts", - "lib/values/number.ts", - "lib/values/boolean.ts", - "lib/values/array.ts", - "lib/promise.ts", - "lib/map.ts", - "lib/set.ts", - "lib/regex.ts", - "lib/core.ts" - ], - "compilerOptions": { - "outFile": "bin/me/topchetoeu/jscript/js/core.js", - // "declarationDir": "", - // "declarationDir": "bin/me/topchetoeu/jscript/dts", - "target": "ES5", - "lib": [], - "module": "None", - "stripInternal": true, - "downlevelIteration": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - } -} -- 2.45.2 From 29d3f378a51b10260064034ed71a70a81825311f Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Mon, 4 Sep 2023 14:30:57 +0300 Subject: [PATCH 03/14] major changes in preparations for environments --- .../debug/DebugServer.java | 308 ++++---- .../debug/DebugState.java | 104 +-- .../engine => dead-code}/debug/Http.java | 130 ++-- .../debug/HttpRequest.java | 32 +- .../engine => dead-code}/debug/V8Error.java | 38 +- .../engine => dead-code}/debug/V8Event.java | 44 +- .../engine => dead-code}/debug/V8Message.java | 100 +-- .../engine => dead-code}/debug/V8Result.java | 44 +- .../engine => dead-code}/debug/WebSocket.java | 370 ++++----- .../debug/WebSocketMessage.java | 58 +- .../debug/handlers/DebuggerHandles.java | 58 +- .../modules/FileModuleProvider.java | 100 +-- .../engine => dead-code}/modules/Module.java | 114 +-- .../modules/ModuleManager.java | 160 ++-- .../modules/ModuleProvider.java | 16 +- lib/core.ts | 122 ++- lib/lib.d.ts | 66 +- lib/map.ts | 107 ++- lib/modules.ts | 3 +- lib/promise.ts | 202 ++++- lib/regex.ts | 242 +++--- lib/set.ts | 98 ++- lib/timeout.ts | 38 + lib/tsconfig.json | 1 + lib/utils.ts | 16 +- lib/values/array.ts | 27 +- lib/values/boolean.ts | 4 +- lib/values/errors.ts | 53 +- lib/values/function.ts | 44 +- lib/values/number.ts | 16 +- lib/values/object.ts | 121 ++- lib/values/string.ts | 87 ++- lib/values/symbol.ts | 33 +- package-lock.json | 6 + package.json | 1 + src/me/topchetoeu/jscript/Main.java | 81 +- .../jscript/engine/CallContext.java | 21 +- src/me/topchetoeu/jscript/engine/Engine.java | 124 +-- .../jscript/engine/Environment.java | 81 ++ .../jscript/engine/WrappersProvider.java | 8 + .../jscript/engine/frame/CodeFrame.java | 11 +- .../jscript/engine/frame/Runners.java | 16 +- .../jscript/engine/scope/GlobalScope.java | 14 +- .../jscript/engine/scope/LocalScope.java | 16 +- .../jscript/engine/values/CodeFunction.java | 10 +- .../jscript/engine/values/NativeWrapper.java | 2 +- .../jscript/engine/values/ObjectValue.java | 38 +- .../jscript/engine/values/Values.java | 16 +- .../jscript/interop/NativeTypeRegister.java | 7 +- .../topchetoeu/jscript/parsing/Parsing.java | 14 +- .../jscript/polyfills/GeneratorFunction.java | 4 +- .../jscript/polyfills/Internals.java | 317 +++----- ...lyfillEngine.java => PolyfillEngine.java-} | 210 ++--- .../polyfills/{Promise.java => Promise.java-} | 720 +++++++++--------- 54 files changed, 2512 insertions(+), 2161 deletions(-) rename {src/me/topchetoeu/jscript/engine => dead-code}/debug/DebugServer.java (97%) rename {src/me/topchetoeu/jscript/engine => dead-code}/debug/DebugState.java (96%) rename {src/me/topchetoeu/jscript/engine => dead-code}/debug/Http.java (97%) rename {src/me/topchetoeu/jscript/engine => dead-code}/debug/HttpRequest.java (95%) rename {src/me/topchetoeu/jscript/engine => dead-code}/debug/V8Error.java (95%) rename {src/me/topchetoeu/jscript/engine => dead-code}/debug/V8Event.java (95%) rename {src/me/topchetoeu/jscript/engine => dead-code}/debug/V8Message.java (96%) rename {src/me/topchetoeu/jscript/engine => dead-code}/debug/V8Result.java (95%) rename {src/me/topchetoeu/jscript/engine => dead-code}/debug/WebSocket.java (96%) rename {src/me/topchetoeu/jscript/engine => dead-code}/debug/WebSocketMessage.java (96%) rename {src/me/topchetoeu/jscript/engine => dead-code}/debug/handlers/DebuggerHandles.java (97%) rename {src/me/topchetoeu/jscript/engine => dead-code}/modules/FileModuleProvider.java (97%) rename {src/me/topchetoeu/jscript/engine => dead-code}/modules/Module.java (97%) rename {src/me/topchetoeu/jscript/engine => dead-code}/modules/ModuleManager.java (96%) rename {src/me/topchetoeu/jscript/engine => dead-code}/modules/ModuleProvider.java (97%) create mode 100644 lib/timeout.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/me/topchetoeu/jscript/engine/Environment.java create mode 100644 src/me/topchetoeu/jscript/engine/WrappersProvider.java rename src/me/topchetoeu/jscript/polyfills/{PolyfillEngine.java => PolyfillEngine.java-} (97%) rename src/me/topchetoeu/jscript/polyfills/{Promise.java => Promise.java-} (97%) diff --git a/src/me/topchetoeu/jscript/engine/debug/DebugServer.java b/dead-code/debug/DebugServer.java similarity index 97% rename from src/me/topchetoeu/jscript/engine/debug/DebugServer.java rename to dead-code/debug/DebugServer.java index b5e6730..23a6ee9 100644 --- a/src/me/topchetoeu/jscript/engine/debug/DebugServer.java +++ b/dead-code/debug/DebugServer.java @@ -1,154 +1,154 @@ -package me.topchetoeu.jscript.engine.debug; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.security.MessageDigest; -import java.util.Base64; - -import me.topchetoeu.jscript.engine.Engine; -import me.topchetoeu.jscript.engine.debug.WebSocketMessage.Type; -import me.topchetoeu.jscript.engine.debug.handlers.DebuggerHandles; -import me.topchetoeu.jscript.exceptions.SyntaxException; - -public class DebugServer { - public static String browserDisplayName = "jscript"; - public static String targetName = "target"; - - public final Engine engine; - - private static void send(Socket socket, String val) throws IOException { - Http.writeResponse(socket.getOutputStream(), 200, "OK", "application/json", val.getBytes()); - } - - // SILENCE JAVA - private MessageDigest getDigestInstance() { - try { - return MessageDigest.getInstance("sha1"); - } - catch (Throwable a) { return null; } - } - - private static Thread runAsync(Runnable func, String name) { - var res = new Thread(func); - res.setName(name); - res.start(); - return res; - } - - private void handle(WebSocket ws) throws InterruptedException, IOException { - WebSocketMessage raw; - - while ((raw = ws.receive()) != null) { - if (raw.type != Type.Text) { - ws.send(new V8Error("Expected a text message.")); - continue; - } - - V8Message msg; - - try { - msg = new V8Message(raw.textData()); - } - catch (SyntaxException e) { - ws.send(new V8Error(e.getMessage())); - return; - } - - switch (msg.name) { - case "Debugger.enable": DebuggerHandles.enable(msg, engine, ws); continue; - case "Debugger.disable": DebuggerHandles.disable(msg, engine, ws); continue; - case "Debugger.stepInto": DebuggerHandles.stepInto(msg, engine, ws); continue; - } - } - } - private void onWsConnect(HttpRequest req, Socket socket) throws IOException { - var key = req.headers.get("sec-websocket-key"); - - if (key == null) { - Http.writeResponse( - socket.getOutputStream(), 426, "Upgrade Required", "text/txt", - "Expected a WS upgrade".getBytes() - ); - return; - } - - var resKey = Base64.getEncoder().encodeToString(getDigestInstance().digest( - (key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes() - )); - - Http.writeCode(socket.getOutputStream(), 101, "Switching Protocols"); - Http.writeHeader(socket.getOutputStream(), "Connection", "Upgrade"); - Http.writeHeader(socket.getOutputStream(), "Sec-WebSocket-Accept", resKey); - Http.writeLastHeader(socket.getOutputStream(), "Upgrade", "WebSocket"); - - var ws = new WebSocket(socket); - - runAsync(() -> { - try { - handle(ws); - } - catch (InterruptedException e) { return; } - catch (IOException e) { e.printStackTrace(); } - finally { ws.close(); } - }, "Debug Server Message Reader"); - runAsync(() -> { - try { - handle(ws); - } - catch (InterruptedException e) { return; } - catch (IOException e) { e.printStackTrace(); } - finally { ws.close(); } - }, "Debug Server Event Writer"); - } - - public void open(InetSocketAddress address) throws IOException { - ServerSocket server = new ServerSocket(); - server.bind(address); - - try { - while (true) { - var socket = server.accept(); - var req = Http.readRequest(socket.getInputStream()); - - switch (req.path) { - case "/json/version": - send(socket, "{\"Browser\":\"" + browserDisplayName + "\",\"Protocol-Version\":\"1.2\"}"); - break; - case "/json/list": - case "/json": - var addr = "ws://" + address.getHostString() + ":" + address.getPort() + "/devtools/page/" + targetName; - send(socket, "{\"id\":\"" + browserDisplayName + "\",\"webSocketDebuggerUrl\":\"" + addr + "\"}"); - break; - case "/json/new": - case "/json/activate": - case "/json/protocol": - case "/json/close": - case "/devtools/inspector.html": - Http.writeResponse( - socket.getOutputStream(), - 501, "Not Implemented", "text/txt", - "This feature isn't (and won't be) implemented.".getBytes() - ); - break; - default: - if (req.path.equals("/devtools/page/" + targetName)) onWsConnect(req, socket); - else { - Http.writeResponse( - socket.getOutputStream(), - 404, "Not Found", "text/txt", - "Not found :/".getBytes() - ); - } - break; - } - } - } - finally { server.close(); } - } - - public DebugServer(Engine engine) { - this.engine = engine; - } -} +package me.topchetoeu.jscript.engine.debug; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.security.MessageDigest; +import java.util.Base64; + +import me.topchetoeu.jscript.engine.Engine; +import me.topchetoeu.jscript.engine.debug.WebSocketMessage.Type; +import me.topchetoeu.jscript.engine.debug.handlers.DebuggerHandles; +import me.topchetoeu.jscript.exceptions.SyntaxException; + +public class DebugServer { + public static String browserDisplayName = "jscript"; + public static String targetName = "target"; + + public final Engine engine; + + private static void send(Socket socket, String val) throws IOException { + Http.writeResponse(socket.getOutputStream(), 200, "OK", "application/json", val.getBytes()); + } + + // SILENCE JAVA + private MessageDigest getDigestInstance() { + try { + return MessageDigest.getInstance("sha1"); + } + catch (Throwable a) { return null; } + } + + private static Thread runAsync(Runnable func, String name) { + var res = new Thread(func); + res.setName(name); + res.start(); + return res; + } + + private void handle(WebSocket ws) throws InterruptedException, IOException { + WebSocketMessage raw; + + while ((raw = ws.receive()) != null) { + if (raw.type != Type.Text) { + ws.send(new V8Error("Expected a text message.")); + continue; + } + + V8Message msg; + + try { + msg = new V8Message(raw.textData()); + } + catch (SyntaxException e) { + ws.send(new V8Error(e.getMessage())); + return; + } + + switch (msg.name) { + case "Debugger.enable": DebuggerHandles.enable(msg, engine, ws); continue; + case "Debugger.disable": DebuggerHandles.disable(msg, engine, ws); continue; + case "Debugger.stepInto": DebuggerHandles.stepInto(msg, engine, ws); continue; + } + } + } + private void onWsConnect(HttpRequest req, Socket socket) throws IOException { + var key = req.headers.get("sec-websocket-key"); + + if (key == null) { + Http.writeResponse( + socket.getOutputStream(), 426, "Upgrade Required", "text/txt", + "Expected a WS upgrade".getBytes() + ); + return; + } + + var resKey = Base64.getEncoder().encodeToString(getDigestInstance().digest( + (key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes() + )); + + Http.writeCode(socket.getOutputStream(), 101, "Switching Protocols"); + Http.writeHeader(socket.getOutputStream(), "Connection", "Upgrade"); + Http.writeHeader(socket.getOutputStream(), "Sec-WebSocket-Accept", resKey); + Http.writeLastHeader(socket.getOutputStream(), "Upgrade", "WebSocket"); + + var ws = new WebSocket(socket); + + runAsync(() -> { + try { + handle(ws); + } + catch (InterruptedException e) { return; } + catch (IOException e) { e.printStackTrace(); } + finally { ws.close(); } + }, "Debug Server Message Reader"); + runAsync(() -> { + try { + handle(ws); + } + catch (InterruptedException e) { return; } + catch (IOException e) { e.printStackTrace(); } + finally { ws.close(); } + }, "Debug Server Event Writer"); + } + + public void open(InetSocketAddress address) throws IOException { + ServerSocket server = new ServerSocket(); + server.bind(address); + + try { + while (true) { + var socket = server.accept(); + var req = Http.readRequest(socket.getInputStream()); + + switch (req.path) { + case "/json/version": + send(socket, "{\"Browser\":\"" + browserDisplayName + "\",\"Protocol-Version\":\"1.2\"}"); + break; + case "/json/list": + case "/json": + var addr = "ws://" + address.getHostString() + ":" + address.getPort() + "/devtools/page/" + targetName; + send(socket, "{\"id\":\"" + browserDisplayName + "\",\"webSocketDebuggerUrl\":\"" + addr + "\"}"); + break; + case "/json/new": + case "/json/activate": + case "/json/protocol": + case "/json/close": + case "/devtools/inspector.html": + Http.writeResponse( + socket.getOutputStream(), + 501, "Not Implemented", "text/txt", + "This feature isn't (and won't be) implemented.".getBytes() + ); + break; + default: + if (req.path.equals("/devtools/page/" + targetName)) onWsConnect(req, socket); + else { + Http.writeResponse( + socket.getOutputStream(), + 404, "Not Found", "text/txt", + "Not found :/".getBytes() + ); + } + break; + } + } + } + finally { server.close(); } + } + + public DebugServer(Engine engine) { + this.engine = engine; + } +} diff --git a/src/me/topchetoeu/jscript/engine/debug/DebugState.java b/dead-code/debug/DebugState.java similarity index 96% rename from src/me/topchetoeu/jscript/engine/debug/DebugState.java rename to dead-code/debug/DebugState.java index 3cf2209..48c4d90 100644 --- a/src/me/topchetoeu/jscript/engine/debug/DebugState.java +++ b/dead-code/debug/DebugState.java @@ -1,52 +1,52 @@ -package me.topchetoeu.jscript.engine.debug; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; - -import me.topchetoeu.jscript.Location; -import me.topchetoeu.jscript.engine.BreakpointData; -import me.topchetoeu.jscript.engine.DebugCommand; -import me.topchetoeu.jscript.engine.frame.CodeFrame; -import me.topchetoeu.jscript.events.Event; - -public class DebugState { - private boolean paused = false; - - public final HashSet breakpoints = new HashSet<>(); - public final List frames = new ArrayList<>(); - public final Map sources = new HashMap<>(); - - public final Event breakpointNotifier = new Event<>(); - public final Event commandNotifier = new Event<>(); - public final Event sourceAdded = new Event<>(); - - public DebugState pushFrame(CodeFrame frame) { - frames.add(frame); - return this; - } - public DebugState popFrame() { - if (frames.size() > 0) frames.remove(frames.size() - 1); - return this; - } - - public DebugCommand pause(BreakpointData data) throws InterruptedException { - paused = true; - breakpointNotifier.next(data); - return commandNotifier.toAwaitable().await(); - } - public void resume(DebugCommand command) { - paused = false; - commandNotifier.next(command); - } - - // public void addSource()? - - public boolean paused() { return paused; } - - public boolean isBreakpoint(Location loc) { - return breakpoints.contains(loc); - } -} +package me.topchetoeu.jscript.engine.debug; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import me.topchetoeu.jscript.Location; +import me.topchetoeu.jscript.engine.BreakpointData; +import me.topchetoeu.jscript.engine.DebugCommand; +import me.topchetoeu.jscript.engine.frame.CodeFrame; +import me.topchetoeu.jscript.events.Event; + +public class DebugState { + private boolean paused = false; + + public final HashSet breakpoints = new HashSet<>(); + public final List frames = new ArrayList<>(); + public final Map sources = new HashMap<>(); + + public final Event breakpointNotifier = new Event<>(); + public final Event commandNotifier = new Event<>(); + public final Event sourceAdded = new Event<>(); + + public DebugState pushFrame(CodeFrame frame) { + frames.add(frame); + return this; + } + public DebugState popFrame() { + if (frames.size() > 0) frames.remove(frames.size() - 1); + return this; + } + + public DebugCommand pause(BreakpointData data) throws InterruptedException { + paused = true; + breakpointNotifier.next(data); + return commandNotifier.toAwaitable().await(); + } + public void resume(DebugCommand command) { + paused = false; + commandNotifier.next(command); + } + + // public void addSource()? + + public boolean paused() { return paused; } + + public boolean isBreakpoint(Location loc) { + return breakpoints.contains(loc); + } +} diff --git a/src/me/topchetoeu/jscript/engine/debug/Http.java b/dead-code/debug/Http.java similarity index 97% rename from src/me/topchetoeu/jscript/engine/debug/Http.java rename to dead-code/debug/Http.java index 2760dfa..291b745 100644 --- a/src/me/topchetoeu/jscript/engine/debug/Http.java +++ b/dead-code/debug/Http.java @@ -1,65 +1,65 @@ -package me.topchetoeu.jscript.engine.debug; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.util.HashMap; -import java.util.IllegalFormatException; - -// We dont need no http library -public class Http { - public static void writeCode(OutputStream str, int code, String name) throws IOException { - str.write(("HTTP/1.1 " + code + " " + name + "\r\n").getBytes()); - } - public static void writeHeader(OutputStream str, String name, String value) throws IOException { - str.write((name + ": " + value + "\r\n").getBytes()); - } - public static void writeLastHeader(OutputStream str, String name, String value) throws IOException { - str.write((name + ": " + value + "\r\n").getBytes()); - writeHeadersEnd(str); - } - public static void writeHeadersEnd(OutputStream str) throws IOException { - str.write("\n".getBytes()); - } - - public static void writeResponse(OutputStream str, int code, String name, String type, byte[] data) throws IOException { - writeCode(str, code, name); - writeHeader(str, "Content-Type", type); - writeLastHeader(str, "Content-Length", data.length + ""); - str.write(data); - str.close(); - } - - public static HttpRequest readRequest(InputStream str) throws IOException { - var lines = new BufferedReader(new InputStreamReader(str)); - var line = lines.readLine(); - var i1 = line.indexOf(" "); - var i2 = line.lastIndexOf(" "); - - var method = line.substring(0, i1).trim().toUpperCase(); - var path = line.substring(i1 + 1, i2).trim(); - var headers = new HashMap(); - - while (!(line = lines.readLine()).isEmpty()) { - var i = line.indexOf(":"); - if (i < 0) continue; - var name = line.substring(0, i).trim().toLowerCase(); - var value = line.substring(i + 1).trim(); - - if (name.length() == 0) continue; - headers.put(name, value); - } - - if (headers.containsKey("content-length")) { - try { - var i = Integer.parseInt(headers.get("content-length")); - str.skip(i); - } - catch (IllegalFormatException e) { /* ¯\_(ツ)_/¯ */ } - } - - return new HttpRequest(method, path, headers); - } -} +package me.topchetoeu.jscript.engine.debug; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.IllegalFormatException; + +// We dont need no http library +public class Http { + public static void writeCode(OutputStream str, int code, String name) throws IOException { + str.write(("HTTP/1.1 " + code + " " + name + "\r\n").getBytes()); + } + public static void writeHeader(OutputStream str, String name, String value) throws IOException { + str.write((name + ": " + value + "\r\n").getBytes()); + } + public static void writeLastHeader(OutputStream str, String name, String value) throws IOException { + str.write((name + ": " + value + "\r\n").getBytes()); + writeHeadersEnd(str); + } + public static void writeHeadersEnd(OutputStream str) throws IOException { + str.write("\n".getBytes()); + } + + public static void writeResponse(OutputStream str, int code, String name, String type, byte[] data) throws IOException { + writeCode(str, code, name); + writeHeader(str, "Content-Type", type); + writeLastHeader(str, "Content-Length", data.length + ""); + str.write(data); + str.close(); + } + + public static HttpRequest readRequest(InputStream str) throws IOException { + var lines = new BufferedReader(new InputStreamReader(str)); + var line = lines.readLine(); + var i1 = line.indexOf(" "); + var i2 = line.lastIndexOf(" "); + + var method = line.substring(0, i1).trim().toUpperCase(); + var path = line.substring(i1 + 1, i2).trim(); + var headers = new HashMap(); + + while (!(line = lines.readLine()).isEmpty()) { + var i = line.indexOf(":"); + if (i < 0) continue; + var name = line.substring(0, i).trim().toLowerCase(); + var value = line.substring(i + 1).trim(); + + if (name.length() == 0) continue; + headers.put(name, value); + } + + if (headers.containsKey("content-length")) { + try { + var i = Integer.parseInt(headers.get("content-length")); + str.skip(i); + } + catch (IllegalFormatException e) { /* ¯\_(ツ)_/¯ */ } + } + + return new HttpRequest(method, path, headers); + } +} diff --git a/src/me/topchetoeu/jscript/engine/debug/HttpRequest.java b/dead-code/debug/HttpRequest.java similarity index 95% rename from src/me/topchetoeu/jscript/engine/debug/HttpRequest.java rename to dead-code/debug/HttpRequest.java index e4a1ae6..3fa9708 100644 --- a/src/me/topchetoeu/jscript/engine/debug/HttpRequest.java +++ b/dead-code/debug/HttpRequest.java @@ -1,16 +1,16 @@ -package me.topchetoeu.jscript.engine.debug; - -import java.util.Map; - -public class HttpRequest { - public final String method; - public final String path; - public final Map headers; - - public HttpRequest(String method, String path, Map headers) { - this.method = method; - this.path = path; - this.headers = headers; - } -} - +package me.topchetoeu.jscript.engine.debug; + +import java.util.Map; + +public class HttpRequest { + public final String method; + public final String path; + public final Map headers; + + public HttpRequest(String method, String path, Map headers) { + this.method = method; + this.path = path; + this.headers = headers; + } +} + diff --git a/src/me/topchetoeu/jscript/engine/debug/V8Error.java b/dead-code/debug/V8Error.java similarity index 95% rename from src/me/topchetoeu/jscript/engine/debug/V8Error.java rename to dead-code/debug/V8Error.java index 778160f..854e1cd 100644 --- a/src/me/topchetoeu/jscript/engine/debug/V8Error.java +++ b/dead-code/debug/V8Error.java @@ -1,19 +1,19 @@ -package me.topchetoeu.jscript.engine.debug; - -import me.topchetoeu.jscript.json.JSON; -import me.topchetoeu.jscript.json.JSONMap; - -public class V8Error { - public final String message; - - public V8Error(String message) { - this.message = message; - } - - @Override - public String toString() { - return JSON.stringify(new JSONMap().set("error", new JSONMap() - .set("message", message) - )); - } -} +package me.topchetoeu.jscript.engine.debug; + +import me.topchetoeu.jscript.json.JSON; +import me.topchetoeu.jscript.json.JSONMap; + +public class V8Error { + public final String message; + + public V8Error(String message) { + this.message = message; + } + + @Override + public String toString() { + return JSON.stringify(new JSONMap().set("error", new JSONMap() + .set("message", message) + )); + } +} diff --git a/src/me/topchetoeu/jscript/engine/debug/V8Event.java b/dead-code/debug/V8Event.java similarity index 95% rename from src/me/topchetoeu/jscript/engine/debug/V8Event.java rename to dead-code/debug/V8Event.java index 437c337..a83e20b 100644 --- a/src/me/topchetoeu/jscript/engine/debug/V8Event.java +++ b/dead-code/debug/V8Event.java @@ -1,22 +1,22 @@ -package me.topchetoeu.jscript.engine.debug; - -import me.topchetoeu.jscript.json.JSON; -import me.topchetoeu.jscript.json.JSONMap; - -public class V8Event { - public final String name; - public final JSONMap params; - - public V8Event(String name, JSONMap params) { - this.name = name; - this.params = params; - } - - @Override - public String toString() { - return JSON.stringify(new JSONMap() - .set("method", name) - .set("params", params) - ); - } -} +package me.topchetoeu.jscript.engine.debug; + +import me.topchetoeu.jscript.json.JSON; +import me.topchetoeu.jscript.json.JSONMap; + +public class V8Event { + public final String name; + public final JSONMap params; + + public V8Event(String name, JSONMap params) { + this.name = name; + this.params = params; + } + + @Override + public String toString() { + return JSON.stringify(new JSONMap() + .set("method", name) + .set("params", params) + ); + } +} diff --git a/src/me/topchetoeu/jscript/engine/debug/V8Message.java b/dead-code/debug/V8Message.java similarity index 96% rename from src/me/topchetoeu/jscript/engine/debug/V8Message.java rename to dead-code/debug/V8Message.java index 1c5dfb2..25d4a5a 100644 --- a/src/me/topchetoeu/jscript/engine/debug/V8Message.java +++ b/dead-code/debug/V8Message.java @@ -1,50 +1,50 @@ -package me.topchetoeu.jscript.engine.debug; - -import java.util.Map; - -import me.topchetoeu.jscript.json.JSON; -import me.topchetoeu.jscript.json.JSONElement; -import me.topchetoeu.jscript.json.JSONMap; - -public class V8Message { - public final String name; - public final int id; - public final JSONMap params; - - public V8Message(String name, int id, Map params) { - this.name = name; - this.params = new JSONMap(params); - this.id = id; - } - public V8Result respond(JSONMap result) { - return new V8Result(id, result); - } - public V8Result respond() { - return new V8Result(id, new JSONMap()); - } - - public V8Message(JSONMap raw) { - if (!raw.isNumber("id")) throw new IllegalArgumentException("Expected number property 'id'."); - if (!raw.isString("method")) throw new IllegalArgumentException("Expected string property 'method'."); - - this.name = raw.string("method"); - this.id = (int)raw.number("id"); - this.params = raw.contains("params") ? raw.map("params") : new JSONMap(); - } - public V8Message(String raw) { - this(JSON.parse("json", raw).map()); - } - - public JSONMap toMap() { - var res = new JSONMap(); - return res; - } - @Override - public String toString() { - return JSON.stringify(new JSONMap() - .set("method", name) - .set("params", params) - .set("id", id) - ); - } -} +package me.topchetoeu.jscript.engine.debug; + +import java.util.Map; + +import me.topchetoeu.jscript.json.JSON; +import me.topchetoeu.jscript.json.JSONElement; +import me.topchetoeu.jscript.json.JSONMap; + +public class V8Message { + public final String name; + public final int id; + public final JSONMap params; + + public V8Message(String name, int id, Map params) { + this.name = name; + this.params = new JSONMap(params); + this.id = id; + } + public V8Result respond(JSONMap result) { + return new V8Result(id, result); + } + public V8Result respond() { + return new V8Result(id, new JSONMap()); + } + + public V8Message(JSONMap raw) { + if (!raw.isNumber("id")) throw new IllegalArgumentException("Expected number property 'id'."); + if (!raw.isString("method")) throw new IllegalArgumentException("Expected string property 'method'."); + + this.name = raw.string("method"); + this.id = (int)raw.number("id"); + this.params = raw.contains("params") ? raw.map("params") : new JSONMap(); + } + public V8Message(String raw) { + this(JSON.parse("json", raw).map()); + } + + public JSONMap toMap() { + var res = new JSONMap(); + return res; + } + @Override + public String toString() { + return JSON.stringify(new JSONMap() + .set("method", name) + .set("params", params) + .set("id", id) + ); + } +} diff --git a/src/me/topchetoeu/jscript/engine/debug/V8Result.java b/dead-code/debug/V8Result.java similarity index 95% rename from src/me/topchetoeu/jscript/engine/debug/V8Result.java rename to dead-code/debug/V8Result.java index e2b8bd3..f605cef 100644 --- a/src/me/topchetoeu/jscript/engine/debug/V8Result.java +++ b/dead-code/debug/V8Result.java @@ -1,22 +1,22 @@ -package me.topchetoeu.jscript.engine.debug; - -import me.topchetoeu.jscript.json.JSON; -import me.topchetoeu.jscript.json.JSONMap; - -public class V8Result { - public final int id; - public final JSONMap result; - - public V8Result(int id, JSONMap result) { - this.id = id; - this.result = result; - } - - @Override - public String toString() { - return JSON.stringify(new JSONMap() - .set("id", id) - .set("result", result) - ); - } -} +package me.topchetoeu.jscript.engine.debug; + +import me.topchetoeu.jscript.json.JSON; +import me.topchetoeu.jscript.json.JSONMap; + +public class V8Result { + public final int id; + public final JSONMap result; + + public V8Result(int id, JSONMap result) { + this.id = id; + this.result = result; + } + + @Override + public String toString() { + return JSON.stringify(new JSONMap() + .set("id", id) + .set("result", result) + ); + } +} diff --git a/src/me/topchetoeu/jscript/engine/debug/WebSocket.java b/dead-code/debug/WebSocket.java similarity index 96% rename from src/me/topchetoeu/jscript/engine/debug/WebSocket.java rename to dead-code/debug/WebSocket.java index b282d67..a237121 100644 --- a/src/me/topchetoeu/jscript/engine/debug/WebSocket.java +++ b/dead-code/debug/WebSocket.java @@ -1,185 +1,185 @@ -package me.topchetoeu.jscript.engine.debug; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; - -import me.topchetoeu.jscript.engine.debug.WebSocketMessage.Type; - -public class WebSocket implements AutoCloseable { - public long maxLength = 2000000; - - private Socket socket; - private boolean closed = false; - - private OutputStream out() throws IOException { - return socket.getOutputStream(); - } - private InputStream in() throws IOException { - return socket.getInputStream(); - } - - private long readLen(int byteLen) throws IOException { - long res = 0; - - if (byteLen == 126) { - res |= in().read() << 8; - res |= in().read(); - return res; - } - else if (byteLen == 127) { - res |= in().read() << 56; - res |= in().read() << 48; - res |= in().read() << 40; - res |= in().read() << 32; - res |= in().read() << 24; - res |= in().read() << 16; - res |= in().read() << 8; - res |= in().read(); - return res; - } - else return byteLen; - } - private byte[] readMask(boolean has) throws IOException { - if (has) { - return new byte[] { - (byte)in().read(), - (byte)in().read(), - (byte)in().read(), - (byte)in().read() - }; - } - else return new byte[4]; - } - - private void writeLength(long len) throws IOException { - if (len < 126) { - out().write((int)len); - } - else if (len < 0xFFFF) { - out().write(126); - out().write((int)(len >> 8) & 0xFF); - out().write((int)len & 0xFF); - } - else { - out().write(127); - out().write((int)(len >> 56) & 0xFF); - out().write((int)(len >> 48) & 0xFF); - out().write((int)(len >> 40) & 0xFF); - out().write((int)(len >> 32) & 0xFF); - out().write((int)(len >> 24) & 0xFF); - out().write((int)(len >> 16) & 0xFF); - out().write((int)(len >> 8) & 0xFF); - out().write((int)len & 0xFF); - } - } - private synchronized void write(int type, byte[] data) throws IOException { - out().write(type | 0x80); - writeLength(data.length); - for (int i = 0; i < data.length; i++) { - out().write(data[i]); - } - } - - public void send(String data) throws IOException { - if (closed) throw new IllegalStateException("Object is closed."); - write(1, data.getBytes()); - } - public void send(byte[] data) throws IOException { - if (closed) throw new IllegalStateException("Object is closed."); - write(2, data); - } - public void send(WebSocketMessage msg) throws IOException { - if (msg.type == Type.Binary) send(msg.binaryData()); - else send(msg.textData()); - } - public void send(Object data) throws IOException { - if (closed) throw new IllegalStateException("Object is closed."); - write(1, data.toString().getBytes()); - } - - public void close(String reason) { - if (socket != null) { - try { write(8, reason.getBytes()); } catch (IOException e) { /* ¯\_(ツ)_/¯ */ } - try { socket.close(); } catch (IOException e) { e.printStackTrace(); } - } - - socket = null; - closed = true; - } - public void close() { - close(""); - } - - private WebSocketMessage fail(String reason) { - System.out.println("WebSocket Error: " + reason); - close(reason); - return null; - } - - private byte[] readData() throws IOException { - var maskLen = in().read(); - var hasMask = (maskLen & 0x80) != 0; - var len = (int)readLen(maskLen & 0x7F); - var mask = readMask(hasMask); - - if (len > maxLength) fail("WebSocket Error: client exceeded configured max message size"); - else { - var buff = new byte[len]; - - if (in().read(buff) < len) fail("WebSocket Error: payload too short"); - else { - for (int i = 0; i < len; i++) { - buff[i] ^= mask[(int)(i % 4)]; - } - return buff; - } - } - - return null; - } - - public WebSocketMessage receive() throws InterruptedException { - try { - var data = new ByteArrayOutputStream(); - var type = 0; - - while (socket != null && !closed) { - var finId = in().read(); - var fin = (finId & 0x80) != 0; - int id = finId & 0x0F; - - if (id == 0x8) { close(); return null; } - if (id >= 0x8) { - if (!fin) return fail("WebSocket Error: client-sent control frame was fragmented"); - if (id == 0x9) write(0xA, data.toByteArray()); - continue; - } - - if (type == 0) type = id; - if (type == 0) return fail("WebSocket Error: client used opcode 0x00 for first fragment"); - - var buff = readData(); - if (buff == null) break; - - if (data.size() + buff.length > maxLength) return fail("WebSocket Error: client exceeded configured max message size"); - data.write(buff); - - if (!fin) continue; - var raw = data.toByteArray(); - - if (type == 1) return new WebSocketMessage(new String(raw)); - else return new WebSocketMessage(raw); - } - } - catch (IOException e) { close(); } - - return null; - } - - public WebSocket(Socket socket) { - this.socket = socket; - } -} +package me.topchetoeu.jscript.engine.debug; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +import me.topchetoeu.jscript.engine.debug.WebSocketMessage.Type; + +public class WebSocket implements AutoCloseable { + public long maxLength = 2000000; + + private Socket socket; + private boolean closed = false; + + private OutputStream out() throws IOException { + return socket.getOutputStream(); + } + private InputStream in() throws IOException { + return socket.getInputStream(); + } + + private long readLen(int byteLen) throws IOException { + long res = 0; + + if (byteLen == 126) { + res |= in().read() << 8; + res |= in().read(); + return res; + } + else if (byteLen == 127) { + res |= in().read() << 56; + res |= in().read() << 48; + res |= in().read() << 40; + res |= in().read() << 32; + res |= in().read() << 24; + res |= in().read() << 16; + res |= in().read() << 8; + res |= in().read(); + return res; + } + else return byteLen; + } + private byte[] readMask(boolean has) throws IOException { + if (has) { + return new byte[] { + (byte)in().read(), + (byte)in().read(), + (byte)in().read(), + (byte)in().read() + }; + } + else return new byte[4]; + } + + private void writeLength(long len) throws IOException { + if (len < 126) { + out().write((int)len); + } + else if (len < 0xFFFF) { + out().write(126); + out().write((int)(len >> 8) & 0xFF); + out().write((int)len & 0xFF); + } + else { + out().write(127); + out().write((int)(len >> 56) & 0xFF); + out().write((int)(len >> 48) & 0xFF); + out().write((int)(len >> 40) & 0xFF); + out().write((int)(len >> 32) & 0xFF); + out().write((int)(len >> 24) & 0xFF); + out().write((int)(len >> 16) & 0xFF); + out().write((int)(len >> 8) & 0xFF); + out().write((int)len & 0xFF); + } + } + private synchronized void write(int type, byte[] data) throws IOException { + out().write(type | 0x80); + writeLength(data.length); + for (int i = 0; i < data.length; i++) { + out().write(data[i]); + } + } + + public void send(String data) throws IOException { + if (closed) throw new IllegalStateException("Object is closed."); + write(1, data.getBytes()); + } + public void send(byte[] data) throws IOException { + if (closed) throw new IllegalStateException("Object is closed."); + write(2, data); + } + public void send(WebSocketMessage msg) throws IOException { + if (msg.type == Type.Binary) send(msg.binaryData()); + else send(msg.textData()); + } + public void send(Object data) throws IOException { + if (closed) throw new IllegalStateException("Object is closed."); + write(1, data.toString().getBytes()); + } + + public void close(String reason) { + if (socket != null) { + try { write(8, reason.getBytes()); } catch (IOException e) { /* ¯\_(ツ)_/¯ */ } + try { socket.close(); } catch (IOException e) { e.printStackTrace(); } + } + + socket = null; + closed = true; + } + public void close() { + close(""); + } + + private WebSocketMessage fail(String reason) { + System.out.println("WebSocket Error: " + reason); + close(reason); + return null; + } + + private byte[] readData() throws IOException { + var maskLen = in().read(); + var hasMask = (maskLen & 0x80) != 0; + var len = (int)readLen(maskLen & 0x7F); + var mask = readMask(hasMask); + + if (len > maxLength) fail("WebSocket Error: client exceeded configured max message size"); + else { + var buff = new byte[len]; + + if (in().read(buff) < len) fail("WebSocket Error: payload too short"); + else { + for (int i = 0; i < len; i++) { + buff[i] ^= mask[(int)(i % 4)]; + } + return buff; + } + } + + return null; + } + + public WebSocketMessage receive() throws InterruptedException { + try { + var data = new ByteArrayOutputStream(); + var type = 0; + + while (socket != null && !closed) { + var finId = in().read(); + var fin = (finId & 0x80) != 0; + int id = finId & 0x0F; + + if (id == 0x8) { close(); return null; } + if (id >= 0x8) { + if (!fin) return fail("WebSocket Error: client-sent control frame was fragmented"); + if (id == 0x9) write(0xA, data.toByteArray()); + continue; + } + + if (type == 0) type = id; + if (type == 0) return fail("WebSocket Error: client used opcode 0x00 for first fragment"); + + var buff = readData(); + if (buff == null) break; + + if (data.size() + buff.length > maxLength) return fail("WebSocket Error: client exceeded configured max message size"); + data.write(buff); + + if (!fin) continue; + var raw = data.toByteArray(); + + if (type == 1) return new WebSocketMessage(new String(raw)); + else return new WebSocketMessage(raw); + } + } + catch (IOException e) { close(); } + + return null; + } + + public WebSocket(Socket socket) { + this.socket = socket; + } +} diff --git a/src/me/topchetoeu/jscript/engine/debug/WebSocketMessage.java b/dead-code/debug/WebSocketMessage.java similarity index 96% rename from src/me/topchetoeu/jscript/engine/debug/WebSocketMessage.java rename to dead-code/debug/WebSocketMessage.java index beb06de..e0b82b5 100644 --- a/src/me/topchetoeu/jscript/engine/debug/WebSocketMessage.java +++ b/dead-code/debug/WebSocketMessage.java @@ -1,29 +1,29 @@ -package me.topchetoeu.jscript.engine.debug; - -public class WebSocketMessage { - public static enum Type { - Text, - Binary, - } - - public final Type type; - private final Object data; - - public final String textData() { - if (type != Type.Text) throw new IllegalStateException("Message is not text."); - return (String)data; - } - public final byte[] binaryData() { - if (type != Type.Binary) throw new IllegalStateException("Message is not binary."); - return (byte[])data; - } - - public WebSocketMessage(String data) { - this.type = Type.Text; - this.data = data; - } - public WebSocketMessage(byte[] data) { - this.type = Type.Binary; - this.data = data; - } -} +package me.topchetoeu.jscript.engine.debug; + +public class WebSocketMessage { + public static enum Type { + Text, + Binary, + } + + public final Type type; + private final Object data; + + public final String textData() { + if (type != Type.Text) throw new IllegalStateException("Message is not text."); + return (String)data; + } + public final byte[] binaryData() { + if (type != Type.Binary) throw new IllegalStateException("Message is not binary."); + return (byte[])data; + } + + public WebSocketMessage(String data) { + this.type = Type.Text; + this.data = data; + } + public WebSocketMessage(byte[] data) { + this.type = Type.Binary; + this.data = data; + } +} diff --git a/src/me/topchetoeu/jscript/engine/debug/handlers/DebuggerHandles.java b/dead-code/debug/handlers/DebuggerHandles.java similarity index 97% rename from src/me/topchetoeu/jscript/engine/debug/handlers/DebuggerHandles.java rename to dead-code/debug/handlers/DebuggerHandles.java index fce42c3..a329c5d 100644 --- a/src/me/topchetoeu/jscript/engine/debug/handlers/DebuggerHandles.java +++ b/dead-code/debug/handlers/DebuggerHandles.java @@ -1,29 +1,29 @@ -package me.topchetoeu.jscript.engine.debug.handlers; - -import java.io.IOException; - -import me.topchetoeu.jscript.engine.DebugCommand; -import me.topchetoeu.jscript.engine.Engine; -import me.topchetoeu.jscript.engine.debug.V8Error; -import me.topchetoeu.jscript.engine.debug.V8Message; -import me.topchetoeu.jscript.engine.debug.WebSocket; -import me.topchetoeu.jscript.json.JSONMap; - -public class DebuggerHandles { - public static void enable(V8Message msg, Engine engine, WebSocket ws) throws IOException { - if (engine.debugState == null) ws.send(new V8Error("Debugging is disabled for this engine.")); - else ws.send(msg.respond(new JSONMap().set("debuggerId", 1))); - } - public static void disable(V8Message msg, Engine engine, WebSocket ws) throws IOException { - if (engine.debugState == null) ws.send(msg.respond()); - else ws.send(new V8Error("Debugger may not be disabled.")); - } - public static void stepInto(V8Message msg, Engine engine, WebSocket ws) throws IOException { - if (engine.debugState == null) ws.send(new V8Error("Debugging is disabled for this engine.")); - else if (!engine.debugState.paused()) ws.send(new V8Error("Debugger is not paused.")); - else { - engine.debugState.resume(DebugCommand.STEP_INTO); - ws.send(msg.respond()); - } - } -} +package me.topchetoeu.jscript.engine.debug.handlers; + +import java.io.IOException; + +import me.topchetoeu.jscript.engine.DebugCommand; +import me.topchetoeu.jscript.engine.Engine; +import me.topchetoeu.jscript.engine.debug.V8Error; +import me.topchetoeu.jscript.engine.debug.V8Message; +import me.topchetoeu.jscript.engine.debug.WebSocket; +import me.topchetoeu.jscript.json.JSONMap; + +public class DebuggerHandles { + public static void enable(V8Message msg, Engine engine, WebSocket ws) throws IOException { + if (engine.debugState == null) ws.send(new V8Error("Debugging is disabled for this engine.")); + else ws.send(msg.respond(new JSONMap().set("debuggerId", 1))); + } + public static void disable(V8Message msg, Engine engine, WebSocket ws) throws IOException { + if (engine.debugState == null) ws.send(msg.respond()); + else ws.send(new V8Error("Debugger may not be disabled.")); + } + public static void stepInto(V8Message msg, Engine engine, WebSocket ws) throws IOException { + if (engine.debugState == null) ws.send(new V8Error("Debugging is disabled for this engine.")); + else if (!engine.debugState.paused()) ws.send(new V8Error("Debugger is not paused.")); + else { + engine.debugState.resume(DebugCommand.STEP_INTO); + ws.send(msg.respond()); + } + } +} diff --git a/src/me/topchetoeu/jscript/engine/modules/FileModuleProvider.java b/dead-code/modules/FileModuleProvider.java similarity index 97% rename from src/me/topchetoeu/jscript/engine/modules/FileModuleProvider.java rename to dead-code/modules/FileModuleProvider.java index 59c1f41..ac9f4f6 100644 --- a/src/me/topchetoeu/jscript/engine/modules/FileModuleProvider.java +++ b/dead-code/modules/FileModuleProvider.java @@ -1,50 +1,50 @@ -package me.topchetoeu.jscript.engine.modules; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.file.Path; - -import me.topchetoeu.jscript.polyfills.PolyfillEngine; - -public class FileModuleProvider implements ModuleProvider { - public File root; - public final boolean allowOutside; - - private boolean checkInside(Path modFile) { - return modFile.toAbsolutePath().startsWith(root.toPath().toAbsolutePath()); - } - - @Override - public Module getModule(File cwd, String name) { - var realName = getRealName(cwd, name); - if (realName == null) return null; - var path = Path.of(realName + ".js").normalize(); - - try { - var res = PolyfillEngine.streamToString(new FileInputStream(path.toFile())); - return new Module(realName, path.toString(), res); - } - catch (IOException e) { - return null; - } - } - @Override - public String getRealName(File cwd, String name) { - var path = Path.of(".", Path.of(cwd.toString(), name).normalize().toString()); - var fileName = path.getFileName().toString(); - if (fileName == null) return null; - if (!fileName.equals("index") && path.toFile().isDirectory()) return getRealName(cwd, name + "/index"); - path = Path.of(path.toString() + ".js"); - if (!allowOutside && !checkInside(path)) return null; - if (!path.toFile().isFile() || !path.toFile().canRead()) return null; - var res = path.toString().replace('\\', '/'); - var i = res.lastIndexOf('.'); - return res.substring(0, i); - } - - public FileModuleProvider(File root, boolean allowOutside) { - this.root = root.toPath().normalize().toFile(); - this.allowOutside = allowOutside; - } -} +package me.topchetoeu.jscript.engine.modules; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Path; + +import me.topchetoeu.jscript.polyfills.PolyfillEngine; + +public class FileModuleProvider implements ModuleProvider { + public File root; + public final boolean allowOutside; + + private boolean checkInside(Path modFile) { + return modFile.toAbsolutePath().startsWith(root.toPath().toAbsolutePath()); + } + + @Override + public Module getModule(File cwd, String name) { + var realName = getRealName(cwd, name); + if (realName == null) return null; + var path = Path.of(realName + ".js").normalize(); + + try { + var res = PolyfillEngine.streamToString(new FileInputStream(path.toFile())); + return new Module(realName, path.toString(), res); + } + catch (IOException e) { + return null; + } + } + @Override + public String getRealName(File cwd, String name) { + var path = Path.of(".", Path.of(cwd.toString(), name).normalize().toString()); + var fileName = path.getFileName().toString(); + if (fileName == null) return null; + if (!fileName.equals("index") && path.toFile().isDirectory()) return getRealName(cwd, name + "/index"); + path = Path.of(path.toString() + ".js"); + if (!allowOutside && !checkInside(path)) return null; + if (!path.toFile().isFile() || !path.toFile().canRead()) return null; + var res = path.toString().replace('\\', '/'); + var i = res.lastIndexOf('.'); + return res.substring(0, i); + } + + public FileModuleProvider(File root, boolean allowOutside) { + this.root = root.toPath().normalize().toFile(); + this.allowOutside = allowOutside; + } +} diff --git a/src/me/topchetoeu/jscript/engine/modules/Module.java b/dead-code/modules/Module.java similarity index 97% rename from src/me/topchetoeu/jscript/engine/modules/Module.java rename to dead-code/modules/Module.java index 44bd681..f6883d3 100644 --- a/src/me/topchetoeu/jscript/engine/modules/Module.java +++ b/dead-code/modules/Module.java @@ -1,57 +1,57 @@ -package me.topchetoeu.jscript.engine.modules; - -import java.io.File; - -import me.topchetoeu.jscript.engine.CallContext; -import me.topchetoeu.jscript.engine.CallContext.DataKey; -import me.topchetoeu.jscript.engine.scope.Variable; -import me.topchetoeu.jscript.engine.values.ObjectValue; -import me.topchetoeu.jscript.interop.NativeGetter; -import me.topchetoeu.jscript.interop.NativeSetter; - -public class Module { - public class ExportsVariable implements Variable { - @Override - public boolean readonly() { return false; } - @Override - public Object get(CallContext ctx) { return exports; } - @Override - public void set(CallContext ctx, Object val) { exports = val; } - } - - public static DataKey KEY = new DataKey<>(); - - public final String filename; - public final String source; - public final String name; - private Object exports = new ObjectValue(); - private boolean executing = false; - - @NativeGetter("name") - public String name() { return name; } - @NativeGetter("exports") - public Object exports() { return exports; } - @NativeSetter("exports") - public void setExports(Object val) { exports = val; } - - public void execute(CallContext ctx) throws InterruptedException { - if (executing) return; - - executing = true; - var scope = ctx.engine().global().globalChild(); - scope.define(null, "module", true, this); - scope.define("exports", new ExportsVariable()); - - var parent = new File(filename).getParentFile(); - if (parent == null) parent = new File("."); - - ctx.engine().compile(scope, filename, source).call(ctx.copy().setData(KEY, this), null); - executing = false; - } - - public Module(String name, String filename, String source) { - this.name = name; - this.filename = filename; - this.source = source; - } -} +package me.topchetoeu.jscript.engine.modules; + +import java.io.File; + +import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.CallContext.DataKey; +import me.topchetoeu.jscript.engine.scope.Variable; +import me.topchetoeu.jscript.engine.values.ObjectValue; +import me.topchetoeu.jscript.interop.NativeGetter; +import me.topchetoeu.jscript.interop.NativeSetter; + +public class Module { + public class ExportsVariable implements Variable { + @Override + public boolean readonly() { return false; } + @Override + public Object get(CallContext ctx) { return exports; } + @Override + public void set(CallContext ctx, Object val) { exports = val; } + } + + public static DataKey KEY = new DataKey<>(); + + public final String filename; + public final String source; + public final String name; + private Object exports = new ObjectValue(); + private boolean executing = false; + + @NativeGetter("name") + public String name() { return name; } + @NativeGetter("exports") + public Object exports() { return exports; } + @NativeSetter("exports") + public void setExports(Object val) { exports = val; } + + public void execute(CallContext ctx) throws InterruptedException { + if (executing) return; + + executing = true; + var scope = ctx.engine().global().globalChild(); + scope.define(null, "module", true, this); + scope.define("exports", new ExportsVariable()); + + var parent = new File(filename).getParentFile(); + if (parent == null) parent = new File("."); + + ctx.engine().compile(scope, filename, source).call(ctx.copy().setData(KEY, this), null); + executing = false; + } + + public Module(String name, String filename, String source) { + this.name = name; + this.filename = filename; + this.source = source; + } +} diff --git a/src/me/topchetoeu/jscript/engine/modules/ModuleManager.java b/dead-code/modules/ModuleManager.java similarity index 96% rename from src/me/topchetoeu/jscript/engine/modules/ModuleManager.java rename to dead-code/modules/ModuleManager.java index 4def5e1..ab5c20a 100644 --- a/src/me/topchetoeu/jscript/engine/modules/ModuleManager.java +++ b/dead-code/modules/ModuleManager.java @@ -1,80 +1,80 @@ -package me.topchetoeu.jscript.engine.modules; - -import java.io.File; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -import me.topchetoeu.jscript.engine.CallContext; - -public class ModuleManager { - private final List providers = new ArrayList<>(); - private final HashMap cache = new HashMap<>(); - public final FileModuleProvider files; - - public void addProvider(ModuleProvider provider) { - this.providers.add(provider); - } - - public boolean isCached(File cwd, String name) { - name = name.replace("\\", "/"); - - // Absolute paths are forbidden - if (name.startsWith("/")) return false; - // Look for files if we have a relative path - if (name.startsWith("../") || name.startsWith("./")) { - var realName = files.getRealName(cwd, name); - if (cache.containsKey(realName)) return true; - else return false; - } - - for (var provider : providers) { - var realName = provider.getRealName(cwd, name); - if (realName == null) continue; - if (cache.containsKey(realName)) return true; - } - - return false; - } - public Module tryLoad(CallContext ctx, String name) throws InterruptedException { - name = name.replace('\\', '/'); - - var pcwd = Path.of("."); - - if (ctx.hasData(Module.KEY)) { - pcwd = Path.of(((Module)ctx.getData(Module.KEY)).filename).getParent(); - if (pcwd == null) pcwd = Path.of("."); - } - - - var cwd = pcwd.toFile(); - - if (name.startsWith("/")) return null; - if (name.startsWith("../") || name.startsWith("./")) { - var realName = files.getRealName(cwd, name); - if (realName == null) return null; - if (cache.containsKey(realName)) return cache.get(realName); - var mod = files.getModule(cwd, name); - cache.put(mod.name(), mod); - mod.execute(ctx); - return mod; - } - - for (var provider : providers) { - var realName = provider.getRealName(cwd, name); - if (realName == null) continue; - if (cache.containsKey(realName)) return cache.get(realName); - var mod = provider.getModule(cwd, name); - cache.put(mod.name(), mod); - mod.execute(ctx); - return mod; - } - - return null; - } - - public ModuleManager(File root) { - files = new FileModuleProvider(root, false); - } -} +package me.topchetoeu.jscript.engine.modules; + +import java.io.File; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import me.topchetoeu.jscript.engine.CallContext; + +public class ModuleManager { + private final List providers = new ArrayList<>(); + private final HashMap cache = new HashMap<>(); + public final FileModuleProvider files; + + public void addProvider(ModuleProvider provider) { + this.providers.add(provider); + } + + public boolean isCached(File cwd, String name) { + name = name.replace("\\", "/"); + + // Absolute paths are forbidden + if (name.startsWith("/")) return false; + // Look for files if we have a relative path + if (name.startsWith("../") || name.startsWith("./")) { + var realName = files.getRealName(cwd, name); + if (cache.containsKey(realName)) return true; + else return false; + } + + for (var provider : providers) { + var realName = provider.getRealName(cwd, name); + if (realName == null) continue; + if (cache.containsKey(realName)) return true; + } + + return false; + } + public Module tryLoad(CallContext ctx, String name) throws InterruptedException { + name = name.replace('\\', '/'); + + var pcwd = Path.of("."); + + if (ctx.hasData(Module.KEY)) { + pcwd = Path.of(((Module)ctx.getData(Module.KEY)).filename).getParent(); + if (pcwd == null) pcwd = Path.of("."); + } + + + var cwd = pcwd.toFile(); + + if (name.startsWith("/")) return null; + if (name.startsWith("../") || name.startsWith("./")) { + var realName = files.getRealName(cwd, name); + if (realName == null) return null; + if (cache.containsKey(realName)) return cache.get(realName); + var mod = files.getModule(cwd, name); + cache.put(mod.name(), mod); + mod.execute(ctx); + return mod; + } + + for (var provider : providers) { + var realName = provider.getRealName(cwd, name); + if (realName == null) continue; + if (cache.containsKey(realName)) return cache.get(realName); + var mod = provider.getModule(cwd, name); + cache.put(mod.name(), mod); + mod.execute(ctx); + return mod; + } + + return null; + } + + public ModuleManager(File root) { + files = new FileModuleProvider(root, false); + } +} diff --git a/src/me/topchetoeu/jscript/engine/modules/ModuleProvider.java b/dead-code/modules/ModuleProvider.java similarity index 97% rename from src/me/topchetoeu/jscript/engine/modules/ModuleProvider.java rename to dead-code/modules/ModuleProvider.java index ad122c1..3305b15 100644 --- a/src/me/topchetoeu/jscript/engine/modules/ModuleProvider.java +++ b/dead-code/modules/ModuleProvider.java @@ -1,9 +1,9 @@ -package me.topchetoeu.jscript.engine.modules; - -import java.io.File; - -public interface ModuleProvider { - Module getModule(File cwd, String name); - String getRealName(File cwd, String name); - default boolean hasModule(File cwd, String name) { return getRealName(cwd, name) != null; } +package me.topchetoeu.jscript.engine.modules; + +import java.io.File; + +public interface ModuleProvider { + Module getModule(File cwd, String name); + String getRealName(File cwd, String name); + default boolean hasModule(File cwd, String name) { return getRealName(cwd, name) != null; } } \ No newline at end of file diff --git a/lib/core.ts b/lib/core.ts index 75d0ff8..f28ad57 100644 --- a/lib/core.ts +++ b/lib/core.ts @@ -1,68 +1,64 @@ -var env: Environment; +interface Environment { + global: typeof globalThis & Record; + proto(name: string): object; + setProto(name: string, val: object): void; +} +interface Internals { + markSpecial(...funcs: Function[]): void; + getEnv(func: Function): Environment | undefined; + setEnv(func: T, env: Environment): T; + apply(func: Function, thisArg: any, args: any[]): any; + delay(timeout: number, callback: Function): () => void; + pushMessage(micro: boolean, func: Function, thisArg: any, args: any[]): void; -// @ts-ignore -return (_env: Environment) => { - env = _env; - env.global.assert = (cond, msg) => { - try { - if (!cond()) throw 'condition not satisfied'; - log('Passed ' + msg); - return true; - } - catch (e) { - log('Failed ' + msg + ' because of: ' + e); - return false; - } - } - try { - run('values/object'); - run('values/symbol'); - run('values/function'); - run('values/errors'); - run('values/string'); - run('values/number'); - run('values/boolean'); - run('values/array'); + strlen(val: string): number; + char(val: string): number; + stringFromStrings(arr: string[]): string; + stringFromChars(arr: number[]): string; + symbol(name?: string): symbol; + symbolToString(sym: symbol): string; - env.internals.special(Object, Function, Error, Array); + isArray(obj: any): boolean; + generator(func: (_yield: (val: T) => unknown) => (...args: any[]) => unknown): GeneratorFunction; + defineField(obj: object, key: any, val: any, writable: boolean, enumerable: boolean, configurable: boolean): boolean; + defineProp(obj: object, key: any, get: Function | undefined, set: Function | undefined, enumerable: boolean, configurable: boolean): boolean; + keys(obj: object, onlyString: boolean): any[]; + ownProp(obj: any, key: string): PropertyDescriptor; + ownPropKeys(obj: any): any[]; + lock(obj: object, type: 'ext' | 'seal' | 'freeze'): void; + extensible(obj: object): boolean; - env.global.setTimeout = (func, delay, ...args) => { - if (typeof func !== 'function') throw new TypeError("func must be a function."); - delay = (delay ?? 0) - 0; - return env.internals.setTimeout(() => func(...args), delay) - }; - env.global.setInterval = (func, delay, ...args) => { - if (typeof func !== 'function') throw new TypeError("func must be a function."); - delay = (delay ?? 0) - 0; - return env.internals.setInterval(() => func(...args), delay) - }; - - env.global.clearTimeout = (id) => { - id = id | 0; - env.internals.clearTimeout(id); - }; - env.global.clearInterval = (id) => { - id = id | 0; - env.internals.clearInterval(id); - }; + sort(arr: any[], comaprator: (a: any, b: any) => number): void; +} + +var env: Environment = arguments[0], internals: Internals = arguments[1]; + +try { + run('values/object'); + run('values/symbol'); + run('values/function'); + run('values/errors'); + run('values/string'); + run('values/number'); + run('values/boolean'); + run('values/array'); + run('promise'); + run('map'); + run('set'); + run('regex'); + run('timeout'); - run('promise'); - run('map'); - run('set'); - run('regex'); - run('require'); - - log('Loaded polyfills!'); + log('Loaded polyfills!'); +} +catch (e: any) { + let err = 'Uncaught error while loading polyfills: '; + + if (typeof Error !== 'undefined' && e instanceof Error && e.toString !== {}.toString) err += e; + else if ('message' in e) { + if ('name' in e) err += e.name + ": " + e.message; + else err += 'Error: ' + e.message; } - catch (e: any) { - if (!_env.captureErr) throw e; - var err = 'Uncaught error while loading polyfills: '; - if (typeof Error !== 'undefined' && e instanceof Error && e.toString !== {}.toString) err += e; - else if ('message' in e) { - if ('name' in e) err += e.name + ": " + e.message; - else err += 'Error: ' + e.message; - } - else err += e; - log(e); - } -}; \ No newline at end of file + else err += e; + + log(e); +} \ No newline at end of file diff --git a/lib/lib.d.ts b/lib/lib.d.ts index e1c5c7d..4e83de2 100644 --- a/lib/lib.d.ts +++ b/lib/lib.d.ts @@ -46,8 +46,8 @@ type IteratorReturnResult = type IteratorResult = IteratorYieldResult | IteratorReturnResult; interface Thenable { - then(this: Promise, onFulfilled: PromiseThenFunc, onRejected?: PromiseRejectFunc): Promise>; - then(this: Promise, onFulfilled: undefined, onRejected?: PromiseRejectFunc): Promise; + then(onFulfilled: PromiseThenFunc, onRejected?: PromiseRejectFunc): Promise>; + then(onFulfilled: undefined, onRejected?: PromiseRejectFunc): Promise; } interface RegExpResultIndices extends Array<[number, number]> { @@ -100,7 +100,6 @@ interface IterableIterator extends Iterator { } interface AsyncIterator { - // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places. next(...args: [] | [TNext]): Promise>; return?(value?: TReturn | Thenable): Promise>; throw?(e?: any): Promise>; @@ -112,65 +111,29 @@ interface AsyncIterableIterator extends AsyncIterator { [Symbol.asyncIterator](): AsyncIterableIterator; } -interface Generator extends Iterator { +interface Generator extends Iterator { [Symbol.iterator](): Generator; - return(value?: TReturn): IteratorResult; - throw(e?: any): IteratorResult; + return(value: TReturn): IteratorResult; + throw(e: any): IteratorResult; } interface GeneratorFunction { - /** - * Creates a new Generator object. - * @param args A list of arguments the function accepts. - */ new (...args: any[]): Generator; - /** - * Creates a new Generator object. - * @param args A list of arguments the function accepts. - */ (...args: any[]): Generator; - /** - * The length of the arguments. - */ readonly length: number; - /** - * Returns the name of the function. - */ readonly name: string; - /** - * A reference to the prototype. - */ readonly prototype: Generator; } -interface AsyncGenerator extends AsyncIterator { - // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places. - next(...args: [] | [TNext]): Promise>; +interface AsyncGenerator extends AsyncIterator { return(value: TReturn | Thenable): Promise>; throw(e: any): Promise>; [Symbol.asyncIterator](): AsyncGenerator; } interface AsyncGeneratorFunction { - /** - * Creates a new AsyncGenerator object. - * @param args A list of arguments the function accepts. - */ new (...args: any[]): AsyncGenerator; - /** - * Creates a new AsyncGenerator object. - * @param args A list of arguments the function accepts. - */ (...args: any[]): AsyncGenerator; - /** - * The length of the arguments. - */ readonly length: number; - /** - * Returns the name of the function. - */ readonly name: string; - /** - * A reference to the prototype. - */ readonly prototype: AsyncGenerator; } @@ -225,7 +188,6 @@ interface MathObject { interface Array extends IterableIterator { [i: number]: T; - constructor: ArrayConstructor; length: number; toString(): string; @@ -283,7 +245,6 @@ interface ArrayConstructor { interface Boolean { valueOf(): boolean; - constructor: BooleanConstructor; } interface BooleanConstructor { (val: any): boolean; @@ -292,10 +253,10 @@ interface BooleanConstructor { } interface Error { - constructor: ErrorConstructor; name: string; message: string; stack: string[]; + toString(): string; } interface ErrorConstructor { (msg?: any): Error; @@ -309,7 +270,6 @@ interface TypeErrorConstructor extends ErrorConstructor { prototype: Error; } interface TypeError extends Error { - constructor: TypeErrorConstructor; name: 'TypeError'; } @@ -319,7 +279,6 @@ interface RangeErrorConstructor extends ErrorConstructor { prototype: Error; } interface RangeError extends Error { - constructor: RangeErrorConstructor; name: 'RangeError'; } @@ -329,7 +288,6 @@ interface SyntaxErrorConstructor extends ErrorConstructor { prototype: Error; } interface SyntaxError extends Error { - constructor: SyntaxErrorConstructor; name: 'SyntaxError'; } @@ -341,7 +299,6 @@ interface Function { toString(): string; prototype: any; - constructor: FunctionConstructor; readonly length: number; name: string; } @@ -375,7 +332,6 @@ interface FunctionConstructor extends Function { interface Number { toString(): string; valueOf(): number; - constructor: NumberConstructor; } interface NumberConstructor { (val: any): number; @@ -477,8 +433,6 @@ interface String { includes(term: string, start?: number): boolean; length: number; - - constructor: StringConstructor; } interface StringConstructor { (val: any): string; @@ -491,7 +445,6 @@ interface StringConstructor { interface Symbol { valueOf(): symbol; - constructor: SymbolConstructor; } interface SymbolConstructor { (val?: any): symbol; @@ -511,7 +464,6 @@ interface SymbolConstructor { } interface Promise extends Thenable { - constructor: PromiseConstructor; catch(func: PromiseRejectFunc): Promise; finally(func: () => void): Promise; } @@ -522,7 +474,8 @@ interface PromiseConstructor { resolve(val: T): Promise>; reject(val: any): Promise; - any(promises: (Promise|T)[]): Promise; + isAwaitable(val: unknown): val is Thenable; + any(promises: T[]): Promise>; race(promises: (Promise|T)[]): Promise; all(promises: T): Promise<{ [Key in keyof T]: Awaited }>; allSettled(...promises: T): Promise<[...{ [P in keyof T]: PromiseResult>}]>; @@ -544,7 +497,6 @@ declare var parseInt: typeof Number.parseInt; declare var parseFloat: typeof Number.parseFloat; declare function log(...vals: any[]): void; -declare function assert(condition: () => unknown, message?: string): boolean; declare var Array: ArrayConstructor; declare var Boolean: BooleanConstructor; diff --git a/lib/map.ts b/lib/map.ts index 2c45e0c..f318687 100644 --- a/lib/map.ts +++ b/lib/map.ts @@ -1,29 +1,92 @@ define("map", () => { - var Map = env.global.Map = env.internals.Map; + const syms = { values: internals.symbol('Map.values') } as { readonly values: unique symbol }; - Map.prototype[Symbol.iterator] = function() { - return this.entries(); - }; + class Map { + [syms.values]: any = {}; - var entries = Map.prototype.entries; - var keys = Map.prototype.keys; - var values = Map.prototype.values; + public [Symbol.iterator](): IterableIterator<[KeyT, ValueT]> { + return this.entries(); + } - Map.prototype.entries = function() { - var it = entries.call(this); - it[Symbol.iterator] = () => it; - return it; - }; - Map.prototype.keys = function() { - var it = keys.call(this); - it[Symbol.iterator] = () => it; - return it; - }; - Map.prototype.values = function() { - var it = values.call(this); - it[Symbol.iterator] = () => it; - return it; - }; + public clear() { + this[syms.values] = {}; + } + public delete(key: KeyT) { + if ((key as any) in this[syms.values]) { + delete this[syms.values]; + return true; + } + else return false; + } + + public entries(): IterableIterator<[KeyT, ValueT]> { + const keys = internals.ownPropKeys(this[syms.values]); + let i = 0; + + return { + next: () => { + if (i >= keys.length) return { done: true }; + else return { done: false, value: [ keys[i], this[syms.values][keys[i++]] ] } + }, + [Symbol.iterator]() { return this; } + } + } + public keys(): IterableIterator { + const keys = internals.ownPropKeys(this[syms.values]); + let i = 0; + + return { + next: () => { + if (i >= keys.length) return { done: true }; + else return { done: false, value: keys[i] } + }, + [Symbol.iterator]() { return this; } + } + } + public values(): IterableIterator { + const keys = internals.ownPropKeys(this[syms.values]); + let i = 0; + + return { + next: () => { + if (i >= keys.length) return { done: true }; + else return { done: false, value: this[syms.values][keys[i++]] } + }, + [Symbol.iterator]() { return this; } + } + } + + public get(key: KeyT) { + return this[syms.values][key]; + } + public set(key: KeyT, val: ValueT) { + this[syms.values][key] = val; + return this; + } + public has(key: KeyT) { + return (key as any) in this[syms.values][key]; + } + + public get size() { + return internals.ownPropKeys(this[syms.values]).length; + } + + public forEach(func: (key: KeyT, val: ValueT, map: Map) => void, thisArg?: any) { + const keys = internals.ownPropKeys(this[syms.values]); + + for (let i = 0; i < keys.length; i++) { + func(keys[i], this[syms.values][keys[i]], this); + } + } + + public constructor(iterable: Iterable<[KeyT, ValueT]>) { + const it = iterable[Symbol.iterator](); + + for (let el = it.next(); !el.done; el = it.next()) { + this[syms.values][el.value[0]] = el.value[1]; + } + } + } env.global.Map = Map; }); diff --git a/lib/modules.ts b/lib/modules.ts index 8eb68a0..c15e496 100644 --- a/lib/modules.ts +++ b/lib/modules.ts @@ -5,7 +5,8 @@ var { define, run } = (() => { modules[name] = func; } function run(name: string) { - return modules[name](); + if (typeof modules[name] === 'function') return modules[name](); + else throw "The module '" + name + "' doesn't exist."; } return { define, run }; diff --git a/lib/promise.ts b/lib/promise.ts index 9eb1404..903c7c2 100644 --- a/lib/promise.ts +++ b/lib/promise.ts @@ -1,3 +1,203 @@ define("promise", () => { - (env.global.Promise = env.internals.Promise).prototype[Symbol.typeName] = 'Promise'; + const syms = { + callbacks: internals.symbol('Promise.callbacks'), + state: internals.symbol('Promise.state'), + value: internals.symbol('Promise.value'), + handled: internals.symbol('Promise.handled'), + } as { + readonly callbacks: unique symbol, + readonly state: unique symbol, + readonly value: unique symbol, + readonly handled: unique symbol, + } + + type Callback = [ PromiseFulfillFunc, PromiseRejectFunc ]; + enum State { + Pending, + Fulfilled, + Rejected, + } + + function isAwaitable(val: unknown): val is Thenable { + return ( + typeof val === 'object' && + val !== null && + 'then' in val && + typeof val.then === 'function' + ); + } + function resolve(promise: Promise, v: any, state: State) { + if (promise[syms.state] === State.Pending) { + if (typeof v === 'object' && v !== null && 'then' in v && typeof v.then === 'function') { + v.then( + (res: any) => resolve(promise, res, state), + (res: any) => resolve(promise, res, State.Rejected) + ); + return; + } + promise[syms.value] = v; + promise[syms.state] = state; + + for (let i = 0; i < promise[syms.callbacks]!.length; i++) { + promise[syms.handled] = true; + promise[syms.callbacks]![i][state - 1](v); + } + + promise[syms.callbacks] = undefined; + + internals.pushMessage(true, internals.setEnv(() => { + if (!promise[syms.handled] && state === State.Rejected) { + log('Uncaught (in promise) ' + promise[syms.value]); + } + }, env), undefined, []); + } + } + + class Promise { + public static isAwaitable(val: unknown): val is Thenable { + return isAwaitable(val); + } + + public static resolve(val: T): Promise> { + return new Promise(res => res(val as any)); + } + public static reject(val: T): Promise> { + return new Promise((_, rej) => rej(val as any)); + } + + public static race(vals: T[]): Promise> { + if (typeof vals.length !== 'number') throw new TypeError('vals must be an array. Note that Promise.race is not variadic.'); + return new Promise((res, rej) => { + for (let i = 0; i < vals.length; i++) { + const val = vals[i]; + if (this.isAwaitable(val)) val.then(res, rej); + else res(val as any); + } + }); + } + public static any(vals: T[]): Promise> { + if (typeof vals.length !== 'number') throw new TypeError('vals must be an array. Note that Promise.any is not variadic.'); + return new Promise((res, rej) => { + let n = 0; + + for (let i = 0; i < vals.length; i++) { + const val = vals[i]; + if (this.isAwaitable(val)) val.then(res, (err) => { + n++; + if (n === vals.length) throw Error('No promise resolved.'); + }); + else res(val as any); + } + + if (vals.length === 0) throw Error('No promise resolved.'); + }); + } + public static all(vals: any[]): Promise { + if (typeof vals.length !== 'number') throw new TypeError('vals must be an array. Note that Promise.all is not variadic.'); + return new Promise((res, rej) => { + const result: any[] = []; + let n = 0; + + for (let i = 0; i < vals.length; i++) { + const val = vals[i]; + if (this.isAwaitable(val)) val.then( + val => { + n++; + result[i] = val; + if (n === vals.length) res(result); + }, + rej + ); + else { + n++; + result[i] = val; + } + } + + if (vals.length === n) res(result); + }); + } + public static allSettled(vals: any[]): Promise { + if (typeof vals.length !== 'number') throw new TypeError('vals must be an array. Note that Promise.allSettled is not variadic.'); + return new Promise((res, rej) => { + const result: any[] = []; + let n = 0; + + for (let i = 0; i < vals.length; i++) { + const value = vals[i]; + if (this.isAwaitable(value)) value.then( + value => { + n++; + result[i] = { status: 'fulfilled', value }; + if (n === vals.length) res(result); + }, + reason => { + n++; + result[i] = { status: 'rejected', reason }; + if (n === vals.length) res(result); + }, + ); + else { + n++; + result[i] = { status: 'fulfilled', value }; + } + } + + if (vals.length === n) res(result); + }); + } + + [syms.callbacks]?: Callback[] = []; + [syms.handled] = false; + [syms.state] = State.Pending; + [syms.value]?: T | unknown; + + public then(onFulfil?: PromiseFulfillFunc, onReject?: PromiseRejectFunc) { + return new Promise((resolve, reject) => { + onFulfil ??= v => v; + onReject ??= v => v; + + const callback = (func: (val: any) => any) => (v: any) => { + try { resolve(func(v)); } + catch (e) { reject(e); } + } + switch (this[syms.state]) { + case State.Pending: + this[syms.callbacks]![this[syms.callbacks]!.length] = [callback(onFulfil), callback(onReject)]; + break; + case State.Fulfilled: + this[syms.handled] = true; + callback(onFulfil)(this[syms.value]); + break; + case State.Rejected: + this[syms.handled] = true; + callback(onReject)(this[syms.value]); + break; + } + }) + } + public catch(func: PromiseRejectFunc) { + return this.then(undefined, func); + } + public finally(func: () => void) { + return this.then( + v => { + func(); + return v; + }, + v => { + func(); + throw v; + } + ); + } + + public constructor(func: PromiseFunc) { + internals.pushMessage(true, func, undefined, [ + ((v) => resolve(this, v, State.Fulfilled)) as PromiseFulfillFunc, + ((err) => resolve(this, err, State.Rejected)) as PromiseRejectFunc + ]); + } + } + env.global.Promise = Promise as any; }); diff --git a/lib/regex.ts b/lib/regex.ts index cb1217f..ed5ce84 100644 --- a/lib/regex.ts +++ b/lib/regex.ts @@ -1,143 +1,143 @@ define("regex", () => { - var RegExp = env.global.RegExp = env.internals.RegExp; + // var RegExp = env.global.RegExp = env.internals.RegExp; - setProps(RegExp.prototype as RegExp, env, { - [Symbol.typeName]: 'RegExp', + // setProps(RegExp.prototype as RegExp, env, { + // [Symbol.typeName]: 'RegExp', - test(val) { - return !!this.exec(val); - }, - toString() { - return '/' + this.source + '/' + this.flags; - }, + // test(val) { + // return !!this.exec(val); + // }, + // toString() { + // return '/' + this.source + '/' + this.flags; + // }, - [Symbol.match](target) { - if (this.global) { - const res: string[] = []; - let val; - while (val = this.exec(target)) { - res.push(val[0]); - } - this.lastIndex = 0; - return res; - } - else { - const res = this.exec(target); - if (!this.sticky) this.lastIndex = 0; - return res; - } - }, - [Symbol.matchAll](target) { - let pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp; + // [Symbol.match](target) { + // if (this.global) { + // const res: string[] = []; + // let val; + // while (val = this.exec(target)) { + // res.push(val[0]); + // } + // this.lastIndex = 0; + // return res; + // } + // else { + // const res = this.exec(target); + // if (!this.sticky) this.lastIndex = 0; + // return res; + // } + // }, + // [Symbol.matchAll](target) { + // let pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp; - return { - next: (): IteratorResult => { - const val = pattern?.exec(target); + // return { + // next: (): IteratorResult => { + // const val = pattern?.exec(target); - if (val === null || val === undefined) { - pattern = undefined; - return { done: true }; - } - else return { value: val }; - }, - [Symbol.iterator]() { return this; } - } - }, - [Symbol.split](target, limit, sensible) { - const pattern = new this.constructor(this, this.flags + "g") as RegExp; - let match: RegExpResult | null; - let lastEnd = 0; - const res: string[] = []; + // if (val === null || val === undefined) { + // pattern = undefined; + // return { done: true }; + // } + // else return { value: val }; + // }, + // [Symbol.iterator]() { return this; } + // } + // }, + // [Symbol.split](target, limit, sensible) { + // const pattern = new this.constructor(this, this.flags + "g") as RegExp; + // let match: RegExpResult | null; + // let lastEnd = 0; + // const res: string[] = []; - while ((match = pattern.exec(target)) !== null) { - let added: string[] = []; + // while ((match = pattern.exec(target)) !== null) { + // let added: string[] = []; - if (match.index >= target.length) break; + // if (match.index >= target.length) break; - if (match[0].length === 0) { - added = [ target.substring(lastEnd, pattern.lastIndex), ]; - if (pattern.lastIndex < target.length) added.push(...match.slice(1)); - } - else if (match.index - lastEnd > 0) { - added = [ target.substring(lastEnd, match.index), ...match.slice(1) ]; - } - else { - for (let i = 1; i < match.length; i++) { - res[res.length - match.length + i] = match[i]; - } - } + // if (match[0].length === 0) { + // added = [ target.substring(lastEnd, pattern.lastIndex), ]; + // if (pattern.lastIndex < target.length) added.push(...match.slice(1)); + // } + // else if (match.index - lastEnd > 0) { + // added = [ target.substring(lastEnd, match.index), ...match.slice(1) ]; + // } + // else { + // for (let i = 1; i < match.length; i++) { + // res[res.length - match.length + i] = match[i]; + // } + // } - if (sensible) { - if (limit !== undefined && res.length + added.length >= limit) break; - else res.push(...added); - } - else { - for (let i = 0; i < added.length; i++) { - if (limit !== undefined && res.length >= limit) return res; - else res.push(added[i]); - } - } + // if (sensible) { + // if (limit !== undefined && res.length + added.length >= limit) break; + // else res.push(...added); + // } + // else { + // for (let i = 0; i < added.length; i++) { + // if (limit !== undefined && res.length >= limit) return res; + // else res.push(added[i]); + // } + // } - lastEnd = pattern.lastIndex; - } + // lastEnd = pattern.lastIndex; + // } - if (lastEnd < target.length) { - res.push(target.substring(lastEnd)); - } + // if (lastEnd < target.length) { + // res.push(target.substring(lastEnd)); + // } - return res; - }, - [Symbol.replace](target, replacement) { - const pattern = new this.constructor(this, this.flags + "d") as RegExp; - let match: RegExpResult | null; - let lastEnd = 0; - const res: string[] = []; + // return res; + // }, + // [Symbol.replace](target, replacement) { + // const pattern = new this.constructor(this, this.flags + "d") as RegExp; + // let match: RegExpResult | null; + // let lastEnd = 0; + // const res: string[] = []; - // log(pattern.toString()); + // // log(pattern.toString()); - while ((match = pattern.exec(target)) !== null) { - const indices = match.indices![0]; - res.push(target.substring(lastEnd, indices[0])); - if (replacement instanceof Function) { - res.push(replacement(target.substring(indices[0], indices[1]), ...match.slice(1), indices[0], target)); - } - else { - res.push(replacement); - } - lastEnd = indices[1]; - if (!pattern.global) break; - } + // while ((match = pattern.exec(target)) !== null) { + // const indices = match.indices![0]; + // res.push(target.substring(lastEnd, indices[0])); + // if (replacement instanceof Function) { + // res.push(replacement(target.substring(indices[0], indices[1]), ...match.slice(1), indices[0], target)); + // } + // else { + // res.push(replacement); + // } + // lastEnd = indices[1]; + // if (!pattern.global) break; + // } - if (lastEnd < target.length) { - res.push(target.substring(lastEnd)); - } + // if (lastEnd < target.length) { + // res.push(target.substring(lastEnd)); + // } - return res.join(''); - }, - [Symbol.search](target, reverse, start) { - const pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp; + // return res.join(''); + // }, + // [Symbol.search](target, reverse, start) { + // const pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp; - if (!reverse) { - pattern.lastIndex = (start as any) | 0; - const res = pattern.exec(target); - if (res) return res.index; - else return -1; - } - else { - start ??= target.length; - start |= 0; - let res: RegExpResult | null = null; + // if (!reverse) { + // pattern.lastIndex = (start as any) | 0; + // const res = pattern.exec(target); + // if (res) return res.index; + // else return -1; + // } + // else { + // start ??= target.length; + // start |= 0; + // let res: RegExpResult | null = null; - while (true) { - const tmp = pattern.exec(target); - if (tmp === null || tmp.index > start) break; - res = tmp; - } + // while (true) { + // const tmp = pattern.exec(target); + // if (tmp === null || tmp.index > start) break; + // res = tmp; + // } - if (res && res.index <= start) return res.index; - else return -1; - } - }, - }); + // if (res && res.index <= start) return res.index; + // else return -1; + // } + // }, + // }); }); \ No newline at end of file diff --git a/lib/set.ts b/lib/set.ts index 3a21671..7bf9a19 100644 --- a/lib/set.ts +++ b/lib/set.ts @@ -1,26 +1,80 @@ define("set", () => { - var Set = env.global.Set = env.internals.Set; - Set.prototype[Symbol.iterator] = function() { - return this.values(); - }; + const syms = { values: internals.symbol('Map.values') } as { readonly values: unique symbol }; - var entries = Set.prototype.entries; - var keys = Set.prototype.keys; - var values = Set.prototype.values; + class Set { + [syms.values]: any = {}; - Set.prototype.entries = function() { - var it = entries.call(this); - it[Symbol.iterator] = () => it; - return it; - }; - Set.prototype.keys = function() { - var it = keys.call(this); - it[Symbol.iterator] = () => it; - return it; - }; - Set.prototype.values = function() { - var it = values.call(this); - it[Symbol.iterator] = () => it; - return it; - }; + public [Symbol.iterator](): IterableIterator<[T, T]> { + return this.entries(); + } + + public clear() { + this[syms.values] = {}; + } + public delete(key: T) { + if ((key as any) in this[syms.values]) { + delete this[syms.values]; + return true; + } + else return false; + } + + public entries(): IterableIterator<[T, T]> { + const keys = internals.ownPropKeys(this[syms.values]); + let i = 0; + + return { + next: () => { + if (i >= keys.length) return { done: true }; + else return { done: false, value: [ keys[i], keys[i] ] } + }, + [Symbol.iterator]() { return this; } + } + } + public keys(): IterableIterator { + const keys = internals.ownPropKeys(this[syms.values]); + let i = 0; + + return { + next: () => { + if (i >= keys.length) return { done: true }; + else return { done: false, value: keys[i] } + }, + [Symbol.iterator]() { return this; } + } + } + public values(): IterableIterator { + return this.keys(); + } + + public add(val: T) { + this[syms.values][val] = undefined; + return this; + } + public has(key: T) { + return (key as any) in this[syms.values][key]; + } + + public get size() { + return internals.ownPropKeys(this[syms.values]).length; + } + + public forEach(func: (key: T, val: T, map: Set) => void, thisArg?: any) { + const keys = internals.ownPropKeys(this[syms.values]); + + for (let i = 0; i < keys.length; i++) { + func(keys[i], this[syms.values][keys[i]], this); + } + } + + public constructor(iterable: Iterable) { + const it = iterable[Symbol.iterator](); + + for (let el = it.next(); !el.done; el = it.next()) { + this[syms.values][el.value] = undefined; + } + } + } + + env.global.Set = Set; }); diff --git a/lib/timeout.ts b/lib/timeout.ts new file mode 100644 index 0000000..8db82a0 --- /dev/null +++ b/lib/timeout.ts @@ -0,0 +1,38 @@ +define("timeout", () => { + const timeouts: Record void> = { }; + const intervals: Record void> = { }; + let timeoutI = 0, intervalI = 0; + + env.global.setTimeout = (func, delay, ...args) => { + if (typeof func !== 'function') throw new TypeError("func must be a function."); + delay = (delay ?? 0) - 0; + const cancelFunc = internals.delay(delay, () => internals.apply(func, undefined, args)); + timeouts[++timeoutI] = cancelFunc; + return timeoutI; + }; + env.global.setInterval = (func, delay, ...args) => { + if (typeof func !== 'function') throw new TypeError("func must be a function."); + delay = (delay ?? 0) - 0; + + const i = ++intervalI; + intervals[i] = internals.delay(delay, callback); + + return i; + + function callback() { + internals.apply(func, undefined, args); + intervals[i] = internals.delay(delay!, callback); + } + }; + + env.global.clearTimeout = (id) => { + const func = timeouts[id]; + if (func) func(); + timeouts[id] = undefined!; + }; + env.global.clearInterval = (id) => { + const func = intervals[id]; + if (func) func(); + intervals[id] = undefined!; + }; +}); \ No newline at end of file diff --git a/lib/tsconfig.json b/lib/tsconfig.json index 5172e2d..8c3e2af 100644 --- a/lib/tsconfig.json +++ b/lib/tsconfig.json @@ -15,6 +15,7 @@ "map.ts", "set.ts", "regex.ts", + "timeout.ts", "core.ts" ], "compilerOptions": { diff --git a/lib/utils.ts b/lib/utils.ts index 90fdba4..6fdf694 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,9 +1,3 @@ -interface Environment { - global: typeof globalThis & Record; - captureErr: boolean; - internals: any; -} - function setProps< TargetT extends object, DescT extends { @@ -11,11 +5,11 @@ function setProps< ((this: TargetT, ...args: ArgsT) => RetT) : TargetT[x] } ->(target: TargetT, env: Environment, desc: DescT) { - var props = env.internals.keys(desc, false); +>(target: TargetT, desc: DescT) { + var props = internals.keys(desc, false); for (var i = 0; i < props.length; i++) { var key = props[i]; - env.internals.defineField( + internals.defineField( target, key, (desc as any)[key], true, // writable false, // enumerable @@ -23,8 +17,8 @@ function setProps< ); } } -function setConstr(target: T, constr: ConstrT, env: Environment) { - env.internals.defineField( +function setConstr(target: object, constr: Function) { + internals.defineField( target, 'constructor', constr, true, // writable false, // enumerable diff --git a/lib/values/array.ts b/lib/values/array.ts index 43290b4..9eea6d7 100644 --- a/lib/values/array.ts +++ b/lib/values/array.ts @@ -11,20 +11,20 @@ define("values/array", () => { res[i] = arguments[i]; } } - + return res; } as ArrayConstructor; - - Array.prototype = ([] as any).__proto__ as Array; - setConstr(Array.prototype, Array, env); - + + env.setProto('array', Array.prototype); (Array.prototype as any)[Symbol.typeName] = "Array"; - - setProps(Array.prototype, env, { + setConstr(Array.prototype, Array); + + setProps(Array.prototype, { [Symbol.iterator]: function() { return this.values(); }, - + [Symbol.typeName]: "Array", + values() { var i = 0; @@ -67,7 +67,7 @@ define("values/array", () => { concat() { var res = [] as any[]; res.push.apply(res, this); - + for (var i = 0; i < arguments.length; i++) { var arg = arguments[i]; if (arg instanceof Array) { @@ -296,7 +296,7 @@ define("values/array", () => { if (typeof func !== 'function') throw new TypeError('Expected func to be undefined or a function.'); - env.internals.sort(this, func); + internals.sort(this, func); return this; }, splice(start, deleteCount, ...items) { @@ -328,8 +328,9 @@ define("values/array", () => { return res; } }); - - setProps(Array, env, { - isArray(val: any) { return env.internals.isArr(val); } + + setProps(Array, { + isArray(val: any) { return internals.isArray(val); } }); + internals.markSpecial(Array); }); \ No newline at end of file diff --git a/lib/values/boolean.ts b/lib/values/boolean.ts index a8868a4..c0d45a2 100644 --- a/lib/values/boolean.ts +++ b/lib/values/boolean.ts @@ -7,6 +7,6 @@ define("values/boolean", () => { else (this as any).value = val; } as BooleanConstructor; - Boolean.prototype = (false as any).__proto__ as Boolean; - setConstr(Boolean.prototype, Boolean, env); + env.setProto('bool', Boolean.prototype); + setConstr(Boolean.prototype, Boolean); }); diff --git a/lib/values/errors.ts b/lib/values/errors.ts index 95ad33f..4bfc7e5 100644 --- a/lib/values/errors.ts +++ b/lib/values/errors.ts @@ -8,36 +8,39 @@ define("values/errors", () => { stack: [] as string[], }, Error.prototype); } as ErrorConstructor; - - Error.prototype = env.internals.err ?? {}; - Error.prototype.name = 'Error'; - setConstr(Error.prototype, Error, env); - Error.prototype.toString = function() { - if (!(this instanceof Error)) return ''; - - if (this.message === '') return this.name; - else return this.name + ': ' + this.message; - }; + setConstr(Error.prototype, Error); + setProps(Error.prototype, { + name: 'Error', + toString: internals.setEnv(function(this: Error) { + if (!(this instanceof Error)) return ''; + + if (this.message === '') return this.name; + else return this.name + ': ' + this.message; + }, env) + }); + env.setProto('error', Error.prototype); + internals.markSpecial(Error); - function makeError(name: string, proto: any): T { - var err = function (msg: string) { + function makeError(name: string, proto: string): T1 { + function constr (msg: string) { var res = new Error(msg); - (res as any).__proto__ = err.prototype; + (res as any).__proto__ = constr.prototype; return res; - } as T; + } - err.prototype = proto; - err.prototype.name = name; - setConstr(err.prototype, err as ErrorConstructor, env); - (err.prototype as any).__proto__ = Error.prototype; - (err as any).__proto__ = Error; - env.internals.special(err); + (constr as any).__proto__ = Error; + (constr.prototype as any).__proto__ = env.proto('error'); + setConstr(constr.prototype, constr as ErrorConstructor); + setProps(constr.prototype, { name: name }); - return err; + internals.markSpecial(constr); + env.setProto(proto, constr.prototype); + + return constr as T1; } - - env.global.RangeError = makeError('RangeError', env.internals.range ?? {}); - env.global.TypeError = makeError('TypeError', env.internals.type ?? {}); - env.global.SyntaxError = makeError('SyntaxError', env.internals.syntax ?? {}); + + env.global.RangeError = makeError('RangeError', 'rangeErr'); + env.global.TypeError = makeError('TypeError', 'typeErr'); + env.global.SyntaxError = makeError('SyntaxError', 'syntaxErr'); }); \ No newline at end of file diff --git a/lib/values/function.ts b/lib/values/function.ts index e00f5c9..c49a4f2 100644 --- a/lib/values/function.ts +++ b/lib/values/function.ts @@ -3,15 +3,15 @@ define("values/function", () => { throw 'Using the constructor Function() is forbidden.'; } as unknown as FunctionConstructor; - Function.prototype = (Function as any).__proto__ as Function; - setConstr(Function.prototype, Function, env); + env.setProto('function', Function.prototype); + setConstr(Function.prototype, Function); - setProps(Function.prototype, env, { + setProps(Function.prototype, { apply(thisArg, args) { if (typeof args !== 'object') throw 'Expected arguments to be an array-like object.'; var len = args.length - 0; let newArgs: any[]; - if (Array.isArray(args)) newArgs = args; + if (internals.isArray(args)) newArgs = args; else { newArgs = []; @@ -21,21 +21,20 @@ define("values/function", () => { } } - return env.internals.apply(this, thisArg, newArgs); + return internals.apply(this, thisArg, newArgs); }, call(thisArg, ...args) { return this.apply(thisArg, args); }, bind(thisArg, ...args) { - var func = this; + const func = this; + const res = function() { + const resArgs = []; - var res = function() { - var resArgs = []; - - for (var i = 0; i < args.length; i++) { + for (let i = 0; i < args.length; i++) { resArgs[i] = args[i]; } - for (var i = 0; i < arguments.length; i++) { + for (let i = 0; i < arguments.length; i++) { resArgs[i + args.length] = arguments[i]; } @@ -48,7 +47,7 @@ define("values/function", () => { return 'function (...) { ... }'; }, }); - setProps(Function, env, { + setProps(Function, { async(func) { if (typeof func !== 'function') throw new TypeError('Expected func to be function.'); @@ -56,7 +55,7 @@ define("values/function", () => { const args = arguments; return new Promise((res, rej) => { - const gen = Function.generator(func as any).apply(this, args as any); + const gen = internals.apply(internals.generator(func as any), this, args as any); (function next(type: 'none' | 'err' | 'ret', val?: any) { try { @@ -83,11 +82,11 @@ define("values/function", () => { asyncGenerator(func) { if (typeof func !== 'function') throw new TypeError('Expected func to be function.'); - return function(this: any) { - const gen = Function.generator((_yield) => func( + return function(this: any, ...args: any[]) { + const gen = internals.apply(internals.generator((_yield) => func( val => _yield(['await', val]) as any, val => _yield(['yield', val]) - )).apply(this, arguments as any); + )), this, args) as Generator<['await' | 'yield', any]>; const next = (resolve: Function, reject: Function, type: 'none' | 'val' | 'ret' | 'err', val?: any) => { let res; @@ -124,17 +123,18 @@ define("values/function", () => { }, generator(func) { if (typeof func !== 'function') throw new TypeError('Expected func to be function.'); - const gen = env.internals.makeGenerator(func); - return (...args: any[]) => { - const it = gen(args); + const gen = internals.generator(func); + return function(this: any, ...args: any[]) { + const it = internals.apply(gen, this, args); return { - next: it.next, - return: it.return, - throw: it.throw, + next: (...args) => internals.apply(it.next, it, args), + return: (val) => internals.apply(it.next, it, [val]), + throw: (val) => internals.apply(it.next, it, [val]), [Symbol.iterator]() { return this; } } } } }) + internals.markSpecial(Function); }); \ No newline at end of file diff --git a/lib/values/number.ts b/lib/values/number.ts index 11366a4..57e8d48 100644 --- a/lib/values/number.ts +++ b/lib/values/number.ts @@ -7,10 +7,10 @@ define("values/number", () => { else (this as any).value = val; } as NumberConstructor; - Number.prototype = (0 as any).__proto__ as Number; - setConstr(Number.prototype, Number, env); + env.setProto('number', Number.prototype); + setConstr(Number.prototype, Number); - setProps(Number.prototype, env, { + setProps(Number.prototype, { valueOf() { if (typeof this === 'number') return this; else return (this as any).value; @@ -21,13 +21,13 @@ define("values/number", () => { } }); - setProps(Number, env, { - parseInt(val) { return Math.trunc(Number.parseFloat(val)); }, - parseFloat(val) { return env.internals.parseFloat(val); }, + setProps(Number, { + parseInt(val) { return Math.trunc(val as any - 0); }, + parseFloat(val) { return val as any - 0; }, }); - env.global.Object.defineProperty(env.global, 'parseInt', { value: Number.parseInt, writable: false }); - env.global.Object.defineProperty(env.global, 'parseFloat', { value: Number.parseFloat, writable: false }); + env.global.parseInt = Number.parseInt; + env.global.parseFloat = Number.parseFloat; env.global.Object.defineProperty(env.global, 'NaN', { value: 0 / 0, writable: false }); env.global.Object.defineProperty(env.global, 'Infinity', { value: 1 / 0, writable: false }); }); \ No newline at end of file diff --git a/lib/values/object.ts b/lib/values/object.ts index 4e63a39..4c8cdc3 100644 --- a/lib/values/object.ts +++ b/lib/values/object.ts @@ -8,8 +8,9 @@ define("values/object", () => { return arg; } as ObjectConstructor; - Object.prototype = ({} as any).__proto__ as Object; - setConstr(Object.prototype, Object as any, env); + env.setProto('object', Object.prototype); + (Object.prototype as any).__proto__ = null; + setConstr(Object.prototype, Object as any); function throwNotObject(obj: any, name: string) { if (obj === null || typeof obj !== 'object' && typeof obj !== 'function') { @@ -20,8 +21,8 @@ define("values/object", () => { return typeof obj === 'object' && obj !== null || typeof obj === 'function'; } - setProps(Object, env, { - assign: function(dst, ...src) { + setProps(Object, { + assign(dst, ...src) { throwNotObject(dst, 'assign'); for (let i = 0; i < src.length; i++) { const obj = src[i]; @@ -40,10 +41,10 @@ define("values/object", () => { defineProperty(obj, key, attrib) { throwNotObject(obj, 'defineProperty'); if (typeof attrib !== 'object') throw new TypeError('Expected attributes to be an object.'); - + if ('value' in attrib) { if ('get' in attrib || 'set' in attrib) throw new TypeError('Cannot specify a value and accessors for a property.'); - if (!env.internals.defineField( + if (!internals.defineField( obj, key, attrib.value, !!attrib.writable, @@ -54,8 +55,8 @@ define("values/object", () => { else { if (typeof attrib.get !== 'function' && attrib.get !== undefined) throw new TypeError('Get accessor must be a function.'); if (typeof attrib.set !== 'function' && attrib.set !== undefined) throw new TypeError('Set accessor must be a function.'); - - if (!env.internals.defineProp( + + if (!internals.defineProp( obj, key, attrib.get, attrib.set, @@ -63,50 +64,87 @@ define("values/object", () => { !!attrib.configurable )) throw new TypeError('Can\'t define property \'' + key + '\'.'); } - + return obj; }, defineProperties(obj, attrib) { throwNotObject(obj, 'defineProperties'); if (typeof attrib !== 'object' && typeof attrib !== 'function') throw 'Expected second argument to be an object.'; - + for (var key in attrib) { Object.defineProperty(obj, key, attrib[key]); } - + return obj; }, keys(obj, onlyString) { - onlyString = !!(onlyString ?? true); - return env.internals.keys(obj, onlyString); + return internals.keys(obj, !!(onlyString ?? true)); }, entries(obj, onlyString) { - return Object.keys(obj, onlyString).map(v => [ v, (obj as any)[v] ]); + const res = []; + const keys = internals.keys(obj, !!(onlyString ?? true)); + + for (let i = 0; i < keys.length; i++) { + res[i] = [ keys[i], (obj as any)[keys[i]] ]; + } + + return keys; }, values(obj, onlyString) { - return Object.keys(obj, onlyString).map(v => (obj as any)[v]); + const res = []; + const keys = internals.keys(obj, !!(onlyString ?? true)); + + for (let i = 0; i < keys.length; i++) { + res[i] = (obj as any)[keys[i]]; + } + + return keys; }, getOwnPropertyDescriptor(obj, key) { - return env.internals.ownProp(obj, key); + return internals.ownProp(obj, key) as any; }, getOwnPropertyDescriptors(obj) { - return Object.fromEntries([ - ...Object.getOwnPropertyNames(obj), - ...Object.getOwnPropertySymbols(obj) - ].map(v => [ v, Object.getOwnPropertyDescriptor(obj, v) ])) as any; + const res = []; + const keys = internals.ownPropKeys(obj); + + for (let i = 0; i < keys.length; i++) { + res[i] = internals.ownProp(obj, keys[i]); + } + + return res; }, getOwnPropertyNames(obj) { - return env.internals.ownPropKeys(obj, false); + const arr = internals.ownPropKeys(obj); + const res = []; + + for (let i = 0; i < arr.length; i++) { + if (typeof arr[i] === 'symbol') continue; + res[res.length] = arr[i]; + } + + return res as any; }, getOwnPropertySymbols(obj) { - return env.internals.ownPropKeys(obj, true); + const arr = internals.ownPropKeys(obj); + const res = []; + + for (let i = 0; i < arr.length; i++) { + if (typeof arr[i] !== 'symbol') continue; + res[res.length] = arr[i]; + } + + return res as any; }, hasOwn(obj, key) { - if (Object.getOwnPropertyNames(obj).includes(key)) return true; - if (Object.getOwnPropertySymbols(obj).includes(key)) return true; + const keys = internals.ownPropKeys(obj); + + for (let i = 0; i < keys.length; i++) { + if (keys[i] === key) return true; + } + return false; }, @@ -130,41 +168,51 @@ define("values/object", () => { preventExtensions(obj) { throwNotObject(obj, 'preventExtensions'); - env.internals.preventExtensions(obj); + internals.lock(obj, 'ext'); return obj; }, seal(obj) { throwNotObject(obj, 'seal'); - env.internals.seal(obj); + internals.lock(obj, 'seal'); return obj; }, freeze(obj) { throwNotObject(obj, 'freeze'); - env.internals.freeze(obj); + internals.lock(obj, 'freeze'); return obj; }, isExtensible(obj) { if (!check(obj)) return false; - return env.internals.extensible(obj); + return internals.extensible(obj); }, isSealed(obj) { if (!check(obj)) return true; - if (Object.isExtensible(obj)) return false; - return Object.getOwnPropertyNames(obj).every(v => !Object.getOwnPropertyDescriptor(obj, v).configurable); + if (internals.extensible(obj)) return false; + const keys = internals.ownPropKeys(obj); + + for (let i = 0; i < keys.length; i++) { + if (internals.ownProp(obj, keys[i]).configurable) return false; + } + + return true; }, isFrozen(obj) { if (!check(obj)) return true; - if (Object.isExtensible(obj)) return false; - return Object.getOwnPropertyNames(obj).every(v => { - var prop = Object.getOwnPropertyDescriptor(obj, v); + if (internals.extensible(obj)) return false; + const keys = internals.ownPropKeys(obj); + + for (let i = 0; i < keys.length; i++) { + const prop = internals.ownProp(obj, keys[i]); + if (prop.configurable) return false; if ('writable' in prop && prop.writable) return false; - return !prop.configurable; - }); + } + + return true; } }); - setProps(Object.prototype, env, { + setProps(Object.prototype, { valueOf() { return this; }, @@ -175,4 +223,5 @@ define("values/object", () => { return Object.hasOwn(this, key); }, }); + internals.markSpecial(Object); }); \ No newline at end of file diff --git a/lib/values/string.ts b/lib/values/string.ts index 95023ff..b1af549 100644 --- a/lib/values/string.ts +++ b/lib/values/string.ts @@ -7,10 +7,10 @@ define("values/string", () => { else (this as any).value = val; } as StringConstructor; - String.prototype = ('' as any).__proto__ as String; - setConstr(String.prototype, String, env); + env.setProto('string', String.prototype); + setConstr(String.prototype, String); - setProps(String.prototype, env, { + setProps(String.prototype, { toString() { if (typeof this === 'string') return this; else return (this as any).value; @@ -27,7 +27,14 @@ define("values/string", () => { } start = start ?? 0 | 0; end = (end ?? this.length) | 0; - return env.internals.substring(this, start, end); + + const res = []; + + for (let i = start; i < end; i++) { + if (i >= 0 && i < this.length) res[res.length] = this[i]; + } + + return internals.stringFromStrings(res); }, substr(start, length) { start = start ?? 0 | 0; @@ -36,14 +43,41 @@ define("values/string", () => { if (start < 0) start = 0; length = (length ?? this.length - start) | 0; - return this.substring(start, length + start); + const end = length + start; + const res = []; + + for (let i = start; i < end; i++) { + if (i >= 0 && i < this.length) res[res.length] = this[i]; + } + + return internals.stringFromStrings(res); }, toLowerCase() { - return env.internals.toLower(this + ''); + // TODO: Implement localization + const res = []; + + for (let i = 0; i < this.length; i++) { + const c = internals.char(this[i]); + + if (c >= 65 && c <= 90) res[i] = c - 65 + 97; + else res[i] = c; + } + + return internals.stringFromChars(res); }, toUpperCase() { - return env.internals.toUpper(this + ''); + // TODO: Implement localization + const res = []; + + for (let i = 0; i < this.length; i++) { + const c = internals.char(this[i]); + + if (c >= 97 && c <= 122) res[i] = c - 97 + 65; + else res[i] = c; + } + + return internals.stringFromChars(res); }, charAt(pos) { @@ -57,9 +91,14 @@ define("values/string", () => { return this[pos]; }, charCodeAt(pos) { - var res = this.charAt(pos); - if (res === '') return NaN; - else return env.internals.toCharCode(res); + if (typeof this !== 'string') { + if (this instanceof String) return (this as any).value.charAt(pos); + else throw new Error('This function may be used only with primitive or object strings.'); + } + + pos = pos | 0; + if (pos < 0 || pos >= this.length) return 0 / 0; + return internals.char(this[pos]); }, startsWith(term, pos) { @@ -68,7 +107,15 @@ define("values/string", () => { else throw new Error('This function may be used only with primitive or object strings.'); } pos = pos! | 0; - return env.internals.startsWith(this, term + '', pos); + term = term + ""; + + if (pos < 0 || this.length < term.length + pos) return false; + + for (let i = 0; i < term.length; i++) { + if (this[i + pos] !== term[i]) return false; + } + + return true; }, endsWith(term, pos) { if (typeof this !== 'string') { @@ -76,7 +123,17 @@ define("values/string", () => { else throw new Error('This function may be used only with primitive or object strings.'); } pos = (pos ?? this.length) | 0; - return env.internals.endsWith(this, term + '', pos); + term = term + ""; + + const start = pos - term.length; + + if (start < 0 || this.length < term.length + start) return false; + + for (let i = 0; i < term.length; i++) { + if (this[i + start] !== term[i]) return false; + } + + return true; }, indexOf(term: any, start) { @@ -189,9 +246,9 @@ define("values/string", () => { } }); - setProps(String, env, { + setProps(String, { fromCharCode(val) { - return env.internals.fromCharCode(val | 0); + return internals.stringFromChars([val | 0]); }, }) @@ -202,7 +259,7 @@ define("values/string", () => { else throw new Error('This function may be used only with primitive or object strings.'); } - return env.internals.strlen(this); + return internals.strlen(this); }, configurable: true, enumerable: false, diff --git a/lib/values/symbol.ts b/lib/values/symbol.ts index 0258b75..4e47c16 100644 --- a/lib/values/symbol.ts +++ b/lib/values/symbol.ts @@ -1,31 +1,34 @@ define("values/symbol", () => { + const symbols: Record = { }; + var Symbol = env.global.Symbol = function(this: any, val?: string) { if (this !== undefined && this !== null) throw new TypeError("Symbol may not be called with 'new'."); if (typeof val !== 'string' && val !== undefined) throw new TypeError('val must be a string or undefined.'); - return env.internals.symbol(val, true); + return internals.symbol(val); } as SymbolConstructor; - Symbol.prototype = env.internals.symbolProto; - setConstr(Symbol.prototype, Symbol, env); - (Symbol as any).typeName = Symbol("Symbol.name"); - (Symbol as any).replace = Symbol('Symbol.replace'); - (Symbol as any).match = Symbol('Symbol.match'); - (Symbol as any).matchAll = Symbol('Symbol.matchAll'); - (Symbol as any).split = Symbol('Symbol.split'); - (Symbol as any).search = Symbol('Symbol.search'); - (Symbol as any).iterator = Symbol('Symbol.iterator'); - (Symbol as any).asyncIterator = Symbol('Symbol.asyncIterator'); + env.setProto('symbol', Symbol.prototype); + setConstr(Symbol.prototype, Symbol); - setProps(Symbol, env, { + setProps(Symbol, { for(key) { if (typeof key !== 'string' && key !== undefined) throw new TypeError('key must be a string or undefined.'); - return env.internals.symbol(key, false); + if (key in symbols) return symbols[key]; + else return symbols[key] = internals.symbol(key); }, keyFor(sym) { if (typeof sym !== 'symbol') throw new TypeError('sym must be a symbol.'); - return env.internals.symStr(sym); + return internals.symbolToString(sym); }, - typeName: Symbol('Symbol.name') as any, + + typeName: Symbol("Symbol.name") as any, + replace: Symbol('Symbol.replace') as any, + match: Symbol('Symbol.match') as any, + matchAll: Symbol('Symbol.matchAll') as any, + split: Symbol('Symbol.split') as any, + search: Symbol('Symbol.search') as any, + iterator: Symbol('Symbol.iterator') as any, + asyncIterator: Symbol('Symbol.asyncIterator') as any, }); env.global.Object.defineProperty(Object.prototype, Symbol.typeName, { value: 'Object' }); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..4bbacb0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "java-jscript", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/package.json @@ -0,0 +1 @@ +{} diff --git a/src/me/topchetoeu/jscript/Main.java b/src/me/topchetoeu/jscript/Main.java index b72f080..7fe117b 100644 --- a/src/me/topchetoeu/jscript/Main.java +++ b/src/me/topchetoeu/jscript/Main.java @@ -2,25 +2,54 @@ package me.topchetoeu.jscript; import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; +import me.topchetoeu.jscript.engine.CallContext; import me.topchetoeu.jscript.engine.Engine; +import me.topchetoeu.jscript.engine.Environment; +import me.topchetoeu.jscript.engine.values.NativeFunction; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.events.Observer; import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.exceptions.SyntaxException; +import me.topchetoeu.jscript.interop.NativeTypeRegister; +import me.topchetoeu.jscript.polyfills.Internals; public class Main { static Thread task; static Engine engine; + static Environment env; + + public static String streamToString(InputStream in) { + try { + StringBuilder out = new StringBuilder(); + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + + for(var line = br.readLine(); line != null; line = br.readLine()) { + out.append(line).append('\n'); + } + + br.close(); + return out.toString(); + } + catch (IOException e) { + return null; + } + } + public static String resourceToString(String name) { + var str = Main.class.getResourceAsStream("/me/topchetoeu/jscript/" + name); + if (str == null) return null; + return streamToString(str); + } private static Observer valuePrinter = new Observer() { public void next(Object data) { try { - Values.printValue(engine.context(), data); + Values.printValue(null, data); } catch (InterruptedException e) { } System.out.println(); @@ -28,20 +57,24 @@ public class Main { public void error(RuntimeException err) { try { - if (err instanceof EngineException) { - System.out.println("Uncaught " + ((EngineException)err).toString(engine.context())); + try { + if (err instanceof EngineException) { + System.out.println("Uncaught " + ((EngineException)err).toString(new CallContext(engine, env))); + } + else if (err instanceof SyntaxException) { + System.out.println("Syntax error:" + ((SyntaxException)err).msg); + } + else if (err.getCause() instanceof InterruptedException) return; + else { + System.out.println("Internal error ocurred:"); + err.printStackTrace(); + } } - else if (err instanceof SyntaxException) { - System.out.println("Syntax error:" + ((SyntaxException)err).msg); + catch (EngineException ex) { + System.out.println("Uncaught "); + Values.printValue(null, ((EngineException)err).value); + System.out.println(); } - else if (err.getCause() instanceof InterruptedException) return; - else { - System.out.println("Internal error ocurred:"); - err.printStackTrace(); - } - } - catch (EngineException ex) { - System.out.println("Uncaught [error while converting to string]"); } catch (InterruptedException ex) { return; @@ -53,23 +86,33 @@ public class Main { System.out.println(String.format("Running %s v%s by %s", Metadata.NAME, Metadata.VERSION, Metadata.AUTHOR)); var in = new BufferedReader(new InputStreamReader(System.in)); engine = new Engine(); - var scope = engine.global().globalChild(); + env = new Environment(null, null, null); var exited = new boolean[1]; - scope.define("exit", ctx -> { + env.global.define("exit", ctx -> { exited[0] = true; task.interrupt(); throw new InterruptedException(); }); - scope.define("go", ctx -> { + env.global.define("go", ctx -> { try { - var func = engine.compile(scope, "do.js", new String(Files.readAllBytes(Path.of("do.js")))); + var func = engine.compile(ctx, "do.js", new String(Files.readAllBytes(Path.of("do.js")))); return func.call(ctx); } catch (IOException e) { throw new EngineException("Couldn't open do.js"); } }); + env.global.define(true, new NativeFunction("log", (el, t, _args) -> { + for (var obj : _args) Values.printValue(el, obj); + System.out.println(); + return null; + })); + + var builderEnv = env.child(); + builderEnv.wrappersProvider = new NativeTypeRegister(); + + engine.pushMsg(false, Map.of(), builderEnv, "core.js", resourceToString("js/core.js"), null, env, new Internals()); task = engine.start(); var reader = new Thread(() -> { @@ -79,11 +122,11 @@ public class Main { var raw = in.readLine(); if (raw == null) break; - engine.pushMsg(false, scope, Map.of(), "", raw, null).toObservable().once(valuePrinter); + engine.pushMsg(false, Map.of(), env, "", raw, null).toObservable().once(valuePrinter); } catch (EngineException e) { try { - System.out.println("Uncaught " + e.toString(engine.context())); + System.out.println("Uncaught " + e.toString(null)); } catch (EngineException ex) { System.out.println("Uncaught [error while converting to string]"); diff --git a/src/me/topchetoeu/jscript/engine/CallContext.java b/src/me/topchetoeu/jscript/engine/CallContext.java index f787872..e62bd12 100644 --- a/src/me/topchetoeu/jscript/engine/CallContext.java +++ b/src/me/topchetoeu/jscript/engine/CallContext.java @@ -4,18 +4,20 @@ import java.util.Collections; import java.util.Hashtable; import java.util.Map; +@SuppressWarnings("unchecked") public class CallContext { public static final class DataKey {} public final Engine engine; - private final Map, Object> data = new Hashtable<>(); + public Environment environment; + private final Map, Object> data; - public Engine engine() { return engine; } public Map, Object> data() { return Collections.unmodifiableMap(data); } public CallContext copy() { - return new CallContext(engine).mergeData(data); + return new CallContext(engine, environment).mergeData(data); } + public CallContext mergeData(Map, Object> objs) { data.putAll(objs); return this; @@ -25,7 +27,6 @@ public class CallContext { else data.put(key, val); return this; } - @SuppressWarnings("unchecked") public T addData(DataKey key, T val) { if (data.containsKey(key)) return (T)data.get(key); else { @@ -34,15 +35,14 @@ public class CallContext { return val; } } - public boolean hasData(DataKey key) { return data.containsKey(key); } public T getData(DataKey key) { return getData(key, null); } - @SuppressWarnings("unchecked") public T getData(DataKey key, T defaultVal) { if (!hasData(key)) return defaultVal; else return (T)data.get(key); } + public boolean hasData(DataKey key) { return data.containsKey(key); } public CallContext changeData(DataKey key, int n, int start) { return setData(key, getData(key, start) + n); @@ -54,7 +54,14 @@ public class CallContext { return changeData(key, 1, 0); } - public CallContext(Engine engine) { + public CallContext(Engine engine, Environment env) { this.engine = engine; + this.data = new Hashtable<>(); + this.environment = env; + } + public CallContext(CallContext parent, Environment env) { + this.engine = parent.engine; + this.data = parent.data; + this.environment = env; } } diff --git a/src/me/topchetoeu/jscript/engine/Engine.java b/src/me/topchetoeu/jscript/engine/Engine.java index 111e66a..917d3db 100644 --- a/src/me/topchetoeu/jscript/engine/Engine.java +++ b/src/me/topchetoeu/jscript/engine/Engine.java @@ -1,43 +1,43 @@ package me.topchetoeu.jscript.engine; -import java.util.HashMap; import java.util.Map; import java.util.concurrent.LinkedBlockingDeque; import me.topchetoeu.jscript.engine.CallContext.DataKey; -import me.topchetoeu.jscript.engine.debug.DebugState; -import me.topchetoeu.jscript.engine.modules.ModuleManager; -import me.topchetoeu.jscript.engine.scope.GlobalScope; import me.topchetoeu.jscript.engine.values.FunctionValue; -import me.topchetoeu.jscript.engine.values.ObjectValue; -import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto; +import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.events.Awaitable; import me.topchetoeu.jscript.events.DataNotifier; import me.topchetoeu.jscript.exceptions.EngineException; -import me.topchetoeu.jscript.interop.NativeTypeRegister; import me.topchetoeu.jscript.parsing.Parsing; public class Engine { - private static class RawFunction { - public final GlobalScope scope; + private class UncompiledFunction extends FunctionValue { public final String filename; public final String raw; - public RawFunction(GlobalScope scope, String filename, String raw) { - this.scope = scope; + @Override + public Object call(CallContext ctx, Object thisArg, Object... args) throws InterruptedException { + return compile(ctx, filename, raw).call(ctx, thisArg, args); + } + + public UncompiledFunction(String filename, String raw) { + super(filename, 0); this.filename = filename; this.raw = raw; } } private static class Task { - public final Object func; + public final FunctionValue func; public final Object thisArg; public final Object[] args; public final Map, Object> data; public final DataNotifier notifier = new DataNotifier<>(); + public final Environment env; - public Task(Object func, Map, Object> data, Object thisArg, Object[] args) { + public Task(Environment env, FunctionValue func, Map, Object> data, Object thisArg, Object[] args) { + this.env = env; this.func = func; this.data = data; this.thisArg = thisArg; @@ -45,72 +45,22 @@ public class Engine { } } - public static final DataKey DEBUG_STATE_KEY = new DataKey<>(); private static int nextId = 0; - private Map, Object> callCtxVals = new HashMap<>(); - private GlobalScope global = new GlobalScope(); - private ObjectValue arrayProto = new ObjectValue(); - private ObjectValue boolProto = new ObjectValue(); - private ObjectValue funcProto = new ObjectValue(); - private ObjectValue numProto = new ObjectValue(); - private ObjectValue objProto = new ObjectValue(PlaceholderProto.NONE); - private ObjectValue strProto = new ObjectValue(); - private ObjectValue symProto = new ObjectValue(); - private ObjectValue errProto = new ObjectValue(); - private ObjectValue syntaxErrProto = new ObjectValue(PlaceholderProto.ERROR); - private ObjectValue typeErrProto = new ObjectValue(PlaceholderProto.ERROR); - private ObjectValue rangeErrProto = new ObjectValue(PlaceholderProto.ERROR); - private NativeTypeRegister typeRegister; + // private Map, Object> callCtxVals = new HashMap<>(); + // private NativeTypeRegister typeRegister; private Thread thread; private LinkedBlockingDeque macroTasks = new LinkedBlockingDeque<>(); private LinkedBlockingDeque microTasks = new LinkedBlockingDeque<>(); public final int id = ++nextId; - public final DebugState debugState = new DebugState(); - public ObjectValue arrayProto() { return arrayProto; } - public ObjectValue booleanProto() { return boolProto; } - public ObjectValue functionProto() { return funcProto; } - public ObjectValue numberProto() { return numProto; } - public ObjectValue objectProto() { return objProto; } - public ObjectValue stringProto() { return strProto; } - public ObjectValue symbolProto() { return symProto; } - public ObjectValue errorProto() { return errProto; } - public ObjectValue syntaxErrorProto() { return syntaxErrProto; } - public ObjectValue typeErrorProto() { return typeErrProto; } - public ObjectValue rangeErrorProto() { return rangeErrProto; } - - public GlobalScope global() { return global; } - public NativeTypeRegister typeRegister() { return typeRegister; } - - public void copyFrom(Engine other) { - global = other.global; - typeRegister = other.typeRegister; - arrayProto = other.arrayProto; - boolProto = other.boolProto; - funcProto = other.funcProto; - numProto = other.numProto; - objProto = other.objProto; - strProto = other.strProto; - symProto = other.symProto; - errProto = other.errProto; - syntaxErrProto = other.syntaxErrProto; - typeErrProto = other.typeErrProto; - rangeErrProto = other.rangeErrProto; - } + // public NativeTypeRegister typeRegister() { return typeRegister; } private void runTask(Task task) throws InterruptedException { try { - FunctionValue func; - if (task.func instanceof FunctionValue) func = (FunctionValue)task.func; - else { - var raw = (RawFunction)task.func; - func = compile(raw.scope, raw.filename, raw.raw); - } - - task.notifier.next(func.call(context().mergeData(task.data), task.thisArg, task.args)); + task.notifier.next(task.func.call(new CallContext(this, task.env).mergeData(task.data), task.thisArg, task.args)); } catch (InterruptedException e) { task.notifier.error(new RuntimeException(e)); @@ -160,42 +110,22 @@ public class Engine { return this.thread != null; } - public Object makeRegex(String pattern, String flags) { - throw EngineException.ofError("Regular expressions not supported."); - } - public ModuleManager modules() { - return null; - } - public ObjectValue getPrototype(Class clazz) { - return typeRegister.getProto(clazz); - } - public FunctionValue getConstructor(Class clazz) { - return typeRegister.getConstr(clazz); - } - public CallContext context() { return new CallContext(this).mergeData(callCtxVals); } - - public Awaitable pushMsg(boolean micro, FunctionValue func, Map, Object> data, Object thisArg, Object... args) { - var msg = new Task(func, data, thisArg, args); + public Awaitable pushMsg(boolean micro, Map, Object> data, Environment env, FunctionValue func, Object thisArg, Object... args) { + var msg = new Task(env, func, data, thisArg, args); if (micro) microTasks.addLast(msg); else macroTasks.addLast(msg); return msg.notifier; } - public Awaitable pushMsg(boolean micro, GlobalScope scope, Map, Object> data, String filename, String raw, Object thisArg, Object... args) { - var msg = new Task(new RawFunction(scope, filename, raw), data, thisArg, args); - if (micro) microTasks.addLast(msg); - else macroTasks.addLast(msg); - return msg.notifier; + public Awaitable pushMsg(boolean micro, Map, Object> data, Environment env, String filename, String raw, Object thisArg, Object... args) { + return pushMsg(micro, data, env, new UncompiledFunction(filename, raw), thisArg, args); } - public FunctionValue compile(GlobalScope scope, String filename, String raw) throws InterruptedException { - return Parsing.compile(scope, filename, raw); + public FunctionValue compile(CallContext ctx, String filename, String raw) throws InterruptedException { + var res = Values.toString(ctx, ctx.environment.compile.call(ctx, null, raw, filename)); + return Parsing.compile(ctx.environment, filename, res); } - public Engine(NativeTypeRegister register) { - this.typeRegister = register; - this.callCtxVals.put(DEBUG_STATE_KEY, debugState); - } - public Engine() { - this(new NativeTypeRegister()); - } + // public Engine() { + // this.typeRegister = new NativeTypeRegister(); + // } } diff --git a/src/me/topchetoeu/jscript/engine/Environment.java b/src/me/topchetoeu/jscript/engine/Environment.java new file mode 100644 index 0000000..8224833 --- /dev/null +++ b/src/me/topchetoeu/jscript/engine/Environment.java @@ -0,0 +1,81 @@ +package me.topchetoeu.jscript.engine; + +import java.util.HashMap; + +import me.topchetoeu.jscript.engine.scope.GlobalScope; +import me.topchetoeu.jscript.engine.values.FunctionValue; +import me.topchetoeu.jscript.engine.values.NativeFunction; +import me.topchetoeu.jscript.engine.values.ObjectValue; +import me.topchetoeu.jscript.exceptions.EngineException; +import me.topchetoeu.jscript.interop.Native; +import me.topchetoeu.jscript.interop.NativeGetter; +import me.topchetoeu.jscript.interop.NativeSetter; + +public class Environment { + private HashMap prototypes = new HashMap<>(); + public GlobalScope global; + public WrappersProvider wrappersProvider; + + @Native public FunctionValue compile; + @Native public FunctionValue regexConstructor = new NativeFunction("RegExp", (ctx, thisArg, args) -> { + throw EngineException.ofError("Regular expressions not supported."); + }); + @Native public ObjectValue proto(String name) { + return prototypes.get(name); + } + @Native public void setProto(String name, ObjectValue val) { + prototypes.put(name, val); + } + // @Native public ObjectValue arrayPrototype = new ObjectValue(); + // @Native public ObjectValue boolPrototype = new ObjectValue(); + // @Native public ObjectValue functionPrototype = new ObjectValue(); + // @Native public ObjectValue numberPrototype = new ObjectValue(); + // @Native public ObjectValue objectPrototype = new ObjectValue(PlaceholderProto.NONE); + // @Native public ObjectValue stringPrototype = new ObjectValue(); + // @Native public ObjectValue symbolPrototype = new ObjectValue(); + // @Native public ObjectValue errorPrototype = new ObjectValue(); + // @Native public ObjectValue syntaxErrPrototype = new ObjectValue(PlaceholderProto.ERROR); + // @Native public ObjectValue typeErrPrototype = new ObjectValue(PlaceholderProto.ERROR); + // @Native public ObjectValue rangeErrPrototype = new ObjectValue(PlaceholderProto.ERROR); + + @NativeGetter("global") + public ObjectValue getGlobal() { + return global.obj; + } + @NativeSetter("global") + public void setGlobal(ObjectValue val) { + global = new GlobalScope(val); + } + + @Native + public Environment fork() { + var res = new Environment(compile, wrappersProvider, global); + res.regexConstructor = regexConstructor; + res.prototypes = new HashMap<>(prototypes); + return res; + } + + @Native + public Environment child() { + var res = fork(); + res.global = res.global.globalChild(); + return res; + } + + public Environment(FunctionValue compile, WrappersProvider nativeConverter, GlobalScope global) { + if (compile == null) compile = new NativeFunction("compile", (ctx, thisArg, args) -> args.length == 0 ? "" : args[0]); + if (nativeConverter == null) nativeConverter = new WrappersProvider() { + public ObjectValue getConstr(Class obj) { + throw EngineException.ofType("Java objects not passable to Javascript."); + } + public ObjectValue getProto(Class obj) { + throw EngineException.ofType("Java objects not passable to Javascript."); + } + }; + if (global == null) global = new GlobalScope(); + + this.wrappersProvider = nativeConverter; + this.compile = compile; + this.global = global; + } +} diff --git a/src/me/topchetoeu/jscript/engine/WrappersProvider.java b/src/me/topchetoeu/jscript/engine/WrappersProvider.java new file mode 100644 index 0000000..6205da8 --- /dev/null +++ b/src/me/topchetoeu/jscript/engine/WrappersProvider.java @@ -0,0 +1,8 @@ +package me.topchetoeu.jscript.engine; + +import me.topchetoeu.jscript.engine.values.ObjectValue; + +public interface WrappersProvider { + public ObjectValue getProto(Class obj); + public ObjectValue getConstr(Class obj); +} diff --git a/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java b/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java index 702a9a4..5a6e188 100644 --- a/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java +++ b/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java @@ -6,7 +6,6 @@ import java.util.List; import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.engine.CallContext; import me.topchetoeu.jscript.engine.DebugCommand; -import me.topchetoeu.jscript.engine.Engine; import me.topchetoeu.jscript.engine.CallContext.DataKey; import me.topchetoeu.jscript.engine.scope.LocalScope; import me.topchetoeu.jscript.engine.scope.ValueVariable; @@ -106,14 +105,14 @@ public class CodeFrame { if (ctx.getData(STACK_N_KEY, 0) >= ctx.addData(MAX_STACK_KEY, 10000)) throw EngineException.ofRange("Stack overflow!"); ctx.changeData(STACK_N_KEY); - var debugState = ctx.getData(Engine.DEBUG_STATE_KEY); - if (debugState != null) debugState.pushFrame(this); + // var debugState = ctx.getData(Engine.DEBUG_STATE_KEY); + // if (debugState != null) debugState.pushFrame(this); } public void end(CallContext ctx) { - var debugState = ctx.getData(Engine.DEBUG_STATE_KEY); - - if (debugState != null) debugState.popFrame(); ctx.changeData(STACK_N_KEY, -1); + + // var debugState = ctx.getData(Engine.DEBUG_STATE_KEY); + // if (debugState != null) debugState.popFrame(); } private Object nextNoTry(CallContext ctx) throws InterruptedException { diff --git a/src/me/topchetoeu/jscript/engine/frame/Runners.java b/src/me/topchetoeu/jscript/engine/frame/Runners.java index c59cb0f..631706e 100644 --- a/src/me/topchetoeu/jscript/engine/frame/Runners.java +++ b/src/me/topchetoeu/jscript/engine/frame/Runners.java @@ -69,7 +69,7 @@ public class Runners { public static Object execMakeVar(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var name = (String)instr.get(0); - frame.function.globals.define(name); + frame.function.environment.global.define(name); frame.codePtr++; return NO_RETURN; } @@ -164,7 +164,7 @@ public class Runners { public static Object execLoadVar(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var i = instr.get(0); - if (i instanceof String) frame.push(ctx, frame.function.globals.get(ctx, (String)i)); + if (i instanceof String) frame.push(ctx, frame.function.environment.global.get(ctx, (String)i)); else frame.push(ctx, frame.scope.get((int)i).get(ctx)); frame.codePtr++; @@ -176,7 +176,7 @@ public class Runners { return NO_RETURN; } public static Object execLoadGlob(CallContext ctx, Instruction instr, CodeFrame frame) { - frame.push(ctx, frame.function.globals.obj); + frame.push(ctx, frame.function.environment.global.obj); frame.codePtr++; return NO_RETURN; } @@ -202,7 +202,7 @@ public class Runners { var body = new Instruction[end - start]; System.arraycopy(frame.function.body, start, body, 0, end - start); - var func = new CodeFunction("", localsN, len, frame.function.globals, captures, body); + var func = new CodeFunction("", localsN, len, frame.function.environment, captures, body); frame.push(ctx, func); frame.codePtr += n; @@ -227,7 +227,7 @@ public class Runners { return execLoadMember(ctx, state, instr, frame); } public static Object execLoadRegEx(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { - frame.push(ctx, ctx.engine().makeRegex(instr.get(0), instr.get(1))); + frame.push(ctx, ctx.environment.regexConstructor.call(ctx, null, instr.get(0), instr.get(1))); frame.codePtr++; return NO_RETURN; } @@ -252,7 +252,7 @@ public class Runners { var val = (boolean)instr.get(1) ? frame.peek() : frame.pop(); var i = instr.get(0); - if (i instanceof String) frame.function.globals.set(ctx, (String)i, val); + if (i instanceof String) frame.function.environment.global.set(ctx, (String)i, val); else frame.scope.get((int)i).set(ctx, val); frame.codePtr++; @@ -299,8 +299,8 @@ public class Runners { Object obj; if (name != null) { - if (frame.function.globals.has(ctx, name)) { - obj = frame.function.globals.get(ctx, name); + if (frame.function.environment.global.has(ctx, name)) { + obj = frame.function.environment.global.get(ctx, name); } else obj = null; } diff --git a/src/me/topchetoeu/jscript/engine/scope/GlobalScope.java b/src/me/topchetoeu/jscript/engine/scope/GlobalScope.java index e318c24..38fac3a 100644 --- a/src/me/topchetoeu/jscript/engine/scope/GlobalScope.java +++ b/src/me/topchetoeu/jscript/engine/scope/GlobalScope.java @@ -11,10 +11,9 @@ import me.topchetoeu.jscript.exceptions.EngineException; public class GlobalScope implements ScopeRecord { public final ObjectValue obj; - public final GlobalScope parent; @Override - public GlobalScope parent() { return parent; } + public GlobalScope parent() { return null; } public boolean has(CallContext ctx, String name) throws InterruptedException { return obj.hasMember(ctx, name, false); @@ -24,7 +23,9 @@ public class GlobalScope implements ScopeRecord { } public GlobalScope globalChild() { - return new GlobalScope(this); + var obj = new ObjectValue(); + obj.setPrototype(null, this.obj); + return new GlobalScope(obj); } public LocalScopeRecord child() { return new LocalScopeRecord(this); @@ -78,12 +79,9 @@ public class GlobalScope implements ScopeRecord { } public GlobalScope() { - this.parent = null; this.obj = new ObjectValue(); } - public GlobalScope(GlobalScope parent) { - this.parent = null; - this.obj = new ObjectValue(); - this.obj.setPrototype(null, parent.obj); + public GlobalScope(ObjectValue val) { + this.obj = val; } } diff --git a/src/me/topchetoeu/jscript/engine/scope/LocalScope.java b/src/me/topchetoeu/jscript/engine/scope/LocalScope.java index efa0bb4..3232f47 100644 --- a/src/me/topchetoeu/jscript/engine/scope/LocalScope.java +++ b/src/me/topchetoeu/jscript/engine/scope/LocalScope.java @@ -4,7 +4,6 @@ import java.util.ArrayList; public class LocalScope { private String[] names; - private LocalScope parent; public final ValueVariable[] captures; public final ValueVariable[] locals; public final ArrayList catchVars = new ArrayList<>(); @@ -33,18 +32,11 @@ public class LocalScope { return captures.length + locals.length; } - public GlobalScope toGlobal(GlobalScope global) { - GlobalScope res; - - if (parent == null) res = new GlobalScope(global); - else res = new GlobalScope(parent.toGlobal(global)); - + public void toGlobal(GlobalScope global) { var names = getNames(); for (var i = 0; i < names.length; i++) { - res.define(names[i], locals[i]); + global.define(names[i], locals[i]); } - - return res; } public LocalScope(int n, ValueVariable[] captures) { @@ -55,8 +47,4 @@ public class LocalScope { locals[i] = new ValueVariable(false, null); } } - public LocalScope(int n, ValueVariable[] captures, LocalScope parent) { - this(n, captures); - this.parent = parent; - } } diff --git a/src/me/topchetoeu/jscript/engine/values/CodeFunction.java b/src/me/topchetoeu/jscript/engine/values/CodeFunction.java index 48a430e..e2ff92e 100644 --- a/src/me/topchetoeu/jscript/engine/values/CodeFunction.java +++ b/src/me/topchetoeu/jscript/engine/values/CodeFunction.java @@ -6,8 +6,8 @@ import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction.Type; import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Environment; import me.topchetoeu.jscript.engine.frame.CodeFrame; -import me.topchetoeu.jscript.engine.scope.GlobalScope; import me.topchetoeu.jscript.engine.scope.ValueVariable; public class CodeFunction extends FunctionValue { @@ -17,7 +17,7 @@ public class CodeFunction extends FunctionValue { public final LinkedHashMap breakableLocToIndex = new LinkedHashMap<>(); public final LinkedHashMap breakableIndexToLoc = new LinkedHashMap<>(); public final ValueVariable[] captures; - public final GlobalScope globals; + public Environment environment; public Location loc() { for (var instr : body) { @@ -34,13 +34,13 @@ public class CodeFunction extends FunctionValue { @Override public Object call(CallContext ctx, Object thisArg, Object... args) throws InterruptedException { - return new CodeFrame(ctx, thisArg, args, this).run(ctx); + return new CodeFrame(ctx, thisArg, args, this).run(new CallContext(ctx, environment)); } - public CodeFunction(String name, int localsN, int length, GlobalScope globals, ValueVariable[] captures, Instruction[] body) { + public CodeFunction(String name, int localsN, int length, Environment environment, ValueVariable[] captures, Instruction[] body) { super(name, length); this.captures = captures; - this.globals = globals; + this.environment = environment; this.localsN = localsN; this.length = length; this.body = body; diff --git a/src/me/topchetoeu/jscript/engine/values/NativeWrapper.java b/src/me/topchetoeu/jscript/engine/values/NativeWrapper.java index 7a5e1d0..50efae2 100644 --- a/src/me/topchetoeu/jscript/engine/values/NativeWrapper.java +++ b/src/me/topchetoeu/jscript/engine/values/NativeWrapper.java @@ -8,7 +8,7 @@ public class NativeWrapper extends ObjectValue { @Override public ObjectValue getPrototype(CallContext ctx) throws InterruptedException { - if (prototype == NATIVE_PROTO) return ctx.engine.getPrototype(wrapped.getClass()); + if (prototype == NATIVE_PROTO) return ctx.environment.wrappersProvider.getProto(wrapped.getClass()); else return super.getPrototype(ctx); } diff --git a/src/me/topchetoeu/jscript/engine/values/ObjectValue.java b/src/me/topchetoeu/jscript/engine/values/ObjectValue.java index 4899448..ca0fb55 100644 --- a/src/me/topchetoeu/jscript/engine/values/ObjectValue.java +++ b/src/me/topchetoeu/jscript/engine/values/ObjectValue.java @@ -147,13 +147,13 @@ public class ObjectValue { public ObjectValue getPrototype(CallContext ctx) throws InterruptedException { try { - if (prototype == OBJ_PROTO) return ctx.engine().objectProto(); - if (prototype == ARR_PROTO) return ctx.engine().arrayProto(); - if (prototype == FUNC_PROTO) return ctx.engine().functionProto(); - if (prototype == ERR_PROTO) return ctx.engine().errorProto(); - if (prototype == RANGE_ERR_PROTO) return ctx.engine().rangeErrorProto(); - if (prototype == SYNTAX_ERR_PROTO) return ctx.engine().syntaxErrorProto(); - if (prototype == TYPE_ERR_PROTO) return ctx.engine().typeErrorProto(); + if (prototype == OBJ_PROTO) return ctx.environment.proto("object"); + if (prototype == ARR_PROTO) return ctx.environment.proto("array"); + if (prototype == FUNC_PROTO) return ctx.environment.proto("function"); + if (prototype == ERR_PROTO) return ctx.environment.proto("error"); + if (prototype == RANGE_ERR_PROTO) return ctx.environment.proto("rangeErr"); + if (prototype == SYNTAX_ERR_PROTO) return ctx.environment.proto("syntaxErr"); + if (prototype == TYPE_ERR_PROTO) return ctx.environment.proto("typeErr"); } catch (NullPointerException e) { return null; @@ -165,18 +165,21 @@ public class ObjectValue { val = Values.normalize(ctx, val); if (!extensible()) return false; - if (val == null || val == Values.NULL) prototype = null; + if (val == null || val == Values.NULL) { + prototype = null; + return true; + } else if (Values.isObject(val)) { var obj = Values.object(val); - if (ctx != null && ctx.engine() != null) { - if (obj == ctx.engine().objectProto()) prototype = OBJ_PROTO; - else if (obj == ctx.engine().arrayProto()) prototype = ARR_PROTO; - else if (obj == ctx.engine().functionProto()) prototype = FUNC_PROTO; - else if (obj == ctx.engine().errorProto()) prototype = ERR_PROTO; - else if (obj == ctx.engine().syntaxErrorProto()) prototype = SYNTAX_ERR_PROTO; - else if (obj == ctx.engine().typeErrorProto()) prototype = TYPE_ERR_PROTO; - else if (obj == ctx.engine().rangeErrorProto()) prototype = RANGE_ERR_PROTO; + if (ctx != null && ctx.environment != null) { + if (obj == ctx.environment.proto("object")) prototype = OBJ_PROTO; + else if (obj == ctx.environment.proto("array")) prototype = ARR_PROTO; + else if (obj == ctx.environment.proto("function")) prototype = FUNC_PROTO; + else if (obj == ctx.environment.proto("error")) prototype = ERR_PROTO; + else if (obj == ctx.environment.proto("syntaxErr")) prototype = SYNTAX_ERR_PROTO; + else if (obj == ctx.environment.proto("typeErr")) prototype = TYPE_ERR_PROTO; + else if (obj == ctx.environment.proto("rangeErr")) prototype = RANGE_ERR_PROTO; else prototype = obj; } else prototype = obj; @@ -277,7 +280,8 @@ public class ObjectValue { if (hasField(ctx, key)) return true; if (properties.containsKey(key)) return true; if (own) return false; - return prototype != null && getPrototype(ctx).hasMember(ctx, key, own); + var proto = getPrototype(ctx); + return proto != null && proto.hasMember(ctx, key, own); } public final boolean deleteMember(CallContext ctx, Object key) throws InterruptedException { key = Values.normalize(ctx, key); diff --git a/src/me/topchetoeu/jscript/engine/values/Values.java b/src/me/topchetoeu/jscript/engine/values/Values.java index 22d4654..5f7f4ca 100644 --- a/src/me/topchetoeu/jscript/engine/values/Values.java +++ b/src/me/topchetoeu/jscript/engine/values/Values.java @@ -321,15 +321,15 @@ public class Values { if (isObject(obj)) return object(obj).getPrototype(ctx); if (ctx == null) return null; - if (obj instanceof String) return ctx.engine().stringProto(); - else if (obj instanceof Number) return ctx.engine().numberProto(); - else if (obj instanceof Boolean) return ctx.engine().booleanProto(); - else if (obj instanceof Symbol) return ctx.engine().symbolProto(); + if (obj instanceof String) return ctx.environment.proto("string"); + else if (obj instanceof Number) return ctx.environment.proto("number"); + else if (obj instanceof Boolean) return ctx.environment.proto("bool"); + else if (obj instanceof Symbol) return ctx.environment.proto("symbol"); return null; } public static boolean setPrototype(CallContext ctx, Object obj, Object proto) throws InterruptedException { - obj = normalize(ctx, obj); proto = normalize(ctx, proto); + obj = normalize(ctx, obj); return isObject(obj) && object(obj).setPrototype(ctx, proto); } public static List getMembers(CallContext ctx, Object obj, boolean own, boolean includeNonEnumerable) throws InterruptedException { @@ -420,7 +420,7 @@ public class Values { if (val instanceof Class) { if (ctx == null) return null; - else return ctx.engine.getConstructor((Class)val); + else return ctx.environment.wrappersProvider.getConstr((Class)val); } return new NativeWrapper(val); @@ -498,7 +498,7 @@ public class Values { public static Iterable toJavaIterable(CallContext ctx, Object obj) throws InterruptedException { return () -> { try { - var constr = getMember(ctx, ctx.engine().symbolProto(), "constructor"); + var constr = getMember(ctx, ctx.environment.proto("symbol"), "constructor"); var symbol = getMember(ctx, constr, "iterator"); var iteratorFunc = getMember(ctx, obj, symbol); @@ -567,7 +567,7 @@ public class Values { var it = iterable.iterator(); try { - var key = getMember(ctx, getMember(ctx, ctx.engine().symbolProto(), "constructor"), "iterator"); + var key = getMember(ctx, getMember(ctx, ctx.environment.proto("symbol"), "constructor"), "iterator"); res.defineProperty(ctx, key, new NativeFunction("", (_ctx, thisArg, args) -> thisArg)); } catch (IllegalArgumentException | NullPointerException e) { } diff --git a/src/me/topchetoeu/jscript/interop/NativeTypeRegister.java b/src/me/topchetoeu/jscript/interop/NativeTypeRegister.java index aceaa79..ac6eb85 100644 --- a/src/me/topchetoeu/jscript/interop/NativeTypeRegister.java +++ b/src/me/topchetoeu/jscript/interop/NativeTypeRegister.java @@ -3,12 +3,13 @@ package me.topchetoeu.jscript.interop; import java.lang.reflect.Modifier; import java.util.HashMap; +import me.topchetoeu.jscript.engine.WrappersProvider; import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.NativeFunction; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.exceptions.EngineException; -public class NativeTypeRegister { +public class NativeTypeRegister implements WrappersProvider { private final HashMap, FunctionValue> constructors = new HashMap<>(); private final HashMap, ObjectValue> prototypes = new HashMap<>(); @@ -80,9 +81,7 @@ public class NativeTypeRegister { var name = nat.value(); if (name.equals("")) name = cl.getSimpleName(); - var getter = new OverloadFunction("get " + name).add(Overload.getter(member ? clazz : null, (ctx, thisArg, args) -> { - return ctx.engine().typeRegister().getConstr(cl); - })); + var getter = new NativeFunction("get " + name, (ctx, thisArg, args) -> cl); target.defineProperty(null, name, getter, null, true, false); } diff --git a/src/me/topchetoeu/jscript/parsing/Parsing.java b/src/me/topchetoeu/jscript/parsing/Parsing.java index 2a22195..ea10930 100644 --- a/src/me/topchetoeu/jscript/parsing/Parsing.java +++ b/src/me/topchetoeu/jscript/parsing/Parsing.java @@ -13,8 +13,8 @@ import me.topchetoeu.jscript.compilation.VariableDeclareStatement.Pair; import me.topchetoeu.jscript.compilation.control.*; import me.topchetoeu.jscript.compilation.control.SwitchStatement.SwitchCase; import me.topchetoeu.jscript.compilation.values.*; +import me.topchetoeu.jscript.engine.Environment; import me.topchetoeu.jscript.engine.Operation; -import me.topchetoeu.jscript.engine.scope.GlobalScope; import me.topchetoeu.jscript.engine.scope.ValueVariable; import me.topchetoeu.jscript.engine.values.CodeFunction; import me.topchetoeu.jscript.engine.values.Values; @@ -1842,8 +1842,8 @@ public class Parsing { return list.toArray(Statement[]::new); } - public static CodeFunction compile(GlobalScope scope, Statement... statements) { - var target = scope.globalChild(); + public static CodeFunction compile(Environment environment, Statement... statements) { + var target = environment.global.globalChild(); var subscope = target.child(); var res = new ArrayList(); var body = new CompoundStatement(null, statements); @@ -1870,14 +1870,14 @@ public class Parsing { } else res.add(Instruction.ret()); - return new CodeFunction("", subscope.localsCount(), 0, scope, new ValueVariable[0], res.toArray(Instruction[]::new)); + return new CodeFunction("", subscope.localsCount(), 0, environment, new ValueVariable[0], res.toArray(Instruction[]::new)); } - public static CodeFunction compile(GlobalScope scope, String filename, String raw) { + public static CodeFunction compile(Environment environment, String filename, String raw) { try { - return compile(scope, parse(filename, raw)); + return compile(environment, parse(filename, raw)); } catch (SyntaxException e) { - return new CodeFunction(null, 2, 0, scope, new ValueVariable[0], new Instruction[] { Instruction.throwSyntax(e) }); + return new CodeFunction(null, 2, 0, environment, new ValueVariable[0], new Instruction[] { Instruction.throwSyntax(e) }); } } } diff --git a/src/me/topchetoeu/jscript/polyfills/GeneratorFunction.java b/src/me/topchetoeu/jscript/polyfills/GeneratorFunction.java index f27dba7..159a01a 100644 --- a/src/me/topchetoeu/jscript/polyfills/GeneratorFunction.java +++ b/src/me/topchetoeu/jscript/polyfills/GeneratorFunction.java @@ -11,7 +11,7 @@ import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.interop.Native; public class GeneratorFunction extends FunctionValue { - public final CodeFunction factory; + public final FunctionValue factory; public static class Generator { private boolean yielding = true; @@ -92,7 +92,7 @@ public class GeneratorFunction extends FunctionValue { return handler; } - public GeneratorFunction(CodeFunction factory) { + public GeneratorFunction(FunctionValue factory) { super(factory.name, factory.length); this.factory = factory; } diff --git a/src/me/topchetoeu/jscript/polyfills/Internals.java b/src/me/topchetoeu/jscript/polyfills/Internals.java index da6628a..c6bee13 100644 --- a/src/me/topchetoeu/jscript/polyfills/Internals.java +++ b/src/me/topchetoeu/jscript/polyfills/Internals.java @@ -1,150 +1,130 @@ package me.topchetoeu.jscript.polyfills; -import java.util.HashMap; - import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Environment; import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.CodeFunction; import me.topchetoeu.jscript.engine.values.FunctionValue; +import me.topchetoeu.jscript.engine.values.NativeFunction; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Symbol; import me.topchetoeu.jscript.engine.values.Values; -import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeGetter; public class Internals { - private HashMap intervals = new HashMap<>(); - private HashMap timeouts = new HashMap<>(); - private HashMap symbols = new HashMap<>(); - private int nextId = 0; - - @Native - public double parseFloat(CallContext ctx, Object val) throws InterruptedException { - return Values.toNumber(ctx, val); - } - - @Native - public boolean isArr(Object val) { - return val instanceof ArrayValue; - } - - @NativeGetter("symbolProto") - public ObjectValue symbolProto(CallContext ctx) { - return ctx.engine().symbolProto(); - } - @Native - public final Object apply(CallContext ctx, FunctionValue func, Object th, ArrayValue args) throws InterruptedException { - return func.call(ctx, th, args.toArray()); - } - @Native - public boolean defineProp(CallContext ctx, ObjectValue obj, Object key, FunctionValue getter, FunctionValue setter, boolean enumerable, boolean configurable) { - return obj.defineProperty(ctx, key, getter, setter, configurable, enumerable); - } - @Native - public boolean defineField(CallContext ctx, ObjectValue obj, Object key, Object val, boolean writable, boolean enumerable, boolean configurable) { - return obj.defineProperty(ctx, key, val, writable, configurable, enumerable); - } - - @Native - public int strlen(String str) { - return str.length(); - } - @Native - public String substring(String str, int start, int end) { - if (start > end) return substring(str, end, start); - - if (start < 0) start = 0; - if (start >= str.length()) return ""; - - if (end < 0) end = 0; - if (end > str.length()) end = str.length(); - - return str.substring(start, end); - } - @Native - public String toLower(String str) { - return str.toLowerCase(); - } - @Native - public String toUpper(String str) { - return str.toUpperCase(); - } - @Native - public int toCharCode(String str) { - return str.codePointAt(0); - } - @Native - public String fromCharCode(int code) { - return Character.toString((char)code); - } - @Native - public boolean startsWith(String str, String term, int offset) { - return str.startsWith(term, offset); - } - @Native - public boolean endsWith(String str, String term, int offset) { - try { - return str.substring(0, offset).endsWith(term); + @Native public void markSpecial(FunctionValue... funcs) { + for (var func : funcs) { + func.special = true; } - catch (IndexOutOfBoundsException e) { return false; } - } - - @Native - public int setInterval(CallContext ctx, FunctionValue func, double delay) { + @Native public Environment getEnv(Object func) { + if (func instanceof CodeFunction) return ((CodeFunction)func).environment; + else return null; + } + @Native public Object setEnv(Object func, Environment env) { + if (func instanceof CodeFunction) ((CodeFunction)func).environment = env; + return func; + } + @Native public Object apply(CallContext ctx, FunctionValue func, Object thisArg, ArrayValue args) throws InterruptedException { + return func.call(ctx, thisArg, args.toArray()); + } + @Native public FunctionValue delay(CallContext ctx, double delay, FunctionValue callback) throws InterruptedException { var thread = new Thread((Runnable)() -> { var ms = (long)delay; var ns = (int)((delay - ms) * 10000000); - while (true) { - try { - Thread.sleep(ms, ns); - } - catch (InterruptedException e) { return; } - - ctx.engine().pushMsg(false, func, ctx.data(), null); - } - }); - thread.start(); - - intervals.put(++nextId, thread); - - return nextId; - } - @Native - public int setTimeout(CallContext ctx, FunctionValue func, double delay) { - var thread = new Thread((Runnable)() -> { - var ms = (long)delay; - var ns = (int)((delay - ms) * 1000000); - try { Thread.sleep(ms, ns); } catch (InterruptedException e) { return; } - ctx.engine().pushMsg(false, func, ctx.data(), null); + ctx.engine.pushMsg(false, ctx.data(), ctx.environment, callback, null); }); thread.start(); - timeouts.put(++nextId, thread); - - return nextId; + return new NativeFunction((_ctx, thisArg, args) -> { + thread.interrupt(); + return null; + }); + } + @Native public void pushMessage(CallContext ctx, boolean micro, FunctionValue func, Object thisArg, Object[] args) { + ctx.engine.pushMsg(micro, ctx.data(), ctx.environment, func, thisArg, args); } - @Native - public void clearInterval(int id) { - var thread = intervals.remove(id); - if (thread != null) thread.interrupt(); + @Native public int strlen(String str) { + return str.length(); } - @Native - public void clearTimeout(int id) { - var thread = timeouts.remove(id); - if (thread != null) thread.interrupt(); + @Native("char") public int _char(String str) { + return str.charAt(0); + } + @Native public String stringFromChars(char[] str) { + return new String(str); + } + @Native public String stringFromStrings(String[] str) { + var res = new char[str.length]; + + for (var i = 0; i < str.length; i++) res[i] = str[i].charAt(0); + + return stringFromChars(res); + } + @Native public Symbol symbol(String str) { + return new Symbol(str); + } + @Native public String symbolToString(Symbol str) { + return str.value; } - @Native - public void sort(CallContext ctx, ArrayValue arr, FunctionValue cmp) { + @Native public boolean isArray(Object obj) { + return obj instanceof ArrayValue; + } + @Native public GeneratorFunction generator(FunctionValue obj) { + return new GeneratorFunction(obj); + } + + @Native public boolean defineField(CallContext ctx, ObjectValue obj, Object key, Object val, boolean writable, boolean enumerable, boolean configurable) { + return obj.defineProperty(ctx, key, val, writable, configurable, enumerable); + } + @Native public boolean defineProp(CallContext ctx, ObjectValue obj, Object key, FunctionValue getter, FunctionValue setter, boolean enumerable, boolean configurable) { + return obj.defineProperty(ctx, key, getter, setter, configurable, enumerable); + } + + @Native public ArrayValue keys(CallContext ctx, Object obj, boolean onlyString) throws InterruptedException { + var res = new ArrayValue(); + + var i = 0; + var list = Values.getMembers(ctx, obj, true, false); + + for (var el : list) res.set(ctx, i++, el); + + return res; + } + @Native public ArrayValue ownPropKeys(CallContext ctx, Object obj, boolean symbols) throws InterruptedException { + var res = new ArrayValue(); + + if (Values.isObject(obj)) { + var i = 0; + var list = Values.object(obj).keys(true); + + for (var el : list) res.set(ctx, i++, el); + } + + return res; + } + @Native public ObjectValue ownProp(CallContext ctx, ObjectValue val, Object key) throws InterruptedException { + return val.getMemberDescriptor(ctx, key); + } + @Native public void lock(ObjectValue val, String type) { + switch (type) { + case "ext": val.preventExtensions(); break; + case "seal": val.seal(); break; + case "freeze": val.freeze(); break; + } + } + @Native public boolean extensible(ObjectValue val) { + return val.extensible(); + } + + @Native public void sort(CallContext ctx, ArrayValue arr, FunctionValue cmp) { arr.sort((a, b) -> { try { var res = Values.toNumber(ctx, cmp.call(ctx, null, a, b)); @@ -158,109 +138,4 @@ public class Internals { } }); } - - @Native - public void special(FunctionValue... funcs) { - for (var func : funcs) { - func.special = true; - } - } - - @Native - public Symbol symbol(String name, boolean unique) { - if (!unique && symbols.containsKey(name)) { - return symbols.get(name); - } - else { - var val = new Symbol(name); - if (!unique) symbols.put(name, val); - return val; - } - } - @Native - public String symStr(Symbol symbol) { - return symbol.value; - } - - @Native - public void freeze(ObjectValue val) { - val.freeze(); - } - @Native - public void seal(ObjectValue val) { - val.seal(); - } - @Native - public void preventExtensions(ObjectValue val) { - val.preventExtensions(); - } - - @Native - public boolean extensible(Object val) { - return Values.isObject(val) && Values.object(val).extensible(); - } - - @Native - public ArrayValue keys(CallContext ctx, Object obj, boolean onlyString) throws InterruptedException { - var res = new ArrayValue(); - - var i = 0; - var list = Values.getMembers(ctx, obj, true, false); - - for (var el : list) { - if (el instanceof Symbol && onlyString) continue; - res.set(ctx, i++, el); - } - - return res; - } - @Native - public ArrayValue ownPropKeys(CallContext ctx, Object obj, boolean symbols) throws InterruptedException { - var res = new ArrayValue(); - - if (Values.isObject(obj)) { - var i = 0; - var list = Values.object(obj).keys(true); - - for (var el : list) { - if (el instanceof Symbol == symbols) res.set(ctx, i++, el); - } - } - - return res; - } - @Native - public ObjectValue ownProp(CallContext ctx, ObjectValue val, Object key) throws InterruptedException { - return val.getMemberDescriptor(ctx, key); - } - - @Native - public Object require(CallContext ctx, Object name) throws InterruptedException { - var res = ctx.engine().modules().tryLoad(ctx, Values.toString(ctx, name)); - if (res == null) throw EngineException.ofError("The module '" + name + "' doesn\'t exist."); - return res.exports(); - } - - @Native - public GeneratorFunction makeGenerator(FunctionValue func) { - if (func instanceof CodeFunction) return new GeneratorFunction((CodeFunction)func); - else throw EngineException.ofType("Can't create a generator with a non-js function."); - } - - @NativeGetter("err") - public ObjectValue errProto(CallContext ctx) { - return ctx.engine.errorProto(); - } - @NativeGetter("syntax") - public ObjectValue syntaxProto(CallContext ctx) { - return ctx.engine.syntaxErrorProto(); - } - @NativeGetter("range") - public ObjectValue rangeProto(CallContext ctx) { - return ctx.engine.rangeErrorProto(); - } - @NativeGetter("type") - public ObjectValue typeProto(CallContext ctx) { - return ctx.engine.typeErrorProto(); - } } diff --git a/src/me/topchetoeu/jscript/polyfills/PolyfillEngine.java b/src/me/topchetoeu/jscript/polyfills/PolyfillEngine.java- similarity index 97% rename from src/me/topchetoeu/jscript/polyfills/PolyfillEngine.java rename to src/me/topchetoeu/jscript/polyfills/PolyfillEngine.java- index f3a7706..0ea635e 100644 --- a/src/me/topchetoeu/jscript/polyfills/PolyfillEngine.java +++ b/src/me/topchetoeu/jscript/polyfills/PolyfillEngine.java- @@ -1,105 +1,105 @@ -package me.topchetoeu.jscript.polyfills; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; - -import me.topchetoeu.jscript.engine.Engine; -import me.topchetoeu.jscript.engine.modules.ModuleManager; - -public class PolyfillEngine extends Engine { - public static String streamToString(InputStream in) { - try { - StringBuilder out = new StringBuilder(); - BufferedReader br = new BufferedReader(new InputStreamReader(in)); - - for(var line = br.readLine(); line != null; line = br.readLine()) { - out.append(line).append('\n'); - } - - br.close(); - return out.toString(); - } - catch (IOException e) { - return null; - } - } - public static String resourceToString(String name) { - var str = PolyfillEngine.class.getResourceAsStream("/me/topchetoeu/jscript/" + name); - if (str == null) return null; - return streamToString(str); - } - - public final ModuleManager modules; - - @Override - public Object makeRegex(String pattern, String flags) { - return new RegExp(pattern, flags); - } - - @Override - public ModuleManager modules() { - return modules; - } - public PolyfillEngine(File root) { - super(); - - this.modules = new ModuleManager(root); - - // exposeNamespace("Math", Math.class); - // exposeNamespace("JSON", JSON.class); - // exposeClass("Promise", Promise.class); - // exposeClass("RegExp", RegExp.class); - // exposeClass("Date", Date.class); - // exposeClass("Map", Map.class); - // exposeClass("Set", Set.class); - - // global().define("Object", "Function", "String", "Number", "Boolean", "Symbol"); - // global().define("Array", "require"); - // global().define("Error", "SyntaxError", "TypeError", "RangeError"); - // global().define("setTimeout", "setInterval", "clearTimeout", "clearInterval"); - // global().define("debugger"); - - // global().define(true, new NativeFunction("measure", (ctx, thisArg, values) -> { - // var start = System.nanoTime(); - // try { - // return Values.call(ctx, values[0], ctx); - // } - // finally { - // System.out.println(String.format("Function took %s ms", (System.nanoTime() - start) / 1000000.)); - // } - // })); - // global().define(true, new NativeFunction("isNaN", (ctx, thisArg, args) -> { - // if (args.length == 0) return true; - // else return Double.isNaN(Values.toNumber(ctx, args[0])); - // })); - // global().define(true, new NativeFunction("log", (el, t, args) -> { - // for (var obj : args) Values.printValue(el, obj); - // System.out.println(); - // return null; - // })); - - // var scope = global().globalChild(); - // scope.define("gt", true, global().obj); - // scope.define("lgt", true, scope.obj); - // scope.define("setProps", "setConstr"); - // scope.define("internals", true, new Internals()); - // scope.define(true, new NativeFunction("run", (ctx, thisArg, args) -> { - // var filename = (String)args[0]; - // boolean pollute = args.length > 1 && args[1].equals(true); - // FunctionValue func; - // var src = resourceToString("js/" + filename); - // if (src == null) throw new RuntimeException("The source '" + filename + "' doesn't exist."); - - // if (pollute) func = Parsing.compile(global(), filename, src); - // else func = Parsing.compile(scope.globalChild(), filename, src); - - // func.call(ctx); - // return null; - // })); - - // pushMsg(false, scope.globalChild(), java.util.Map.of(), "core.js", resourceToString("js/core.js"), null); - } -} +package me.topchetoeu.jscript.polyfills; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import me.topchetoeu.jscript.engine.Engine; +import me.topchetoeu.jscript.engine.modules.ModuleManager; + +public class PolyfillEngine extends Engine { + public static String streamToString(InputStream in) { + try { + StringBuilder out = new StringBuilder(); + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + + for(var line = br.readLine(); line != null; line = br.readLine()) { + out.append(line).append('\n'); + } + + br.close(); + return out.toString(); + } + catch (IOException e) { + return null; + } + } + public static String resourceToString(String name) { + var str = PolyfillEngine.class.getResourceAsStream("/me/topchetoeu/jscript/" + name); + if (str == null) return null; + return streamToString(str); + } + + public final ModuleManager modules; + + @Override + public Object makeRegex(String pattern, String flags) { + return new RegExp(pattern, flags); + } + + @Override + public ModuleManager modules() { + return modules; + } + public PolyfillEngine(File root) { + super(); + + this.modules = new ModuleManager(root); + + // exposeNamespace("Math", Math.class); + // exposeNamespace("JSON", JSON.class); + // exposeClass("Promise", Promise.class); + // exposeClass("RegExp", RegExp.class); + // exposeClass("Date", Date.class); + // exposeClass("Map", Map.class); + // exposeClass("Set", Set.class); + + // global().define("Object", "Function", "String", "Number", "Boolean", "Symbol"); + // global().define("Array", "require"); + // global().define("Error", "SyntaxError", "TypeError", "RangeError"); + // global().define("setTimeout", "setInterval", "clearTimeout", "clearInterval"); + // global().define("debugger"); + + // global().define(true, new NativeFunction("measure", (ctx, thisArg, values) -> { + // var start = System.nanoTime(); + // try { + // return Values.call(ctx, values[0], ctx); + // } + // finally { + // System.out.println(String.format("Function took %s ms", (System.nanoTime() - start) / 1000000.)); + // } + // })); + // global().define(true, new NativeFunction("isNaN", (ctx, thisArg, args) -> { + // if (args.length == 0) return true; + // else return Double.isNaN(Values.toNumber(ctx, args[0])); + // })); + // global().define(true, new NativeFunction("log", (el, t, args) -> { + // for (var obj : args) Values.printValue(el, obj); + // System.out.println(); + // return null; + // })); + + // var scope = global().globalChild(); + // scope.define("gt", true, global().obj); + // scope.define("lgt", true, scope.obj); + // scope.define("setProps", "setConstr"); + // scope.define("internals", true, new Internals()); + // scope.define(true, new NativeFunction("run", (ctx, thisArg, args) -> { + // var filename = (String)args[0]; + // boolean pollute = args.length > 1 && args[1].equals(true); + // FunctionValue func; + // var src = resourceToString("js/" + filename); + // if (src == null) throw new RuntimeException("The source '" + filename + "' doesn't exist."); + + // if (pollute) func = Parsing.compile(global(), filename, src); + // else func = Parsing.compile(scope.globalChild(), filename, src); + + // func.call(ctx); + // return null; + // })); + + // pushMsg(false, scope.globalChild(), java.util.Map.of(), "core.js", resourceToString("js/core.js"), null); + } +} diff --git a/src/me/topchetoeu/jscript/polyfills/Promise.java b/src/me/topchetoeu/jscript/polyfills/Promise.java- similarity index 97% rename from src/me/topchetoeu/jscript/polyfills/Promise.java rename to src/me/topchetoeu/jscript/polyfills/Promise.java- index 6a4481e..f911f9b 100644 --- a/src/me/topchetoeu/jscript/polyfills/Promise.java +++ b/src/me/topchetoeu/jscript/polyfills/Promise.java- @@ -1,360 +1,360 @@ -package me.topchetoeu.jscript.polyfills; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import me.topchetoeu.jscript.engine.CallContext; -import me.topchetoeu.jscript.engine.values.ArrayValue; -import me.topchetoeu.jscript.engine.values.FunctionValue; -import me.topchetoeu.jscript.engine.values.NativeFunction; -import me.topchetoeu.jscript.engine.values.ObjectValue; -import me.topchetoeu.jscript.engine.values.Values; -import me.topchetoeu.jscript.exceptions.EngineException; -import me.topchetoeu.jscript.interop.Native; - -public class Promise { - private static class Handle { - public final CallContext ctx; - public final FunctionValue fulfilled; - public final FunctionValue rejected; - - public Handle(CallContext ctx, FunctionValue fulfilled, FunctionValue rejected) { - this.ctx = ctx; - this.fulfilled = fulfilled; - this.rejected = rejected; - } - } - - @Native("resolve") - public static Promise ofResolved(CallContext ctx, Object val) { - if (Values.isWrapper(val, Promise.class)) return Values.wrapper(val, Promise.class); - var res = new Promise(); - res.fulfill(ctx, val); - return res; - } - public static Promise ofResolved(Object val) { - if (Values.isWrapper(val, Promise.class)) return Values.wrapper(val, Promise.class); - var res = new Promise(); - res.fulfill(val); - return res; - } - - @Native("reject") - public static Promise ofRejected(CallContext ctx, Object val) { - var res = new Promise(); - res.reject(ctx, val); - return res; - } - public static Promise ofRejected(Object val) { - var res = new Promise(); - res.fulfill(val); - return res; - } - - @Native - public static Promise any(CallContext ctx, Object _promises) { - if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); - var promises = Values.array(_promises); - if (promises.size() == 0) return ofResolved(new ArrayValue()); - var n = new int[] { promises.size() }; - var res = new Promise(); - - var errors = new ArrayValue(); - - for (var i = 0; i < promises.size(); i++) { - var index = i; - var val = promises.get(i); - if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then( - ctx, - new NativeFunction(null, (e, th, args) -> { res.fulfill(e, args[0]); return null; }), - new NativeFunction(null, (e, th, args) -> { - errors.set(ctx, index, args[0]); - n[0]--; - if (n[0] <= 0) res.reject(e, errors); - return null; - }) - ); - else { - res.fulfill(ctx, val); - break; - } - } - - return res; - } - @Native - public static Promise race(CallContext ctx, Object _promises) { - if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); - var promises = Values.array(_promises); - if (promises.size() == 0) return ofResolved(new ArrayValue()); - var res = new Promise(); - - for (var i = 0; i < promises.size(); i++) { - var val = promises.get(i); - if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then( - ctx, - new NativeFunction(null, (e, th, args) -> { res.fulfill(e, args[0]); return null; }), - new NativeFunction(null, (e, th, args) -> { res.reject(e, args[0]); return null; }) - ); - else { - res.fulfill(val); - break; - } - } - - return res; - } - @Native - public static Promise all(CallContext ctx, Object _promises) { - if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); - var promises = Values.array(_promises); - if (promises.size() == 0) return ofResolved(new ArrayValue()); - var n = new int[] { promises.size() }; - var res = new Promise(); - - var result = new ArrayValue(); - - for (var i = 0; i < promises.size(); i++) { - var index = i; - var val = promises.get(i); - if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then( - ctx, - new NativeFunction(null, (e, th, args) -> { - result.set(ctx, index, args[0]); - n[0]--; - if (n[0] <= 0) res.fulfill(e, result); - return null; - }), - new NativeFunction(null, (e, th, args) -> { res.reject(e, args[0]); return null; }) - ); - else { - result.set(ctx, i, val); - break; - } - } - - if (n[0] <= 0) res.fulfill(ctx, result); - - return res; - } - @Native - public static Promise allSettled(CallContext ctx, Object _promises) { - if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); - var promises = Values.array(_promises); - if (promises.size() == 0) return ofResolved(new ArrayValue()); - var n = new int[] { promises.size() }; - var res = new Promise(); - - var result = new ArrayValue(); - - for (var i = 0; i < promises.size(); i++) { - var index = i; - var val = promises.get(i); - if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then( - ctx, - new NativeFunction(null, (e, th, args) -> { - result.set(ctx, index, new ObjectValue(ctx, Map.of( - "status", "fulfilled", - "value", args[0] - ))); - n[0]--; - if (n[0] <= 0) res.fulfill(e, result); - return null; - }), - new NativeFunction(null, (e, th, args) -> { - result.set(ctx, index, new ObjectValue(ctx, Map.of( - "status", "rejected", - "reason", args[0] - ))); - n[0]--; - if (n[0] <= 0) res.fulfill(e, result); - return null; - }) - ); - else { - result.set(ctx, i, new ObjectValue(ctx, Map.of( - "status", "fulfilled", - "value", val - ))); - n[0]--; - } - } - - if (n[0] <= 0) res.fulfill(ctx, result); - - return res; - } - - private List handles = new ArrayList<>(); - - private static final int STATE_PENDING = 0; - private static final int STATE_FULFILLED = 1; - private static final int STATE_REJECTED = 2; - - private int state = STATE_PENDING; - private Object val; - - /** - * Thread safe - call from any thread - */ - public void fulfill(Object val) { - if (Values.isWrapper(val, Promise.class)) throw new IllegalArgumentException("A promise may not be a fulfil value."); - if (state != STATE_PENDING) return; - - this.state = STATE_FULFILLED; - this.val = val; - for (var el : handles) el.ctx.engine().pushMsg(true, el.fulfilled, el.ctx.data(), null, val); - handles = null; - } - /** - * Thread safe - call from any thread - */ - public void fulfill(CallContext ctx, Object val) { - if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then(ctx, - new NativeFunction(null, (e, th, args) -> { - this.fulfill(e, args[0]); - return null; - }), - new NativeFunction(null, (e, th, args) -> { - this.reject(e, args[0]); - return null; - }) - ); - else this.fulfill(val); - } - /** - * Thread safe - call from any thread - */ - public void reject(Object val) { - if (Values.isWrapper(val, Promise.class)) throw new IllegalArgumentException("A promise may not be a reject value."); - if (state != STATE_PENDING) return; - - this.state = STATE_REJECTED; - this.val = val; - for (var el : handles) el.ctx.engine().pushMsg(true, el.rejected, el.ctx.data(), null, val); - handles = null; - } - /** - * Thread safe - call from any thread - */ - public void reject(CallContext ctx, Object val) { - if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then(ctx, - new NativeFunction(null, (e, th, args) -> { - this.reject(e, args[0]); - return null; - }), - new NativeFunction(null, (e, th, args) -> { - this.reject(e, args[0]); - return null; - }) - ); - else this.reject(val); - } - - private void handle(CallContext ctx, FunctionValue fulfill, FunctionValue reject) { - if (state == STATE_FULFILLED) ctx.engine().pushMsg(true, fulfill, ctx.data(), null, val); - else if (state == STATE_REJECTED) ctx.engine().pushMsg(true, reject, ctx.data(), null, val); - else handles.add(new Handle(ctx, fulfill, reject)); - } - - /** - * Thread safe - you can call this from anywhere - * HOWEVER, it's strongly recommended to use this only in javascript - */ - @Native - public Promise then(CallContext ctx, Object onFulfill, Object onReject) { - if (!(onFulfill instanceof FunctionValue)) onFulfill = null; - if (!(onReject instanceof FunctionValue)) onReject = null; - - var res = new Promise(); - - var fulfill = (FunctionValue)onFulfill; - var reject = (FunctionValue)onReject; - - handle(ctx, - new NativeFunction(null, (e, th, args) -> { - if (fulfill == null) res.fulfill(e, args[0]); - else { - try { res.fulfill(e, fulfill.call(e, null, args[0])); } - catch (EngineException err) { res.reject(e, err.value); } - } - return null; - }), - new NativeFunction(null, (e, th, args) -> { - if (reject == null) res.reject(e, args[0]); - else { - try { res.fulfill(e, reject.call(e, null, args[0])); } - catch (EngineException err) { res.reject(e, err.value); } - } - return null; - }) - ); - - return res; - } - /** - * Thread safe - you can call this from anywhere - * HOWEVER, it's strongly recommended to use this only in javascript - */ - @Native("catch") - public Promise _catch(CallContext ctx, Object onReject) { - return then(ctx, null, onReject); - } - /** - * Thread safe - you can call this from anywhere - * HOWEVER, it's strongly recommended to use this only in javascript - */ - @Native("finally") - public Promise _finally(CallContext ctx, Object handle) { - return then(ctx, - new NativeFunction(null, (e, th, args) -> { - if (handle instanceof FunctionValue) ((FunctionValue)handle).call(ctx); - return args[0]; - }), - new NativeFunction(null, (e, th, args) -> { - if (handle instanceof FunctionValue) ((FunctionValue)handle).call(ctx); - throw new EngineException(args[0]); - }) - ); - } - - /** - * NOT THREAD SAFE - must be called from the engine executor thread - */ - @Native - public Promise(CallContext ctx, FunctionValue func) throws InterruptedException { - if (!(func instanceof FunctionValue)) throw EngineException.ofType("A function must be passed to the promise constructor."); - try { - func.call( - ctx, null, - new NativeFunction(null, (e, th, args) -> { - fulfill(e, args.length > 0 ? args[0] : null); - return null; - }), - new NativeFunction(null, (e, th, args) -> { - reject(e, args.length > 0 ? args[0] : null); - return null; - }) - ); - } - catch (EngineException e) { - reject(ctx, e.value); - } - } - - @Override @Native - public String toString() { - if (state == STATE_PENDING) return "Promise (pending)"; - else if (state == STATE_FULFILLED) return "Promise (fulfilled)"; - else return "Promise (rejected)"; - } - - private Promise(int state, Object val) { - this.state = state; - this.val = val; - } - public Promise() { - this(STATE_PENDING, null); - } -} +package me.topchetoeu.jscript.polyfills; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.values.ArrayValue; +import me.topchetoeu.jscript.engine.values.FunctionValue; +import me.topchetoeu.jscript.engine.values.NativeFunction; +import me.topchetoeu.jscript.engine.values.ObjectValue; +import me.topchetoeu.jscript.engine.values.Values; +import me.topchetoeu.jscript.exceptions.EngineException; +import me.topchetoeu.jscript.interop.Native; + +public class Promise { + private static class Handle { + public final CallContext ctx; + public final FunctionValue fulfilled; + public final FunctionValue rejected; + + public Handle(CallContext ctx, FunctionValue fulfilled, FunctionValue rejected) { + this.ctx = ctx; + this.fulfilled = fulfilled; + this.rejected = rejected; + } + } + + @Native("resolve") + public static Promise ofResolved(CallContext ctx, Object val) { + if (Values.isWrapper(val, Promise.class)) return Values.wrapper(val, Promise.class); + var res = new Promise(); + res.fulfill(ctx, val); + return res; + } + public static Promise ofResolved(Object val) { + if (Values.isWrapper(val, Promise.class)) return Values.wrapper(val, Promise.class); + var res = new Promise(); + res.fulfill(val); + return res; + } + + @Native("reject") + public static Promise ofRejected(CallContext ctx, Object val) { + var res = new Promise(); + res.reject(ctx, val); + return res; + } + public static Promise ofRejected(Object val) { + var res = new Promise(); + res.fulfill(val); + return res; + } + + @Native + public static Promise any(CallContext ctx, Object _promises) { + if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); + var promises = Values.array(_promises); + if (promises.size() == 0) return ofResolved(new ArrayValue()); + var n = new int[] { promises.size() }; + var res = new Promise(); + + var errors = new ArrayValue(); + + for (var i = 0; i < promises.size(); i++) { + var index = i; + var val = promises.get(i); + if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then( + ctx, + new NativeFunction(null, (e, th, args) -> { res.fulfill(e, args[0]); return null; }), + new NativeFunction(null, (e, th, args) -> { + errors.set(ctx, index, args[0]); + n[0]--; + if (n[0] <= 0) res.reject(e, errors); + return null; + }) + ); + else { + res.fulfill(ctx, val); + break; + } + } + + return res; + } + @Native + public static Promise race(CallContext ctx, Object _promises) { + if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); + var promises = Values.array(_promises); + if (promises.size() == 0) return ofResolved(new ArrayValue()); + var res = new Promise(); + + for (var i = 0; i < promises.size(); i++) { + var val = promises.get(i); + if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then( + ctx, + new NativeFunction(null, (e, th, args) -> { res.fulfill(e, args[0]); return null; }), + new NativeFunction(null, (e, th, args) -> { res.reject(e, args[0]); return null; }) + ); + else { + res.fulfill(val); + break; + } + } + + return res; + } + @Native + public static Promise all(CallContext ctx, Object _promises) { + if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); + var promises = Values.array(_promises); + if (promises.size() == 0) return ofResolved(new ArrayValue()); + var n = new int[] { promises.size() }; + var res = new Promise(); + + var result = new ArrayValue(); + + for (var i = 0; i < promises.size(); i++) { + var index = i; + var val = promises.get(i); + if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then( + ctx, + new NativeFunction(null, (e, th, args) -> { + result.set(ctx, index, args[0]); + n[0]--; + if (n[0] <= 0) res.fulfill(e, result); + return null; + }), + new NativeFunction(null, (e, th, args) -> { res.reject(e, args[0]); return null; }) + ); + else { + result.set(ctx, i, val); + break; + } + } + + if (n[0] <= 0) res.fulfill(ctx, result); + + return res; + } + @Native + public static Promise allSettled(CallContext ctx, Object _promises) { + if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); + var promises = Values.array(_promises); + if (promises.size() == 0) return ofResolved(new ArrayValue()); + var n = new int[] { promises.size() }; + var res = new Promise(); + + var result = new ArrayValue(); + + for (var i = 0; i < promises.size(); i++) { + var index = i; + var val = promises.get(i); + if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then( + ctx, + new NativeFunction(null, (e, th, args) -> { + result.set(ctx, index, new ObjectValue(ctx, Map.of( + "status", "fulfilled", + "value", args[0] + ))); + n[0]--; + if (n[0] <= 0) res.fulfill(e, result); + return null; + }), + new NativeFunction(null, (e, th, args) -> { + result.set(ctx, index, new ObjectValue(ctx, Map.of( + "status", "rejected", + "reason", args[0] + ))); + n[0]--; + if (n[0] <= 0) res.fulfill(e, result); + return null; + }) + ); + else { + result.set(ctx, i, new ObjectValue(ctx, Map.of( + "status", "fulfilled", + "value", val + ))); + n[0]--; + } + } + + if (n[0] <= 0) res.fulfill(ctx, result); + + return res; + } + + private List handles = new ArrayList<>(); + + private static final int STATE_PENDING = 0; + private static final int STATE_FULFILLED = 1; + private static final int STATE_REJECTED = 2; + + private int state = STATE_PENDING; + private Object val; + + /** + * Thread safe - call from any thread + */ + public void fulfill(Object val) { + if (Values.isWrapper(val, Promise.class)) throw new IllegalArgumentException("A promise may not be a fulfil value."); + if (state != STATE_PENDING) return; + + this.state = STATE_FULFILLED; + this.val = val; + for (var el : handles) el.ctx.engine().pushMsg(true, el.fulfilled, el.ctx.data(), null, val); + handles = null; + } + /** + * Thread safe - call from any thread + */ + public void fulfill(CallContext ctx, Object val) { + if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then(ctx, + new NativeFunction(null, (e, th, args) -> { + this.fulfill(e, args[0]); + return null; + }), + new NativeFunction(null, (e, th, args) -> { + this.reject(e, args[0]); + return null; + }) + ); + else this.fulfill(val); + } + /** + * Thread safe - call from any thread + */ + public void reject(Object val) { + if (Values.isWrapper(val, Promise.class)) throw new IllegalArgumentException("A promise may not be a reject value."); + if (state != STATE_PENDING) return; + + this.state = STATE_REJECTED; + this.val = val; + for (var el : handles) el.ctx.engine().pushMsg(true, el.rejected, el.ctx.data(), null, val); + handles = null; + } + /** + * Thread safe - call from any thread + */ + public void reject(CallContext ctx, Object val) { + if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then(ctx, + new NativeFunction(null, (e, th, args) -> { + this.reject(e, args[0]); + return null; + }), + new NativeFunction(null, (e, th, args) -> { + this.reject(e, args[0]); + return null; + }) + ); + else this.reject(val); + } + + private void handle(CallContext ctx, FunctionValue fulfill, FunctionValue reject) { + if (state == STATE_FULFILLED) ctx.engine().pushMsg(true, fulfill, ctx.data(), null, val); + else if (state == STATE_REJECTED) ctx.engine().pushMsg(true, reject, ctx.data(), null, val); + else handles.add(new Handle(ctx, fulfill, reject)); + } + + /** + * Thread safe - you can call this from anywhere + * HOWEVER, it's strongly recommended to use this only in javascript + */ + @Native + public Promise then(CallContext ctx, Object onFulfill, Object onReject) { + if (!(onFulfill instanceof FunctionValue)) onFulfill = null; + if (!(onReject instanceof FunctionValue)) onReject = null; + + var res = new Promise(); + + var fulfill = (FunctionValue)onFulfill; + var reject = (FunctionValue)onReject; + + handle(ctx, + new NativeFunction(null, (e, th, args) -> { + if (fulfill == null) res.fulfill(e, args[0]); + else { + try { res.fulfill(e, fulfill.call(e, null, args[0])); } + catch (EngineException err) { res.reject(e, err.value); } + } + return null; + }), + new NativeFunction(null, (e, th, args) -> { + if (reject == null) res.reject(e, args[0]); + else { + try { res.fulfill(e, reject.call(e, null, args[0])); } + catch (EngineException err) { res.reject(e, err.value); } + } + return null; + }) + ); + + return res; + } + /** + * Thread safe - you can call this from anywhere + * HOWEVER, it's strongly recommended to use this only in javascript + */ + @Native("catch") + public Promise _catch(CallContext ctx, Object onReject) { + return then(ctx, null, onReject); + } + /** + * Thread safe - you can call this from anywhere + * HOWEVER, it's strongly recommended to use this only in javascript + */ + @Native("finally") + public Promise _finally(CallContext ctx, Object handle) { + return then(ctx, + new NativeFunction(null, (e, th, args) -> { + if (handle instanceof FunctionValue) ((FunctionValue)handle).call(ctx); + return args[0]; + }), + new NativeFunction(null, (e, th, args) -> { + if (handle instanceof FunctionValue) ((FunctionValue)handle).call(ctx); + throw new EngineException(args[0]); + }) + ); + } + + /** + * NOT THREAD SAFE - must be called from the engine executor thread + */ + @Native + public Promise(CallContext ctx, FunctionValue func) throws InterruptedException { + if (!(func instanceof FunctionValue)) throw EngineException.ofType("A function must be passed to the promise constructor."); + try { + func.call( + ctx, null, + new NativeFunction(null, (e, th, args) -> { + fulfill(e, args.length > 0 ? args[0] : null); + return null; + }), + new NativeFunction(null, (e, th, args) -> { + reject(e, args.length > 0 ? args[0] : null); + return null; + }) + ); + } + catch (EngineException e) { + reject(ctx, e.value); + } + } + + @Override @Native + public String toString() { + if (state == STATE_PENDING) return "Promise (pending)"; + else if (state == STATE_FULFILLED) return "Promise (fulfilled)"; + else return "Promise (rejected)"; + } + + private Promise(int state, Object val) { + this.state = state; + this.val = val; + } + public Promise() { + this(STATE_PENDING, null); + } +} -- 2.45.2 From 38656bd6545969adc1114b893b6689422b55586d Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Thu, 7 Sep 2023 16:35:49 +0300 Subject: [PATCH 04/14] fix: small issues --- Metadata.java | 7 + build.js | 2 +- lib/values/object.ts | 1 - meta.json | 3 +- .../jscript/filesystem/Filesystem.java | 2 +- .../filesystem/FilesystemRegister.java | 7 + src/me/topchetoeu/jscript/polyfills/Map.java | 86 ----- .../jscript/polyfills/Promise.java- | 360 ------------------ src/me/topchetoeu/jscript/polyfills/Set.java | 76 ---- 9 files changed, 18 insertions(+), 526 deletions(-) create mode 100644 Metadata.java create mode 100644 src/me/topchetoeu/jscript/filesystem/FilesystemRegister.java delete mode 100644 src/me/topchetoeu/jscript/polyfills/Map.java delete mode 100644 src/me/topchetoeu/jscript/polyfills/Promise.java- delete mode 100644 src/me/topchetoeu/jscript/polyfills/Set.java diff --git a/Metadata.java b/Metadata.java new file mode 100644 index 0000000..128bfaf --- /dev/null +++ b/Metadata.java @@ -0,0 +1,7 @@ +package me.topchetoeu.jscript; + +public class Metadata { + public static final String VERSION = "0.0.1-alpha"; + public static final String AUTHOR = "TopchetoEU"; + public static final String NAME = "java-jscript"; +} diff --git a/build.js b/build.js index 718963f..b6e9609 100644 --- a/build.js +++ b/build.js @@ -46,7 +46,7 @@ async function compileJava() { const args = ['-d', 'dst/classes', 'Metadata.java']; for await (const path of find('src', undefined, v => v.endsWith('.java') && !v.endsWith('Metadata.java'))) args.push(path); - await run('javac', ...args); + await run(conf.javahome + '/javac', ...args); await fs.rm('Metadata.java'); } diff --git a/lib/values/object.ts b/lib/values/object.ts index 4c8cdc3..277decd 100644 --- a/lib/values/object.ts +++ b/lib/values/object.ts @@ -1,4 +1,3 @@ -/** @internal */ define("values/object", () => { var Object = env.global.Object = function(arg: any) { if (arg === undefined || arg === null) return {}; diff --git a/meta.json b/meta.json index d888310..c573484 100644 --- a/meta.json +++ b/meta.json @@ -1,5 +1,6 @@ { "version": "0.0.1-alpha", "name": "java-jscript", - "author": "TopchetoEU" + "author": "TopchetoEU", + "javahome": "/usr/lib/jvm/java-8-openjdk-amd64/bin" } \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/filesystem/Filesystem.java b/src/me/topchetoeu/jscript/filesystem/Filesystem.java index eee941f..8986220 100644 --- a/src/me/topchetoeu/jscript/filesystem/Filesystem.java +++ b/src/me/topchetoeu/jscript/filesystem/Filesystem.java @@ -9,7 +9,7 @@ public interface Filesystem extends PermissionsProvider { FILE, FOLDER, } - + File open(Path path) throws IOException; EntryType type(Path path); boolean mkdir(Path path); diff --git a/src/me/topchetoeu/jscript/filesystem/FilesystemRegister.java b/src/me/topchetoeu/jscript/filesystem/FilesystemRegister.java new file mode 100644 index 0000000..03940f5 --- /dev/null +++ b/src/me/topchetoeu/jscript/filesystem/FilesystemRegister.java @@ -0,0 +1,7 @@ +package me.topchetoeu.jscript.filesystem; + +import java.util.HashMap; + +public class FilesystemRegister { + private HashMap filesystems = new HashMap<>(); +} diff --git a/src/me/topchetoeu/jscript/polyfills/Map.java b/src/me/topchetoeu/jscript/polyfills/Map.java deleted file mode 100644 index cbb8a2e..0000000 --- a/src/me/topchetoeu/jscript/polyfills/Map.java +++ /dev/null @@ -1,86 +0,0 @@ -package me.topchetoeu.jscript.polyfills; - -import java.util.LinkedHashMap; -import java.util.stream.Collectors; - -import me.topchetoeu.jscript.engine.CallContext; -import me.topchetoeu.jscript.engine.values.ArrayValue; -import me.topchetoeu.jscript.engine.values.FunctionValue; -import me.topchetoeu.jscript.engine.values.Values; -import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeGetter; - -public class Map { - private LinkedHashMap objs = new LinkedHashMap<>(); - - @Native - public Object get(Object key) { - return objs.get(key); - } - @Native - public Map set(Object key, Object val) { - objs.put(key, val); - return this; - } - @Native - public boolean delete(Object key) { - if (objs.containsKey(key)) { - objs.remove(key); - return true; - } - else return false; - } - @Native - public boolean has(Object key) { - return objs.containsKey(key); - } - - @Native - public void clear() { - objs.clear(); - } - - @Native - public void forEach(CallContext ctx, FunctionValue func, Object thisArg) throws InterruptedException { - - for (var el : objs.entrySet().stream().collect(Collectors.toList())) { - func.call(ctx, thisArg, el.getValue(), el.getKey(), this); - } - } - - @Native - public Object entries(CallContext ctx) throws InterruptedException { - return Values.fromJavaIterable(ctx, objs - .entrySet() - .stream() - .map(v -> new ArrayValue(ctx, v.getKey(), v.getValue())) - .collect(Collectors.toList()) - ); - } - @Native - public Object keys(CallContext ctx) throws InterruptedException { - return Values.fromJavaIterable(ctx, objs.keySet().stream().collect(Collectors.toList())); - } - @Native - public Object values(CallContext ctx) throws InterruptedException { - return Values.fromJavaIterable(ctx, objs.values().stream().collect(Collectors.toList())); - } - - @NativeGetter("size") - public int size() { - return objs.size(); - } - - @Native - public Map(CallContext ctx, Object iterable) throws InterruptedException { - if (Values.isPrimitive(iterable)) return; - - for (var val : Values.toJavaIterable(ctx, iterable)) { - var first = Values.getMember(ctx, val, 0); - var second = Values.getMember(ctx, val, 1); - - set(first, second); - } - } - public Map() { } -} diff --git a/src/me/topchetoeu/jscript/polyfills/Promise.java- b/src/me/topchetoeu/jscript/polyfills/Promise.java- deleted file mode 100644 index f911f9b..0000000 --- a/src/me/topchetoeu/jscript/polyfills/Promise.java- +++ /dev/null @@ -1,360 +0,0 @@ -package me.topchetoeu.jscript.polyfills; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import me.topchetoeu.jscript.engine.CallContext; -import me.topchetoeu.jscript.engine.values.ArrayValue; -import me.topchetoeu.jscript.engine.values.FunctionValue; -import me.topchetoeu.jscript.engine.values.NativeFunction; -import me.topchetoeu.jscript.engine.values.ObjectValue; -import me.topchetoeu.jscript.engine.values.Values; -import me.topchetoeu.jscript.exceptions.EngineException; -import me.topchetoeu.jscript.interop.Native; - -public class Promise { - private static class Handle { - public final CallContext ctx; - public final FunctionValue fulfilled; - public final FunctionValue rejected; - - public Handle(CallContext ctx, FunctionValue fulfilled, FunctionValue rejected) { - this.ctx = ctx; - this.fulfilled = fulfilled; - this.rejected = rejected; - } - } - - @Native("resolve") - public static Promise ofResolved(CallContext ctx, Object val) { - if (Values.isWrapper(val, Promise.class)) return Values.wrapper(val, Promise.class); - var res = new Promise(); - res.fulfill(ctx, val); - return res; - } - public static Promise ofResolved(Object val) { - if (Values.isWrapper(val, Promise.class)) return Values.wrapper(val, Promise.class); - var res = new Promise(); - res.fulfill(val); - return res; - } - - @Native("reject") - public static Promise ofRejected(CallContext ctx, Object val) { - var res = new Promise(); - res.reject(ctx, val); - return res; - } - public static Promise ofRejected(Object val) { - var res = new Promise(); - res.fulfill(val); - return res; - } - - @Native - public static Promise any(CallContext ctx, Object _promises) { - if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); - var promises = Values.array(_promises); - if (promises.size() == 0) return ofResolved(new ArrayValue()); - var n = new int[] { promises.size() }; - var res = new Promise(); - - var errors = new ArrayValue(); - - for (var i = 0; i < promises.size(); i++) { - var index = i; - var val = promises.get(i); - if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then( - ctx, - new NativeFunction(null, (e, th, args) -> { res.fulfill(e, args[0]); return null; }), - new NativeFunction(null, (e, th, args) -> { - errors.set(ctx, index, args[0]); - n[0]--; - if (n[0] <= 0) res.reject(e, errors); - return null; - }) - ); - else { - res.fulfill(ctx, val); - break; - } - } - - return res; - } - @Native - public static Promise race(CallContext ctx, Object _promises) { - if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); - var promises = Values.array(_promises); - if (promises.size() == 0) return ofResolved(new ArrayValue()); - var res = new Promise(); - - for (var i = 0; i < promises.size(); i++) { - var val = promises.get(i); - if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then( - ctx, - new NativeFunction(null, (e, th, args) -> { res.fulfill(e, args[0]); return null; }), - new NativeFunction(null, (e, th, args) -> { res.reject(e, args[0]); return null; }) - ); - else { - res.fulfill(val); - break; - } - } - - return res; - } - @Native - public static Promise all(CallContext ctx, Object _promises) { - if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); - var promises = Values.array(_promises); - if (promises.size() == 0) return ofResolved(new ArrayValue()); - var n = new int[] { promises.size() }; - var res = new Promise(); - - var result = new ArrayValue(); - - for (var i = 0; i < promises.size(); i++) { - var index = i; - var val = promises.get(i); - if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then( - ctx, - new NativeFunction(null, (e, th, args) -> { - result.set(ctx, index, args[0]); - n[0]--; - if (n[0] <= 0) res.fulfill(e, result); - return null; - }), - new NativeFunction(null, (e, th, args) -> { res.reject(e, args[0]); return null; }) - ); - else { - result.set(ctx, i, val); - break; - } - } - - if (n[0] <= 0) res.fulfill(ctx, result); - - return res; - } - @Native - public static Promise allSettled(CallContext ctx, Object _promises) { - if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); - var promises = Values.array(_promises); - if (promises.size() == 0) return ofResolved(new ArrayValue()); - var n = new int[] { promises.size() }; - var res = new Promise(); - - var result = new ArrayValue(); - - for (var i = 0; i < promises.size(); i++) { - var index = i; - var val = promises.get(i); - if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then( - ctx, - new NativeFunction(null, (e, th, args) -> { - result.set(ctx, index, new ObjectValue(ctx, Map.of( - "status", "fulfilled", - "value", args[0] - ))); - n[0]--; - if (n[0] <= 0) res.fulfill(e, result); - return null; - }), - new NativeFunction(null, (e, th, args) -> { - result.set(ctx, index, new ObjectValue(ctx, Map.of( - "status", "rejected", - "reason", args[0] - ))); - n[0]--; - if (n[0] <= 0) res.fulfill(e, result); - return null; - }) - ); - else { - result.set(ctx, i, new ObjectValue(ctx, Map.of( - "status", "fulfilled", - "value", val - ))); - n[0]--; - } - } - - if (n[0] <= 0) res.fulfill(ctx, result); - - return res; - } - - private List handles = new ArrayList<>(); - - private static final int STATE_PENDING = 0; - private static final int STATE_FULFILLED = 1; - private static final int STATE_REJECTED = 2; - - private int state = STATE_PENDING; - private Object val; - - /** - * Thread safe - call from any thread - */ - public void fulfill(Object val) { - if (Values.isWrapper(val, Promise.class)) throw new IllegalArgumentException("A promise may not be a fulfil value."); - if (state != STATE_PENDING) return; - - this.state = STATE_FULFILLED; - this.val = val; - for (var el : handles) el.ctx.engine().pushMsg(true, el.fulfilled, el.ctx.data(), null, val); - handles = null; - } - /** - * Thread safe - call from any thread - */ - public void fulfill(CallContext ctx, Object val) { - if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then(ctx, - new NativeFunction(null, (e, th, args) -> { - this.fulfill(e, args[0]); - return null; - }), - new NativeFunction(null, (e, th, args) -> { - this.reject(e, args[0]); - return null; - }) - ); - else this.fulfill(val); - } - /** - * Thread safe - call from any thread - */ - public void reject(Object val) { - if (Values.isWrapper(val, Promise.class)) throw new IllegalArgumentException("A promise may not be a reject value."); - if (state != STATE_PENDING) return; - - this.state = STATE_REJECTED; - this.val = val; - for (var el : handles) el.ctx.engine().pushMsg(true, el.rejected, el.ctx.data(), null, val); - handles = null; - } - /** - * Thread safe - call from any thread - */ - public void reject(CallContext ctx, Object val) { - if (Values.isWrapper(val, Promise.class)) Values.wrapper(val, Promise.class).then(ctx, - new NativeFunction(null, (e, th, args) -> { - this.reject(e, args[0]); - return null; - }), - new NativeFunction(null, (e, th, args) -> { - this.reject(e, args[0]); - return null; - }) - ); - else this.reject(val); - } - - private void handle(CallContext ctx, FunctionValue fulfill, FunctionValue reject) { - if (state == STATE_FULFILLED) ctx.engine().pushMsg(true, fulfill, ctx.data(), null, val); - else if (state == STATE_REJECTED) ctx.engine().pushMsg(true, reject, ctx.data(), null, val); - else handles.add(new Handle(ctx, fulfill, reject)); - } - - /** - * Thread safe - you can call this from anywhere - * HOWEVER, it's strongly recommended to use this only in javascript - */ - @Native - public Promise then(CallContext ctx, Object onFulfill, Object onReject) { - if (!(onFulfill instanceof FunctionValue)) onFulfill = null; - if (!(onReject instanceof FunctionValue)) onReject = null; - - var res = new Promise(); - - var fulfill = (FunctionValue)onFulfill; - var reject = (FunctionValue)onReject; - - handle(ctx, - new NativeFunction(null, (e, th, args) -> { - if (fulfill == null) res.fulfill(e, args[0]); - else { - try { res.fulfill(e, fulfill.call(e, null, args[0])); } - catch (EngineException err) { res.reject(e, err.value); } - } - return null; - }), - new NativeFunction(null, (e, th, args) -> { - if (reject == null) res.reject(e, args[0]); - else { - try { res.fulfill(e, reject.call(e, null, args[0])); } - catch (EngineException err) { res.reject(e, err.value); } - } - return null; - }) - ); - - return res; - } - /** - * Thread safe - you can call this from anywhere - * HOWEVER, it's strongly recommended to use this only in javascript - */ - @Native("catch") - public Promise _catch(CallContext ctx, Object onReject) { - return then(ctx, null, onReject); - } - /** - * Thread safe - you can call this from anywhere - * HOWEVER, it's strongly recommended to use this only in javascript - */ - @Native("finally") - public Promise _finally(CallContext ctx, Object handle) { - return then(ctx, - new NativeFunction(null, (e, th, args) -> { - if (handle instanceof FunctionValue) ((FunctionValue)handle).call(ctx); - return args[0]; - }), - new NativeFunction(null, (e, th, args) -> { - if (handle instanceof FunctionValue) ((FunctionValue)handle).call(ctx); - throw new EngineException(args[0]); - }) - ); - } - - /** - * NOT THREAD SAFE - must be called from the engine executor thread - */ - @Native - public Promise(CallContext ctx, FunctionValue func) throws InterruptedException { - if (!(func instanceof FunctionValue)) throw EngineException.ofType("A function must be passed to the promise constructor."); - try { - func.call( - ctx, null, - new NativeFunction(null, (e, th, args) -> { - fulfill(e, args.length > 0 ? args[0] : null); - return null; - }), - new NativeFunction(null, (e, th, args) -> { - reject(e, args.length > 0 ? args[0] : null); - return null; - }) - ); - } - catch (EngineException e) { - reject(ctx, e.value); - } - } - - @Override @Native - public String toString() { - if (state == STATE_PENDING) return "Promise (pending)"; - else if (state == STATE_FULFILLED) return "Promise (fulfilled)"; - else return "Promise (rejected)"; - } - - private Promise(int state, Object val) { - this.state = state; - this.val = val; - } - public Promise() { - this(STATE_PENDING, null); - } -} diff --git a/src/me/topchetoeu/jscript/polyfills/Set.java b/src/me/topchetoeu/jscript/polyfills/Set.java deleted file mode 100644 index c87b30b..0000000 --- a/src/me/topchetoeu/jscript/polyfills/Set.java +++ /dev/null @@ -1,76 +0,0 @@ -package me.topchetoeu.jscript.polyfills; - -import java.util.LinkedHashSet; -import java.util.stream.Collectors; - -import me.topchetoeu.jscript.engine.CallContext; -import me.topchetoeu.jscript.engine.values.ArrayValue; -import me.topchetoeu.jscript.engine.values.FunctionValue; -import me.topchetoeu.jscript.engine.values.ObjectValue; -import me.topchetoeu.jscript.engine.values.Values; -import me.topchetoeu.jscript.exceptions.EngineException; -import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeGetter; - -public class Set { - private LinkedHashSet objs = new LinkedHashSet<>(); - - @Native - public Set add(Object key) { - objs.add(key); - return this; - } - @Native - public boolean delete(Object key) { - return objs.remove(key); - } - @Native - public boolean has(Object key) { - return objs.contains(key); - } - @Native - public void clear() { - objs.clear(); - } - - @Native - public void forEach(CallContext ctx, Object func, Object thisArg) throws InterruptedException { - if (!(func instanceof FunctionValue)) throw EngineException.ofType("func must be a function."); - - for (var el : objs.stream().collect(Collectors.toList())) { - ((FunctionValue)func).call(ctx, thisArg, el, el, this); - } - } - - @Native - public ObjectValue entries(CallContext ctx) throws InterruptedException { - return Values.fromJavaIterable(ctx, objs - .stream() - .map(v -> new ArrayValue(ctx, v, v)) - .collect(Collectors.toList()) - ); - } - @Native - public ObjectValue keys(CallContext ctx) throws InterruptedException { - return Values.fromJavaIterable(ctx, objs); - } - @Native - public ObjectValue values(CallContext ctx) throws InterruptedException { - return Values.fromJavaIterable(ctx, objs); - } - - @NativeGetter("size") - public int size() { - return objs.size(); - } - - @Native - public Set(CallContext ctx, Object iterable) throws InterruptedException { - if (Values.isPrimitive(iterable)) return; - - for (var val : Values.toJavaIterable(ctx, iterable)) { - add(val); - } - } - public Set() { } -} -- 2.45.2 From 78b192babe4b175ef3853baff28b23aa3e9185d2 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Fri, 8 Sep 2023 02:36:09 +0300 Subject: [PATCH 05/14] fix: some build scripts --- Metadata.java | 7 ------- build.js | 28 +++++++++++++++++----------- meta.json | 3 +-- 3 files changed, 18 insertions(+), 20 deletions(-) delete mode 100644 Metadata.java diff --git a/Metadata.java b/Metadata.java deleted file mode 100644 index 128bfaf..0000000 --- a/Metadata.java +++ /dev/null @@ -1,7 +0,0 @@ -package me.topchetoeu.jscript; - -public class Metadata { - public static final String VERSION = "0.0.1-alpha"; - public static final String AUTHOR = "TopchetoEU"; - public static final String NAME = "java-jscript"; -} diff --git a/build.js b/build.js index b6e9609..f4f3b5f 100644 --- a/build.js +++ b/build.js @@ -38,16 +38,22 @@ function run(cmd, ...args) { } async function compileJava() { - await fs.writeFile('Metadata.java', (await fs.readFile('src/me/topchetoeu/jscript/Metadata.java')).toString() - .replace('${VERSION}', conf.version) - .replace('${NAME}', conf.name) - .replace('${AUTHOR}', conf.author) - ); - - const args = ['-d', 'dst/classes', 'Metadata.java']; - for await (const path of find('src', undefined, v => v.endsWith('.java') && !v.endsWith('Metadata.java'))) args.push(path); - await run(conf.javahome + '/javac', ...args); - await fs.rm('Metadata.java'); + try { + await fs.writeFile('Metadata.java', (await fs.readFile('src/me/topchetoeu/jscript/Metadata.java')).toString() + .replace('${VERSION}', conf.version) + .replace('${NAME}', conf.name) + .replace('${AUTHOR}', conf.author) + ); + const args = ['--release', '10', ]; + if (argv[1] === 'debug') args.push('-g'); + args.push('-d', 'dst/classes', 'Metadata.java'); + + for await (const path of find('src', undefined, v => v.endsWith('.java') && !v.endsWith('Metadata.java'))) args.push(path); + await run(conf.javahome + 'javac', ...args); + } + finally { + await fs.rm('Metadata.java'); + } } (async () => { @@ -59,7 +65,7 @@ async function compileJava() { await run('jar', '-c', '-f', 'dst/jscript.jar', '-e', 'me.topchetoeu.jscript.Main', '-C', 'dst/classes', '.'); } catch (e) { - if (argv.includes('debug')) throw e; + if (argv[1] === 'debug') throw e; else console.log(e.toString()); } })(); diff --git a/meta.json b/meta.json index c573484..fb3c57f 100644 --- a/meta.json +++ b/meta.json @@ -1,6 +1,5 @@ { - "version": "0.0.1-alpha", "name": "java-jscript", "author": "TopchetoEU", - "javahome": "/usr/lib/jvm/java-8-openjdk-amd64/bin" + "javahome": "" } \ No newline at end of file -- 2.45.2 From 0e04459fe742aa6b130988fe1e2854e75708bf07 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 9 Sep 2023 17:32:46 +0300 Subject: [PATCH 06/14] feat: implement new system for context tracking --- lib/core.ts | 11 +- lib/map.ts | 11 +- lib/set.ts | 9 +- lib/values/array.ts | 12 +- lib/values/function.ts | 4 +- lib/values/object.ts | 2 +- lib/values/string.ts | 28 ++--- lib/values/symbol.ts | 4 +- src/me/topchetoeu/jscript/Main.java | 26 ++-- .../compilation/CompoundStatement.java | 2 +- .../jscript/compilation/Instruction.java | 2 +- .../compilation/values/CallStatement.java | 4 +- .../compilation/values/NewStatement.java | 2 +- .../values/OperationStatement.java | 2 +- .../jscript/engine/BreakpointData.java | 13 -- .../jscript/engine/CallContext.java | 67 ---------- src/me/topchetoeu/jscript/engine/Context.java | 11 ++ .../jscript/engine/DebugCommand.java | 8 -- src/me/topchetoeu/jscript/engine/Engine.java | 32 ++--- ...{Environment.java => FunctionContext.java} | 10 +- .../jscript/engine/MessageContext.java | 33 +++++ .../jscript/engine/frame/CodeFrame.java | 74 +++-------- .../jscript/engine/frame/Runners.java | 116 +++++++++--------- .../jscript/engine/scope/GlobalScope.java | 12 +- .../engine/scope/LocalScopeRecord.java | 4 +- .../jscript/engine/scope/ValueVariable.java | 6 +- .../jscript/engine/scope/Variable.java | 6 +- .../jscript/engine/values/ArrayValue.java | 16 +-- .../jscript/engine/values/CodeFunction.java | 25 +--- .../jscript/engine/values/FunctionValue.java | 14 +-- .../jscript/engine/values/NativeFunction.java | 6 +- .../jscript/engine/values/NativeWrapper.java | 6 +- .../jscript/engine/values/ObjectValue.java | 68 +++++----- .../jscript/engine/values/Values.java | 88 ++++++------- .../jscript/exceptions/EngineException.java | 11 +- .../filesystem/FilesystemRegister.java | 7 -- .../topchetoeu/jscript/interop/Overload.java | 4 +- .../jscript/interop/OverloadFunction.java | 6 +- .../topchetoeu/jscript/parsing/ParseRes.java | 4 +- .../topchetoeu/jscript/parsing/Parsing.java | 10 +- src/me/topchetoeu/jscript/polyfills/Date.java | 36 +++--- .../jscript/polyfills/GeneratorFunction.java | 18 +-- .../jscript/polyfills/Internals.java | 39 +++--- src/me/topchetoeu/jscript/polyfills/JSON.java | 8 +- src/me/topchetoeu/jscript/polyfills/Math.java | 70 +++++------ .../jscript/polyfills/PolyfillEngine.java- | 105 ---------------- .../topchetoeu/jscript/polyfills/RegExp.java | 14 +-- .../jscript/polyfills/TypescriptEngine.java- | 61 --------- 48 files changed, 435 insertions(+), 692 deletions(-) delete mode 100644 src/me/topchetoeu/jscript/engine/BreakpointData.java delete mode 100644 src/me/topchetoeu/jscript/engine/CallContext.java create mode 100644 src/me/topchetoeu/jscript/engine/Context.java delete mode 100644 src/me/topchetoeu/jscript/engine/DebugCommand.java rename src/me/topchetoeu/jscript/engine/{Environment.java => FunctionContext.java} (91%) create mode 100644 src/me/topchetoeu/jscript/engine/MessageContext.java delete mode 100644 src/me/topchetoeu/jscript/filesystem/FilesystemRegister.java delete mode 100644 src/me/topchetoeu/jscript/polyfills/PolyfillEngine.java- delete mode 100644 src/me/topchetoeu/jscript/polyfills/TypescriptEngine.java- diff --git a/lib/core.ts b/lib/core.ts index f28ad57..4e1b9d9 100644 --- a/lib/core.ts +++ b/lib/core.ts @@ -29,9 +29,14 @@ interface Internals { extensible(obj: object): boolean; sort(arr: any[], comaprator: (a: any, b: any) => number): void; + + constructor: { + log(...args: any[]): void; + } } -var env: Environment = arguments[0], internals: Internals = arguments[1]; +// @ts-ignore +var env: Environment = arguments[0], internals: Internals = arguments[1], log = internals.constructor.log; try { run('values/object'); @@ -47,7 +52,9 @@ try { run('set'); run('regex'); run('timeout'); - + + env.global.log = log; + log('Loaded polyfills!'); } catch (e: any) { diff --git a/lib/map.ts b/lib/map.ts index f318687..b7745af 100644 --- a/lib/map.ts +++ b/lib/map.ts @@ -1,10 +1,11 @@ define("map", () => { const syms = { values: internals.symbol('Map.values') } as { readonly values: unique symbol }; + const Object = env.global.Object; class Map { [syms.values]: any = {}; - public [Symbol.iterator](): IterableIterator<[KeyT, ValueT]> { + public [env.global.Symbol.iterator](): IterableIterator<[KeyT, ValueT]> { return this.entries(); } @@ -28,7 +29,7 @@ define("map", () => { if (i >= keys.length) return { done: true }; else return { done: false, value: [ keys[i], this[syms.values][keys[i++]] ] } }, - [Symbol.iterator]() { return this; } + [env.global.Symbol.iterator]() { return this; } } } public keys(): IterableIterator { @@ -40,7 +41,7 @@ define("map", () => { if (i >= keys.length) return { done: true }; else return { done: false, value: keys[i] } }, - [Symbol.iterator]() { return this; } + [env.global.Symbol.iterator]() { return this; } } } public values(): IterableIterator { @@ -52,7 +53,7 @@ define("map", () => { if (i >= keys.length) return { done: true }; else return { done: false, value: this[syms.values][keys[i++]] } }, - [Symbol.iterator]() { return this; } + [env.global.Symbol.iterator]() { return this; } } } @@ -80,7 +81,7 @@ define("map", () => { } public constructor(iterable: Iterable<[KeyT, ValueT]>) { - const it = iterable[Symbol.iterator](); + const it = iterable[env.global.Symbol.iterator](); for (let el = it.next(); !el.done; el = it.next()) { this[syms.values][el.value[0]] = el.value[1]; diff --git a/lib/set.ts b/lib/set.ts index 7bf9a19..a0ac277 100644 --- a/lib/set.ts +++ b/lib/set.ts @@ -1,10 +1,11 @@ define("set", () => { const syms = { values: internals.symbol('Map.values') } as { readonly values: unique symbol }; + const Object = env.global.Object; class Set { [syms.values]: any = {}; - public [Symbol.iterator](): IterableIterator<[T, T]> { + public [env.global.Symbol.iterator](): IterableIterator<[T, T]> { return this.entries(); } @@ -28,7 +29,7 @@ define("set", () => { if (i >= keys.length) return { done: true }; else return { done: false, value: [ keys[i], keys[i] ] } }, - [Symbol.iterator]() { return this; } + [env.global.Symbol.iterator]() { return this; } } } public keys(): IterableIterator { @@ -40,7 +41,7 @@ define("set", () => { if (i >= keys.length) return { done: true }; else return { done: false, value: keys[i] } }, - [Symbol.iterator]() { return this; } + [env.global.Symbol.iterator]() { return this; } } } public values(): IterableIterator { @@ -68,7 +69,7 @@ define("set", () => { } public constructor(iterable: Iterable) { - const it = iterable[Symbol.iterator](); + const it = iterable[env.global.Symbol.iterator](); for (let el = it.next(); !el.done; el = it.next()) { this[syms.values][el.value] = undefined; diff --git a/lib/values/array.ts b/lib/values/array.ts index 9eea6d7..12928ba 100644 --- a/lib/values/array.ts +++ b/lib/values/array.ts @@ -16,14 +16,14 @@ define("values/array", () => { } as ArrayConstructor; env.setProto('array', Array.prototype); - (Array.prototype as any)[Symbol.typeName] = "Array"; + (Array.prototype as any)[env.global.Symbol.typeName] = "Array"; setConstr(Array.prototype, Array); setProps(Array.prototype, { - [Symbol.iterator]: function() { + [env.global.Symbol.iterator]: function() { return this.values(); }, - [Symbol.typeName]: "Array", + [env.global.Symbol.typeName]: "Array", values() { var i = 0; @@ -35,7 +35,7 @@ define("values/array", () => { } return { done: true, value: undefined }; }, - [Symbol.iterator]() { return this; } + [env.global.Symbol.iterator]() { return this; } }; }, keys() { @@ -48,7 +48,7 @@ define("values/array", () => { } return { done: true, value: undefined }; }, - [Symbol.iterator]() { return this; } + [env.global.Symbol.iterator]() { return this; } }; }, entries() { @@ -61,7 +61,7 @@ define("values/array", () => { } return { done: true, value: undefined }; }, - [Symbol.iterator]() { return this; } + [env.global.Symbol.iterator]() { return this; } }; }, concat() { diff --git a/lib/values/function.ts b/lib/values/function.ts index c49a4f2..4a161d6 100644 --- a/lib/values/function.ts +++ b/lib/values/function.ts @@ -117,7 +117,7 @@ define("values/function", () => { }, return: (value) => new Promise((res, rej) => next(res, rej, 'ret', value)), throw: (value) => new Promise((res, rej) => next(res, rej, 'err', value)), - [Symbol.asyncIterator]() { return this; } + [env.global.Symbol.asyncIterator]() { return this; } } } }, @@ -131,7 +131,7 @@ define("values/function", () => { next: (...args) => internals.apply(it.next, it, args), return: (val) => internals.apply(it.next, it, [val]), throw: (val) => internals.apply(it.next, it, [val]), - [Symbol.iterator]() { return this; } + [env.global.Symbol.iterator]() { return this; } } } } diff --git a/lib/values/object.ts b/lib/values/object.ts index 277decd..475e4da 100644 --- a/lib/values/object.ts +++ b/lib/values/object.ts @@ -216,7 +216,7 @@ define("values/object", () => { return this; }, toString() { - return '[object ' + (this[Symbol.typeName] ?? 'Unknown') + ']'; + return '[object ' + (this[env.global.Symbol.typeName] ?? 'Unknown') + ']'; }, hasOwnProperty(key) { return Object.hasOwn(this, key); diff --git a/lib/values/string.ts b/lib/values/string.ts index b1af549..bfce164 100644 --- a/lib/values/string.ts +++ b/lib/values/string.ts @@ -142,9 +142,9 @@ define("values/string", () => { else throw new Error('This function may be used only with primitive or object strings.'); } - if (typeof term[Symbol.search] !== 'function') term = RegExp.escape(term); + if (typeof term[env.global.Symbol.search] !== 'function') term = RegExp.escape(term); - return term[Symbol.search](this, false, start); + return term[env.global.Symbol.search](this, false, start); }, lastIndexOf(term: any, start) { if (typeof this !== 'string') { @@ -152,9 +152,9 @@ define("values/string", () => { else throw new Error('This function may be used only with primitive or object strings.'); } - if (typeof term[Symbol.search] !== 'function') term = RegExp.escape(term); + if (typeof term[env.global.Symbol.search] !== 'function') term = RegExp.escape(term); - return term[Symbol.search](this, true, start); + return term[env.global.Symbol.search](this, true, start); }, includes(term, start) { return this.indexOf(term, start) >= 0; @@ -166,9 +166,9 @@ define("values/string", () => { else throw new Error('This function may be used only with primitive or object strings.'); } - if (typeof pattern[Symbol.replace] !== 'function') pattern = RegExp.escape(pattern); + if (typeof pattern[env.global.Symbol.replace] !== 'function') pattern = RegExp.escape(pattern); - return pattern[Symbol.replace](this, val); + return pattern[env.global.Symbol.replace](this, val); }, replaceAll(pattern: any, val) { if (typeof this !== 'string') { @@ -176,10 +176,10 @@ define("values/string", () => { else throw new Error('This function may be used only with primitive or object strings.'); } - if (typeof pattern[Symbol.replace] !== 'function') pattern = RegExp.escape(pattern, "g"); + if (typeof pattern[env.global.Symbol.replace] !== 'function') pattern = RegExp.escape(pattern, "g"); if (pattern instanceof RegExp && !pattern.global) pattern = new pattern.constructor(pattern.source, pattern.flags + "g"); - return pattern[Symbol.replace](this, val); + return pattern[env.global.Symbol.replace](this, val); }, match(pattern: any) { @@ -188,9 +188,9 @@ define("values/string", () => { else throw new Error('This function may be used only with primitive or object strings.'); } - if (typeof pattern[Symbol.match] !== 'function') pattern = RegExp.escape(pattern); + if (typeof pattern[env.global.Symbol.match] !== 'function') pattern = RegExp.escape(pattern); - return pattern[Symbol.match](this); + return pattern[env.global.Symbol.match](this); }, matchAll(pattern: any) { if (typeof this !== 'string') { @@ -198,10 +198,10 @@ define("values/string", () => { else throw new Error('This function may be used only with primitive or object strings.'); } - if (typeof pattern[Symbol.match] !== 'function') pattern = RegExp.escape(pattern, "g"); + if (typeof pattern[env.global.Symbol.match] !== 'function') pattern = RegExp.escape(pattern, "g"); if (pattern instanceof RegExp && !pattern.global) pattern = new pattern.constructor(pattern.source, pattern.flags + "g"); - return pattern[Symbol.match](this); + return pattern[env.global.Symbol.match](this); }, split(pattern: any, lim, sensible) { @@ -210,9 +210,9 @@ define("values/string", () => { else throw new Error('This function may be used only with primitive or object strings.'); } - if (typeof pattern[Symbol.split] !== 'function') pattern = RegExp.escape(pattern, "g"); + if (typeof pattern[env.global.Symbol.split] !== 'function') pattern = RegExp.escape(pattern, "g"); - return pattern[Symbol.split](this, lim, sensible); + return pattern[env.global.Symbol.split](this, lim, sensible); }, slice(start, end) { if (typeof this !== 'string') { diff --git a/lib/values/symbol.ts b/lib/values/symbol.ts index 4e47c16..64b4d5e 100644 --- a/lib/values/symbol.ts +++ b/lib/values/symbol.ts @@ -31,6 +31,6 @@ define("values/symbol", () => { asyncIterator: Symbol('Symbol.asyncIterator') as any, }); - env.global.Object.defineProperty(Object.prototype, Symbol.typeName, { value: 'Object' }); - env.global.Object.defineProperty(env.global, Symbol.typeName, { value: 'Window' }); + internals.defineField(env.global.Object.prototype, Symbol.typeName, 'Object', false, false, false); + internals.defineField(env.global, Symbol.typeName, 'Window', false, false, false); }); \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/Main.java b/src/me/topchetoeu/jscript/Main.java index 7fe117b..9de34f3 100644 --- a/src/me/topchetoeu/jscript/Main.java +++ b/src/me/topchetoeu/jscript/Main.java @@ -6,12 +6,11 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Map; -import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.MessageContext; +import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.Engine; -import me.topchetoeu.jscript.engine.Environment; -import me.topchetoeu.jscript.engine.values.NativeFunction; +import me.topchetoeu.jscript.engine.FunctionContext; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.events.Observer; import me.topchetoeu.jscript.exceptions.EngineException; @@ -22,7 +21,7 @@ import me.topchetoeu.jscript.polyfills.Internals; public class Main { static Thread task; static Engine engine; - static Environment env; + static FunctionContext env; public static String streamToString(InputStream in) { try { @@ -59,7 +58,7 @@ public class Main { try { try { if (err instanceof EngineException) { - System.out.println("Uncaught " + ((EngineException)err).toString(new CallContext(engine, env))); + System.out.println("Uncaught " + ((EngineException)err).toString(new Context(null, new MessageContext(engine)))); } else if (err instanceof SyntaxException) { System.out.println("Syntax error:" + ((SyntaxException)err).msg); @@ -86,7 +85,8 @@ public class Main { System.out.println(String.format("Running %s v%s by %s", Metadata.NAME, Metadata.VERSION, Metadata.AUTHOR)); var in = new BufferedReader(new InputStreamReader(System.in)); engine = new Engine(); - env = new Environment(null, null, null); + env = new FunctionContext(null, null, null); + var builderEnv = new FunctionContext(null, new NativeTypeRegister(), null); var exited = new boolean[1]; env.global.define("exit", ctx -> { @@ -103,16 +103,8 @@ public class Main { throw new EngineException("Couldn't open do.js"); } }); - env.global.define(true, new NativeFunction("log", (el, t, _args) -> { - for (var obj : _args) Values.printValue(el, obj); - System.out.println(); - return null; - })); - var builderEnv = env.child(); - builderEnv.wrappersProvider = new NativeTypeRegister(); - - engine.pushMsg(false, Map.of(), builderEnv, "core.js", resourceToString("js/core.js"), null, env, new Internals()); + engine.pushMsg(false, new Context(builderEnv, new MessageContext(engine)), "core.js", resourceToString("js/core.js"), null, env, new Internals()).toObservable().on(valuePrinter); task = engine.start(); var reader = new Thread(() -> { @@ -122,7 +114,7 @@ public class Main { var raw = in.readLine(); if (raw == null) break; - engine.pushMsg(false, Map.of(), env, "", raw, null).toObservable().once(valuePrinter); + engine.pushMsg(false, new Context(env, new MessageContext(engine)), "", raw, null).toObservable().once(valuePrinter); } catch (EngineException e) { try { diff --git a/src/me/topchetoeu/jscript/compilation/CompoundStatement.java b/src/me/topchetoeu/jscript/compilation/CompoundStatement.java index d85bbe6..b5054c3 100644 --- a/src/me/topchetoeu/jscript/compilation/CompoundStatement.java +++ b/src/me/topchetoeu/jscript/compilation/CompoundStatement.java @@ -70,7 +70,7 @@ public class CompoundStatement extends Statement { else return new CompoundStatement(loc(), res.toArray(Statement[]::new)); } - public CompoundStatement(Location loc, Statement... statements) { + public CompoundStatement(Location loc, Statement ...statements) { super(loc); this.statements = statements; } diff --git a/src/me/topchetoeu/jscript/compilation/Instruction.java b/src/me/topchetoeu/jscript/compilation/Instruction.java index 6e3be1c..b0f66a0 100644 --- a/src/me/topchetoeu/jscript/compilation/Instruction.java +++ b/src/me/topchetoeu/jscript/compilation/Instruction.java @@ -129,7 +129,7 @@ public class Instruction { return params[i].equals(arg); } - private Instruction(Location location, Type type, Object... params) { + private Instruction(Location location, Type type, Object ...params) { this.location = location; this.type = type; this.params = params; diff --git a/src/me/topchetoeu/jscript/compilation/values/CallStatement.java b/src/me/topchetoeu/jscript/compilation/values/CallStatement.java index 5a9f33c..de1c495 100644 --- a/src/me/topchetoeu/jscript/compilation/values/CallStatement.java +++ b/src/me/topchetoeu/jscript/compilation/values/CallStatement.java @@ -31,12 +31,12 @@ public class CallStatement extends Statement { target.add(Instruction.call(args.length).locate(loc()).setDebug(true)); } - public CallStatement(Location loc, Statement func, Statement... args) { + public CallStatement(Location loc, Statement func, Statement ...args) { super(loc); this.func = func; this.args = args; } - public CallStatement(Location loc, Statement obj, Object key, Statement... args) { + public CallStatement(Location loc, Statement obj, Object key, Statement ...args) { super(loc); this.func = new IndexStatement(loc, obj, new ConstantStatement(loc, key)); this.args = args; diff --git a/src/me/topchetoeu/jscript/compilation/values/NewStatement.java b/src/me/topchetoeu/jscript/compilation/values/NewStatement.java index c325429..3b5d1d8 100644 --- a/src/me/topchetoeu/jscript/compilation/values/NewStatement.java +++ b/src/me/topchetoeu/jscript/compilation/values/NewStatement.java @@ -24,7 +24,7 @@ public class NewStatement extends Statement { target.add(Instruction.callNew(args.length).locate(loc()).setDebug(true)); } - public NewStatement(Location loc, Statement func, Statement... args) { + public NewStatement(Location loc, Statement func, Statement ...args) { super(loc); this.func = func; this.args = args; diff --git a/src/me/topchetoeu/jscript/compilation/values/OperationStatement.java b/src/me/topchetoeu/jscript/compilation/values/OperationStatement.java index ab56757..1723429 100644 --- a/src/me/topchetoeu/jscript/compilation/values/OperationStatement.java +++ b/src/me/topchetoeu/jscript/compilation/values/OperationStatement.java @@ -63,7 +63,7 @@ public class OperationStatement extends Statement { } - public OperationStatement(Location loc, Operation operation, Statement... args) { + public OperationStatement(Location loc, Operation operation, Statement ...args) { super(loc); this.operation = operation; this.args = args; diff --git a/src/me/topchetoeu/jscript/engine/BreakpointData.java b/src/me/topchetoeu/jscript/engine/BreakpointData.java deleted file mode 100644 index 5e68ec1..0000000 --- a/src/me/topchetoeu/jscript/engine/BreakpointData.java +++ /dev/null @@ -1,13 +0,0 @@ -package me.topchetoeu.jscript.engine; - -import me.topchetoeu.jscript.Location; - -public class BreakpointData { - public final Location loc; - public final CallContext ctx; - - public BreakpointData(Location loc, CallContext ctx) { - this.loc = loc; - this.ctx = ctx; - } -} \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/engine/CallContext.java b/src/me/topchetoeu/jscript/engine/CallContext.java deleted file mode 100644 index e62bd12..0000000 --- a/src/me/topchetoeu/jscript/engine/CallContext.java +++ /dev/null @@ -1,67 +0,0 @@ -package me.topchetoeu.jscript.engine; - -import java.util.Collections; -import java.util.Hashtable; -import java.util.Map; - -@SuppressWarnings("unchecked") -public class CallContext { - public static final class DataKey {} - - public final Engine engine; - public Environment environment; - private final Map, Object> data; - - public Map, Object> data() { return Collections.unmodifiableMap(data); } - - public CallContext copy() { - return new CallContext(engine, environment).mergeData(data); - } - - public CallContext mergeData(Map, Object> objs) { - data.putAll(objs); - return this; - } - public CallContext setData(DataKey key, T val) { - if (val == null) data.remove(key); - else data.put(key, val); - return this; - } - public T addData(DataKey key, T val) { - if (data.containsKey(key)) return (T)data.get(key); - else { - if (val == null) data.remove(key); - else data.put(key, val); - return val; - } - } - public T getData(DataKey key) { - return getData(key, null); - } - public T getData(DataKey key, T defaultVal) { - if (!hasData(key)) return defaultVal; - else return (T)data.get(key); - } - public boolean hasData(DataKey key) { return data.containsKey(key); } - - public CallContext changeData(DataKey key, int n, int start) { - return setData(key, getData(key, start) + n); - } - public CallContext changeData(DataKey key, int n) { - return changeData(key, n, 0); - } - public CallContext changeData(DataKey key) { - return changeData(key, 1, 0); - } - - public CallContext(Engine engine, Environment env) { - this.engine = engine; - this.data = new Hashtable<>(); - this.environment = env; - } - public CallContext(CallContext parent, Environment env) { - this.engine = parent.engine; - this.data = parent.data; - this.environment = env; - } -} diff --git a/src/me/topchetoeu/jscript/engine/Context.java b/src/me/topchetoeu/jscript/engine/Context.java new file mode 100644 index 0000000..adcc37a --- /dev/null +++ b/src/me/topchetoeu/jscript/engine/Context.java @@ -0,0 +1,11 @@ +package me.topchetoeu.jscript.engine; + +public class Context { + public final FunctionContext function; + public final MessageContext message; + + public Context(FunctionContext funcCtx, MessageContext msgCtx) { + this.function = funcCtx; + this.message = msgCtx; + } +} diff --git a/src/me/topchetoeu/jscript/engine/DebugCommand.java b/src/me/topchetoeu/jscript/engine/DebugCommand.java deleted file mode 100644 index 1afb346..0000000 --- a/src/me/topchetoeu/jscript/engine/DebugCommand.java +++ /dev/null @@ -1,8 +0,0 @@ -package me.topchetoeu.jscript.engine; - -public enum DebugCommand { - NORMAL, - STEP_OVER, - STEP_OUT, - STEP_INTO, -} \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/engine/Engine.java b/src/me/topchetoeu/jscript/engine/Engine.java index 917d3db..e98e000 100644 --- a/src/me/topchetoeu/jscript/engine/Engine.java +++ b/src/me/topchetoeu/jscript/engine/Engine.java @@ -3,7 +3,6 @@ package me.topchetoeu.jscript.engine; import java.util.Map; import java.util.concurrent.LinkedBlockingDeque; -import me.topchetoeu.jscript.engine.CallContext.DataKey; import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.events.Awaitable; @@ -15,16 +14,19 @@ public class Engine { private class UncompiledFunction extends FunctionValue { public final String filename; public final String raw; + public final FunctionContext ctx; @Override - public Object call(CallContext ctx, Object thisArg, Object... args) throws InterruptedException { + public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException { + ctx = new Context(this.ctx, ctx.message); return compile(ctx, filename, raw).call(ctx, thisArg, args); } - public UncompiledFunction(String filename, String raw) { + public UncompiledFunction(FunctionContext ctx, String filename, String raw) { super(filename, 0); this.filename = filename; this.raw = raw; + this.ctx = ctx; } } @@ -32,14 +34,12 @@ public class Engine { public final FunctionValue func; public final Object thisArg; public final Object[] args; - public final Map, Object> data; public final DataNotifier notifier = new DataNotifier<>(); - public final Environment env; + public final MessageContext ctx; - public Task(Environment env, FunctionValue func, Map, Object> data, Object thisArg, Object[] args) { - this.env = env; + public Task(MessageContext ctx, FunctionValue func, Object thisArg, Object[] args) { + this.ctx = ctx; this.func = func; - this.data = data; this.thisArg = thisArg; this.args = args; } @@ -60,7 +60,7 @@ public class Engine { private void runTask(Task task) throws InterruptedException { try { - task.notifier.next(task.func.call(new CallContext(this, task.env).mergeData(task.data), task.thisArg, task.args)); + task.notifier.next(task.func.call(new Context(null, new MessageContext(this)), task.thisArg, task.args)); } catch (InterruptedException e) { task.notifier.error(new RuntimeException(e)); @@ -110,19 +110,19 @@ public class Engine { return this.thread != null; } - public Awaitable pushMsg(boolean micro, Map, Object> data, Environment env, FunctionValue func, Object thisArg, Object... args) { - var msg = new Task(env, func, data, thisArg, args); + public Awaitable pushMsg(boolean micro, MessageContext ctx, FunctionValue func, Object thisArg, Object ...args) { + var msg = new Task(ctx, func, thisArg, args); if (micro) microTasks.addLast(msg); else macroTasks.addLast(msg); return msg.notifier; } - public Awaitable pushMsg(boolean micro, Map, Object> data, Environment env, String filename, String raw, Object thisArg, Object... args) { - return pushMsg(micro, data, env, new UncompiledFunction(filename, raw), thisArg, args); + public Awaitable pushMsg(boolean micro, Context ctx, String filename, String raw, Object thisArg, Object ...args) { + return pushMsg(micro, ctx.message, new UncompiledFunction(ctx.function, filename, raw), thisArg, args); } - public FunctionValue compile(CallContext ctx, String filename, String raw) throws InterruptedException { - var res = Values.toString(ctx, ctx.environment.compile.call(ctx, null, raw, filename)); - return Parsing.compile(ctx.environment, filename, res); + public FunctionValue compile(Context ctx, String filename, String raw) throws InterruptedException { + var res = Values.toString(ctx, ctx.function.compile.call(ctx, null, raw, filename)); + return Parsing.compile(ctx.function, filename, res); } // public Engine() { diff --git a/src/me/topchetoeu/jscript/engine/Environment.java b/src/me/topchetoeu/jscript/engine/FunctionContext.java similarity index 91% rename from src/me/topchetoeu/jscript/engine/Environment.java rename to src/me/topchetoeu/jscript/engine/FunctionContext.java index 8224833..e21b7d9 100644 --- a/src/me/topchetoeu/jscript/engine/Environment.java +++ b/src/me/topchetoeu/jscript/engine/FunctionContext.java @@ -11,7 +11,7 @@ import me.topchetoeu.jscript.interop.Native; import me.topchetoeu.jscript.interop.NativeGetter; import me.topchetoeu.jscript.interop.NativeSetter; -public class Environment { +public class FunctionContext { private HashMap prototypes = new HashMap<>(); public GlobalScope global; public WrappersProvider wrappersProvider; @@ -48,21 +48,21 @@ public class Environment { } @Native - public Environment fork() { - var res = new Environment(compile, wrappersProvider, global); + public FunctionContext fork() { + var res = new FunctionContext(compile, wrappersProvider, global); res.regexConstructor = regexConstructor; res.prototypes = new HashMap<>(prototypes); return res; } @Native - public Environment child() { + public FunctionContext child() { var res = fork(); res.global = res.global.globalChild(); return res; } - public Environment(FunctionValue compile, WrappersProvider nativeConverter, GlobalScope global) { + public FunctionContext(FunctionValue compile, WrappersProvider nativeConverter, GlobalScope global) { if (compile == null) compile = new NativeFunction("compile", (ctx, thisArg, args) -> args.length == 0 ? "" : args[0]); if (nativeConverter == null) nativeConverter = new WrappersProvider() { public ObjectValue getConstr(Class obj) { diff --git a/src/me/topchetoeu/jscript/engine/MessageContext.java b/src/me/topchetoeu/jscript/engine/MessageContext.java new file mode 100644 index 0000000..2ac19b4 --- /dev/null +++ b/src/me/topchetoeu/jscript/engine/MessageContext.java @@ -0,0 +1,33 @@ +package me.topchetoeu.jscript.engine; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import me.topchetoeu.jscript.engine.frame.CodeFrame; +import me.topchetoeu.jscript.exceptions.EngineException; + +public class MessageContext { + public final Engine engine; + + private final ArrayList frames = new ArrayList<>(); + public int maxStackFrames = 1000; + + public List frames() { return Collections.unmodifiableList(frames); } + + public MessageContext pushFrame(CodeFrame frame) { + this.frames.add(frame); + if (this.frames.size() > maxStackFrames) throw EngineException.ofRange("Stack overflow!"); + return this; + } + public boolean popFrame(CodeFrame frame) { + if (this.frames.size() == 0) return false; + if (this.frames.get(this.frames.size() - 1) != frame) return false; + this.frames.remove(this.frames.size() - 1); + return true; + } + + public MessageContext(Engine engine) { + this.engine = engine; + } +} diff --git a/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java b/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java index 5a6e188..aeaf264 100644 --- a/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java +++ b/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java @@ -4,9 +4,7 @@ import java.util.ArrayList; import java.util.List; import me.topchetoeu.jscript.Location; -import me.topchetoeu.jscript.engine.CallContext; -import me.topchetoeu.jscript.engine.DebugCommand; -import me.topchetoeu.jscript.engine.CallContext.DataKey; +import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.scope.LocalScope; import me.topchetoeu.jscript.engine.scope.ValueVariable; import me.topchetoeu.jscript.engine.values.ArrayValue; @@ -44,11 +42,6 @@ public class CodeFrame { } } - public static final DataKey STACK_N_KEY = new DataKey<>(); - public static final DataKey MAX_STACK_KEY = new DataKey<>(); - public static final DataKey STOP_AT_START_KEY = new DataKey<>(); - public static final DataKey STEPPING_TROUGH_KEY = new DataKey<>(); - public final LocalScope scope; public final Object thisArg; public final Object[] args; @@ -59,7 +52,6 @@ public class CodeFrame { public int stackPtr = 0; public int codePtr = 0; public boolean jumpFlag = false; - private DebugCommand debugCmd = null; private Location prevLoc = null; public void addTry(int n, int catchN, int finallyN) { @@ -92,7 +84,7 @@ public class CodeFrame { return res; } - public void push(CallContext ctx, Object val) { + public void push(Context ctx, Object val) { if (stack.length <= stackPtr) { var newStack = new Object[stack.length * 2]; System.arraycopy(stack, 0, newStack, 0, stack.length); @@ -101,21 +93,7 @@ public class CodeFrame { stack[stackPtr++] = Values.normalize(ctx, val); } - public void start(CallContext ctx) { - if (ctx.getData(STACK_N_KEY, 0) >= ctx.addData(MAX_STACK_KEY, 10000)) throw EngineException.ofRange("Stack overflow!"); - ctx.changeData(STACK_N_KEY); - - // var debugState = ctx.getData(Engine.DEBUG_STATE_KEY); - // if (debugState != null) debugState.pushFrame(this); - } - public void end(CallContext ctx) { - ctx.changeData(STACK_N_KEY, -1); - - // var debugState = ctx.getData(Engine.DEBUG_STATE_KEY); - // if (debugState != null) debugState.popFrame(); - } - - private Object nextNoTry(CallContext ctx) throws InterruptedException { + private Object nextNoTry(Context ctx) throws InterruptedException { if (Thread.currentThread().isInterrupted()) throw new InterruptedException(); if (codePtr < 0 || codePtr >= function.body.length) return null; @@ -124,39 +102,16 @@ public class CodeFrame { var loc = instr.location; if (loc != null) prevLoc = loc; - // var debugState = ctx.getData(Engine.DEBUG_STATE_KEY); - // if (debugCmd == null) { - // if (ctx.getData(STOP_AT_START_KEY, false)) debugCmd = DebugCommand.STEP_OVER; - // else debugCmd = DebugCommand.NORMAL; - - // if (debugState != null) debugState.pushFrame(this); - // } - - // if (debugState != null && loc != null) { - // if ( - // instr.type == Type.NOP && instr.match("debug") || debugState.breakpoints.contains(loc) || ( - // ctx.getData(STEPPING_TROUGH_KEY, false) && - // (debugCmd == DebugCommand.STEP_INTO || debugCmd == DebugCommand.STEP_OVER) - // ) - // ) { - // ctx.setData(STEPPING_TROUGH_KEY, true); - - // debugState.breakpointNotifier.next(new BreakpointData(loc, ctx)); - // debugCmd = debugState.commandNotifier.toAwaitable().await(); - // if (debugCmd == DebugCommand.NORMAL) ctx.setData(STEPPING_TROUGH_KEY, false); - // } - // } - try { this.jumpFlag = false; - return Runners.exec(ctx, debugCmd, instr, this); + return Runners.exec(ctx, instr, this); } catch (EngineException e) { throw e.add(function.name, prevLoc); } } - public Object next(CallContext ctx, Object prevReturn, Object prevError) throws InterruptedException { + public Object next(Context ctx, Object prevReturn, Object prevError) throws InterruptedException { TryCtx tryCtx = null; if (prevError != Runners.NO_RETURN) prevReturn = Runners.NO_RETURN; @@ -256,6 +211,7 @@ public class CodeFrame { catch (EngineException e) { if (tryCtx.hasCatch) { tryCtx.state = TryCtx.STATE_CATCH; + tryCtx.err = e; codePtr = tryCtx.catchStart; scope.catchVars.add(new ValueVariable(false, e.value)); return Runners.NO_RETURN; @@ -280,6 +236,7 @@ public class CodeFrame { else return res; } catch (EngineException e) { + e.cause = tryCtx.err; if (tryCtx.hasFinally) { tryCtx.err = e; tryCtx.state = TryCtx.STATE_FINALLY_THREW; @@ -290,23 +247,32 @@ public class CodeFrame { codePtr = tryCtx.finallyStart; return Runners.NO_RETURN; } + else if (tryCtx.state == TryCtx.STATE_FINALLY_THREW) { + try { + return nextNoTry(ctx); + } + catch (EngineException e) { + e.cause = tryCtx.err; + throw e; + } + } else return nextNoTry(ctx); } - public Object run(CallContext ctx) throws InterruptedException { + public Object run(Context ctx) throws InterruptedException { try { - start(ctx); + ctx.message.pushFrame(this); while (true) { var res = next(ctx, Runners.NO_RETURN, Runners.NO_RETURN); if (res != Runners.NO_RETURN) return res; } } finally { - end(ctx); + ctx.message.popFrame(this); } } - public CodeFrame(CallContext ctx, Object thisArg, Object[] args, CodeFunction func) { + public CodeFrame(Context ctx, Object thisArg, Object[] args, CodeFunction func) { this.args = args.clone(); this.scope = new LocalScope(func.localsN, func.captures); this.scope.get(0).set(null, thisArg); diff --git a/src/me/topchetoeu/jscript/engine/frame/Runners.java b/src/me/topchetoeu/jscript/engine/frame/Runners.java index 631706e..37ce098 100644 --- a/src/me/topchetoeu/jscript/engine/frame/Runners.java +++ b/src/me/topchetoeu/jscript/engine/frame/Runners.java @@ -3,8 +3,7 @@ package me.topchetoeu.jscript.engine.frame; import java.util.Collections; import me.topchetoeu.jscript.compilation.Instruction; -import me.topchetoeu.jscript.engine.CallContext; -import me.topchetoeu.jscript.engine.DebugCommand; +import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.scope.ValueVariable; import me.topchetoeu.jscript.engine.values.ArrayValue; @@ -18,48 +17,45 @@ import me.topchetoeu.jscript.exceptions.EngineException; public class Runners { public static final Object NO_RETURN = new Object(); - public static Object execReturn(CallContext ctx, Instruction instr, CodeFrame frame) { - frame.codePtr++; + public static Object execReturn(Context ctx, Instruction instr, CodeFrame frame) { return frame.pop(); } - public static Object execSignal(CallContext ctx, Instruction instr, CodeFrame frame) { - frame.codePtr++; + public static Object execSignal(Context ctx, Instruction instr, CodeFrame frame) { return new SignalValue(instr.get(0)); } - public static Object execThrow(CallContext ctx, Instruction instr, CodeFrame frame) { + public static Object execThrow(Context ctx, Instruction instr, CodeFrame frame) { throw new EngineException(frame.pop()); } - public static Object execThrowSyntax(CallContext ctx, Instruction instr, CodeFrame frame) { + public static Object execThrowSyntax(Context ctx, Instruction instr, CodeFrame frame) { throw EngineException.ofSyntax((String)instr.get(0)); } - private static Object call(CallContext ctx, DebugCommand state, Object func, Object thisArg, Object... args) throws InterruptedException { - ctx.setData(CodeFrame.STOP_AT_START_KEY, state == DebugCommand.STEP_INTO); + private static Object call(Context ctx, Object func, Object thisArg, Object ...args) throws InterruptedException { return Values.call(ctx, func, thisArg, args); } - public static Object execCall(CallContext ctx, DebugCommand state, Instruction instr, CodeFrame frame) throws InterruptedException { + public static Object execCall(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var callArgs = frame.take(instr.get(0)); var func = frame.pop(); var thisArg = frame.pop(); - frame.push(ctx, call(ctx, state, func, thisArg, callArgs)); + frame.push(ctx, call(ctx, func, thisArg, callArgs)); frame.codePtr++; return NO_RETURN; } - public static Object execCallNew(CallContext ctx, DebugCommand state, Instruction instr, CodeFrame frame) throws InterruptedException { + public static Object execCallNew(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var callArgs = frame.take(instr.get(0)); var funcObj = frame.pop(); if (Values.isFunction(funcObj) && Values.function(funcObj).special) { - frame.push(ctx, call(ctx, state, funcObj, null, callArgs)); + frame.push(ctx, call(ctx, funcObj, null, callArgs)); } else { var proto = Values.getMember(ctx, funcObj, "prototype"); var obj = new ObjectValue(); obj.setPrototype(ctx, proto); - call(ctx, state, funcObj, obj, callArgs); + call(ctx, funcObj, obj, callArgs); frame.push(ctx, obj); } @@ -67,13 +63,13 @@ public class Runners { return NO_RETURN; } - public static Object execMakeVar(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { + public static Object execMakeVar(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var name = (String)instr.get(0); - frame.function.environment.global.define(name); + ctx.function.global.define(name); frame.codePtr++; return NO_RETURN; } - public static Object execDefProp(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { + public static Object execDefProp(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var setter = frame.pop(); var getter = frame.pop(); var name = frame.pop(); @@ -88,7 +84,7 @@ public class Runners { frame.codePtr++; return NO_RETURN; } - public static Object execInstanceof(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { + public static Object execInstanceof(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var type = frame.pop(); var obj = frame.pop(); @@ -103,7 +99,7 @@ public class Runners { frame.codePtr++; return NO_RETURN; } - public static Object execKeys(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { + public static Object execKeys(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var val = frame.pop(); var arr = new ObjectValue(); @@ -123,13 +119,13 @@ public class Runners { return NO_RETURN; } - public static Object execTry(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { + public static Object execTry(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { frame.addTry(instr.get(0), instr.get(1), instr.get(2)); frame.codePtr++; return NO_RETURN; } - public static Object execDup(CallContext ctx, Instruction instr, CodeFrame frame) { + public static Object execDup(Context ctx, Instruction instr, CodeFrame frame) { int offset = instr.get(0), count = instr.get(1); for (var i = 0; i < count; i++) { @@ -139,7 +135,7 @@ public class Runners { frame.codePtr++; return NO_RETURN; } - public static Object execMove(CallContext ctx, Instruction instr, CodeFrame frame) { + public static Object execMove(Context ctx, Instruction instr, CodeFrame frame) { int offset = instr.get(0), count = instr.get(1); var tmp = frame.take(offset); @@ -151,43 +147,43 @@ public class Runners { frame.codePtr++; return NO_RETURN; } - public static Object execLoadUndefined(CallContext ctx, Instruction instr, CodeFrame frame) { + public static Object execLoadUndefined(Context ctx, Instruction instr, CodeFrame frame) { frame.push(ctx, null); frame.codePtr++; return NO_RETURN; } - public static Object execLoadValue(CallContext ctx, Instruction instr, CodeFrame frame) { + public static Object execLoadValue(Context ctx, Instruction instr, CodeFrame frame) { frame.push(ctx, instr.get(0)); frame.codePtr++; return NO_RETURN; } - public static Object execLoadVar(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { + public static Object execLoadVar(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var i = instr.get(0); - if (i instanceof String) frame.push(ctx, frame.function.environment.global.get(ctx, (String)i)); + if (i instanceof String) frame.push(ctx, ctx.function.global.get(ctx, (String)i)); else frame.push(ctx, frame.scope.get((int)i).get(ctx)); frame.codePtr++; return NO_RETURN; } - public static Object execLoadObj(CallContext ctx, Instruction instr, CodeFrame frame) { + public static Object execLoadObj(Context ctx, Instruction instr, CodeFrame frame) { frame.push(ctx, new ObjectValue()); frame.codePtr++; return NO_RETURN; } - public static Object execLoadGlob(CallContext ctx, Instruction instr, CodeFrame frame) { - frame.push(ctx, frame.function.environment.global.obj); + public static Object execLoadGlob(Context ctx, Instruction instr, CodeFrame frame) { + frame.push(ctx, ctx.function.global.obj); frame.codePtr++; return NO_RETURN; } - public static Object execLoadArr(CallContext ctx, Instruction instr, CodeFrame frame) { + public static Object execLoadArr(Context ctx, Instruction instr, CodeFrame frame) { var res = new ArrayValue(); res.setSize(instr.get(0)); frame.push(ctx, res); frame.codePtr++; return NO_RETURN; } - public static Object execLoadFunc(CallContext ctx, Instruction instr, CodeFrame frame) { + public static Object execLoadFunc(Context ctx, Instruction instr, CodeFrame frame) { int n = (Integer)instr.get(0); int localsN = (Integer)instr.get(1); int len = (Integer)instr.get(2); @@ -202,18 +198,17 @@ public class Runners { var body = new Instruction[end - start]; System.arraycopy(frame.function.body, start, body, 0, end - start); - var func = new CodeFunction("", localsN, len, frame.function.environment, captures, body); + var func = new CodeFunction(ctx.function, "", localsN, len, captures, body); frame.push(ctx, func); frame.codePtr += n; return NO_RETURN; } - public static Object execLoadMember(CallContext ctx, DebugCommand state, Instruction instr, CodeFrame frame) throws InterruptedException { + public static Object execLoadMember(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var key = frame.pop(); var obj = frame.pop(); try { - ctx.setData(CodeFrame.STOP_AT_START_KEY, state == DebugCommand.STEP_INTO); frame.push(ctx, Values.getMember(ctx, obj, key)); } catch (IllegalArgumentException e) { @@ -222,54 +217,53 @@ public class Runners { frame.codePtr++; return NO_RETURN; } - public static Object execLoadKeyMember(CallContext ctx, DebugCommand state, Instruction instr, CodeFrame frame) throws InterruptedException { + public static Object execLoadKeyMember(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { frame.push(ctx, instr.get(0)); - return execLoadMember(ctx, state, instr, frame); + return execLoadMember(ctx, instr, frame); } - public static Object execLoadRegEx(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { - frame.push(ctx, ctx.environment.regexConstructor.call(ctx, null, instr.get(0), instr.get(1))); + public static Object execLoadRegEx(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { + frame.push(ctx, ctx.function.regexConstructor.call(ctx, null, instr.get(0), instr.get(1))); frame.codePtr++; return NO_RETURN; } - public static Object execDiscard(CallContext ctx, Instruction instr, CodeFrame frame) { + public static Object execDiscard(Context ctx, Instruction instr, CodeFrame frame) { frame.pop(); frame.codePtr++; return NO_RETURN; } - public static Object execStoreMember(CallContext ctx, DebugCommand state, Instruction instr, CodeFrame frame) throws InterruptedException { + public static Object execStoreMember(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var val = frame.pop(); var key = frame.pop(); var obj = frame.pop(); - ctx.setData(CodeFrame.STOP_AT_START_KEY, state == DebugCommand.STEP_INTO); if (!Values.setMember(ctx, obj, key, val)) throw EngineException.ofSyntax("Can't set member '" + key + "'."); if ((boolean)instr.get(0)) frame.push(ctx, val); frame.codePtr++; return NO_RETURN; } - public static Object execStoreVar(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { + public static Object execStoreVar(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var val = (boolean)instr.get(1) ? frame.peek() : frame.pop(); var i = instr.get(0); - if (i instanceof String) frame.function.environment.global.set(ctx, (String)i, val); + if (i instanceof String) ctx.function.global.set(ctx, (String)i, val); else frame.scope.get((int)i).set(ctx, val); frame.codePtr++; return NO_RETURN; } - public static Object execStoreSelfFunc(CallContext ctx, Instruction instr, CodeFrame frame) { + public static Object execStoreSelfFunc(Context ctx, Instruction instr, CodeFrame frame) { frame.scope.locals[(int)instr.get(0)].set(ctx, frame.function); frame.codePtr++; return NO_RETURN; } - public static Object execJmp(CallContext ctx, Instruction instr, CodeFrame frame) { + public static Object execJmp(Context ctx, Instruction instr, CodeFrame frame) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; return NO_RETURN; } - public static Object execJmpIf(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { + public static Object execJmpIf(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { if (Values.toBoolean(frame.pop())) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; @@ -277,7 +271,7 @@ public class Runners { else frame.codePtr ++; return NO_RETURN; } - public static Object execJmpIfNot(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { + public static Object execJmpIfNot(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { if (Values.not(frame.pop())) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; @@ -286,7 +280,7 @@ public class Runners { return NO_RETURN; } - public static Object execIn(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { + public static Object execIn(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var obj = frame.pop(); var index = frame.pop(); @@ -294,13 +288,13 @@ public class Runners { frame.codePtr++; return NO_RETURN; } - public static Object execTypeof(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { + public static Object execTypeof(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { String name = instr.get(0); Object obj; if (name != null) { - if (frame.function.environment.global.has(ctx, name)) { - obj = frame.function.environment.global.get(ctx, name); + if (ctx.function.global.has(ctx, name)) { + obj = ctx.function.global.get(ctx, name); } else obj = null; } @@ -311,7 +305,7 @@ public class Runners { frame.codePtr++; return NO_RETURN; } - public static Object execNop(CallContext ctx, Instruction instr, CodeFrame frame) { + public static Object execNop(Context ctx, Instruction instr, CodeFrame frame) { if (instr.is(0, "dbg_names")) { var names = new String[instr.params.length - 1]; for (var i = 0; i < instr.params.length - 1; i++) { @@ -325,7 +319,7 @@ public class Runners { return NO_RETURN; } - public static Object execDelete(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { + public static Object execDelete(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { var key = frame.pop(); var val = frame.pop(); @@ -335,7 +329,7 @@ public class Runners { return NO_RETURN; } - public static Object execOperation(CallContext ctx, Instruction instr, CodeFrame frame) throws InterruptedException { + public static Object execOperation(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { Operation op = instr.get(0); var args = new Object[op.operands]; @@ -346,7 +340,7 @@ public class Runners { return NO_RETURN; } - public static Object exec(CallContext ctx, DebugCommand state, Instruction instr, CodeFrame frame) throws InterruptedException { + public static Object exec(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { // System.out.println(instr + "@" + instr.location); switch (instr.type) { case NOP: return execNop(ctx, instr, frame); @@ -354,8 +348,8 @@ public class Runners { case SIGNAL: return execSignal(ctx, instr, frame); case THROW: return execThrow(ctx, instr, frame); case THROW_SYNTAX: return execThrowSyntax(ctx, instr, frame); - case CALL: return execCall(ctx, state, instr, frame); - case CALL_NEW: return execCallNew(ctx, state, instr, frame); + case CALL: return execCall(ctx, instr, frame); + case CALL_NEW: return execCallNew(ctx, instr, frame); case TRY: return execTry(ctx, instr, frame); case DUP: return execDup(ctx, instr, frame); @@ -365,13 +359,13 @@ public class Runners { case LOAD_OBJ: return execLoadObj(ctx, instr, frame); case LOAD_ARR: return execLoadArr(ctx, instr, frame); case LOAD_FUNC: return execLoadFunc(ctx, instr, frame); - case LOAD_MEMBER: return execLoadMember(ctx, state, instr, frame); - case LOAD_VAL_MEMBER: return execLoadKeyMember(ctx, state, instr, frame); + case LOAD_MEMBER: return execLoadMember(ctx, instr, frame); + case LOAD_VAL_MEMBER: return execLoadKeyMember(ctx, instr, frame); case LOAD_REGEX: return execLoadRegEx(ctx, instr, frame); case LOAD_GLOB: return execLoadGlob(ctx, instr, frame); case DISCARD: return execDiscard(ctx, instr, frame); - case STORE_MEMBER: return execStoreMember(ctx, state, instr, frame); + case STORE_MEMBER: return execStoreMember(ctx, instr, frame); case STORE_VAR: return execStoreVar(ctx, instr, frame); case STORE_SELF_FUNC: return execStoreSelfFunc(ctx, instr, frame); case MAKE_VAR: return execMakeVar(ctx, instr, frame); diff --git a/src/me/topchetoeu/jscript/engine/scope/GlobalScope.java b/src/me/topchetoeu/jscript/engine/scope/GlobalScope.java index 38fac3a..e447f2a 100644 --- a/src/me/topchetoeu/jscript/engine/scope/GlobalScope.java +++ b/src/me/topchetoeu/jscript/engine/scope/GlobalScope.java @@ -3,7 +3,7 @@ package me.topchetoeu.jscript.engine.scope; import java.util.HashSet; import java.util.Set; -import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.NativeFunction; import me.topchetoeu.jscript.engine.values.ObjectValue; @@ -15,7 +15,7 @@ public class GlobalScope implements ScopeRecord { @Override public GlobalScope parent() { return null; } - public boolean has(CallContext ctx, String name) throws InterruptedException { + public boolean has(Context ctx, String name) throws InterruptedException { return obj.hasMember(ctx, name, false); } public Object getKey(String name) { @@ -49,21 +49,21 @@ public class GlobalScope implements ScopeRecord { true, true ); } - public void define(CallContext ctx, String name, boolean readonly, Object val) { + public void define(Context ctx, String name, boolean readonly, Object val) { obj.defineProperty(ctx, name, val, readonly, true, true); } - public void define(String... names) { + public void define(String ...names) { for (var n : names) define(n); } public void define(boolean readonly, FunctionValue val) { define(null, val.name, readonly, val); } - public Object get(CallContext ctx, String name) throws InterruptedException { + public Object get(Context ctx, String name) throws InterruptedException { if (!obj.hasMember(ctx, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist."); else return obj.getMember(ctx, name); } - public void set(CallContext ctx, String name, Object val) throws InterruptedException { + public void set(Context ctx, String name, Object val) throws InterruptedException { if (!obj.hasMember(ctx, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist."); if (!obj.setMember(ctx, name, val, false)) throw EngineException.ofSyntax("The global '" + name + "' is readonly."); } diff --git a/src/me/topchetoeu/jscript/engine/scope/LocalScopeRecord.java b/src/me/topchetoeu/jscript/engine/scope/LocalScopeRecord.java index 5839e19..7d5cf12 100644 --- a/src/me/topchetoeu/jscript/engine/scope/LocalScopeRecord.java +++ b/src/me/topchetoeu/jscript/engine/scope/LocalScopeRecord.java @@ -2,7 +2,7 @@ package me.topchetoeu.jscript.engine.scope; import java.util.ArrayList; -import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Context; public class LocalScopeRecord implements ScopeRecord { public final LocalScopeRecord parent; @@ -59,7 +59,7 @@ public class LocalScopeRecord implements ScopeRecord { return name; } - public boolean has(CallContext ctx, String name) throws InterruptedException { + public boolean has(Context ctx, String name) throws InterruptedException { return global.has(ctx, name) || locals.contains(name) || diff --git a/src/me/topchetoeu/jscript/engine/scope/ValueVariable.java b/src/me/topchetoeu/jscript/engine/scope/ValueVariable.java index 2d68066..8a6797c 100644 --- a/src/me/topchetoeu/jscript/engine/scope/ValueVariable.java +++ b/src/me/topchetoeu/jscript/engine/scope/ValueVariable.java @@ -1,6 +1,6 @@ package me.topchetoeu.jscript.engine.scope; -import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.values.Values; public class ValueVariable implements Variable { @@ -11,12 +11,12 @@ public class ValueVariable implements Variable { public boolean readonly() { return readonly; } @Override - public Object get(CallContext ctx) { + public Object get(Context ctx) { return value; } @Override - public void set(CallContext ctx, Object val) { + public void set(Context ctx, Object val) { if (readonly) return; this.value = Values.normalize(ctx, val); } diff --git a/src/me/topchetoeu/jscript/engine/scope/Variable.java b/src/me/topchetoeu/jscript/engine/scope/Variable.java index 83b724c..6bd3394 100644 --- a/src/me/topchetoeu/jscript/engine/scope/Variable.java +++ b/src/me/topchetoeu/jscript/engine/scope/Variable.java @@ -1,9 +1,9 @@ package me.topchetoeu.jscript.engine.scope; -import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Context; public interface Variable { - Object get(CallContext ctx) throws InterruptedException; + Object get(Context ctx) throws InterruptedException; default boolean readonly() { return true; } - default void set(CallContext ctx, Object val) throws InterruptedException { } + default void set(Context ctx, Object val) throws InterruptedException { } } diff --git a/src/me/topchetoeu/jscript/engine/values/ArrayValue.java b/src/me/topchetoeu/jscript/engine/values/ArrayValue.java index 9278ed4..0bd59ea 100644 --- a/src/me/topchetoeu/jscript/engine/values/ArrayValue.java +++ b/src/me/topchetoeu/jscript/engine/values/ArrayValue.java @@ -5,7 +5,7 @@ import java.util.Collection; import java.util.Comparator; import java.util.List; -import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Context; public class ArrayValue extends ObjectValue { private static final Object EMPTY = new Object(); @@ -29,7 +29,7 @@ public class ArrayValue extends ObjectValue { if (res == EMPTY) return null; else return res; } - public void set(CallContext ctx, int i, Object val) { + public void set(Context ctx, int i, Object val) { if (i < 0) return; while (values.size() <= i) { @@ -83,7 +83,7 @@ public class ArrayValue extends ObjectValue { } @Override - protected Object getField(CallContext ctx, Object key) throws InterruptedException { + protected Object getField(Context ctx, Object key) throws InterruptedException { if (key.equals("length")) return values.size(); if (key instanceof Number) { var i = ((Number)key).doubleValue(); @@ -95,7 +95,7 @@ public class ArrayValue extends ObjectValue { return super.getField(ctx, key); } @Override - protected boolean setField(CallContext ctx, Object key, Object val) throws InterruptedException { + protected boolean setField(Context ctx, Object key, Object val) throws InterruptedException { if (key.equals("length")) { return setSize((int)Values.toNumber(ctx, val)); } @@ -110,7 +110,7 @@ public class ArrayValue extends ObjectValue { return super.setField(ctx, key, val); } @Override - protected boolean hasField(CallContext ctx, Object key) throws InterruptedException { + protected boolean hasField(Context ctx, Object key) throws InterruptedException { if (key.equals("length")) return true; if (key instanceof Number) { var i = Values.number(key); @@ -122,7 +122,7 @@ public class ArrayValue extends ObjectValue { return super.hasField(ctx, key); } @Override - protected void deleteField(CallContext ctx, Object key) throws InterruptedException { + protected void deleteField(Context ctx, Object key) throws InterruptedException { if (key instanceof Number) { var i = Values.number(key); if (i >= 0 && i - Math.floor(i) == 0) { @@ -149,12 +149,12 @@ public class ArrayValue extends ObjectValue { nonEnumerableSet.add("length"); nonConfigurableSet.add("length"); } - public ArrayValue(CallContext ctx, Object ...values) { + public ArrayValue(Context ctx, Object ...values) { this(); for (var i = 0; i < values.length; i++) this.values.add(Values.normalize(ctx, values[i])); } - public static ArrayValue of(CallContext ctx, Collection values) { + public static ArrayValue of(Context ctx, Collection values) { return new ArrayValue(ctx, values.toArray(Object[]::new)); } } diff --git a/src/me/topchetoeu/jscript/engine/values/CodeFunction.java b/src/me/topchetoeu/jscript/engine/values/CodeFunction.java index e2ff92e..d70b20d 100644 --- a/src/me/topchetoeu/jscript/engine/values/CodeFunction.java +++ b/src/me/topchetoeu/jscript/engine/values/CodeFunction.java @@ -1,12 +1,9 @@ package me.topchetoeu.jscript.engine.values; -import java.util.LinkedHashMap; - import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.compilation.Instruction; -import me.topchetoeu.jscript.compilation.Instruction.Type; -import me.topchetoeu.jscript.engine.CallContext; -import me.topchetoeu.jscript.engine.Environment; +import me.topchetoeu.jscript.engine.Context; +import me.topchetoeu.jscript.engine.FunctionContext; import me.topchetoeu.jscript.engine.frame.CodeFrame; import me.topchetoeu.jscript.engine.scope.ValueVariable; @@ -14,10 +11,8 @@ public class CodeFunction extends FunctionValue { public final int localsN; public final int length; public final Instruction[] body; - public final LinkedHashMap breakableLocToIndex = new LinkedHashMap<>(); - public final LinkedHashMap breakableIndexToLoc = new LinkedHashMap<>(); public final ValueVariable[] captures; - public Environment environment; + public FunctionContext environment; public Location loc() { for (var instr : body) { @@ -33,24 +28,16 @@ public class CodeFunction extends FunctionValue { } @Override - public Object call(CallContext ctx, Object thisArg, Object... args) throws InterruptedException { - return new CodeFrame(ctx, thisArg, args, this).run(new CallContext(ctx, environment)); + public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException { + return new CodeFrame(ctx, thisArg, args, this).run(new Context(environment, ctx.message)); } - public CodeFunction(String name, int localsN, int length, Environment environment, ValueVariable[] captures, Instruction[] body) { + public CodeFunction(FunctionContext environment, String name, int localsN, int length, ValueVariable[] captures, Instruction[] body) { super(name, length); this.captures = captures; this.environment = environment; this.localsN = localsN; this.length = length; this.body = body; - - for (var i = 0; i < body.length; i++) { - if (body[i].type == Type.LOAD_FUNC) i += (int)body[i].get(0); - if (body[i].debugged && body[i].location != null) { - breakableLocToIndex.put(body[i].location, i); - breakableIndexToLoc.put(i, body[i].location); - } - } } } diff --git a/src/me/topchetoeu/jscript/engine/values/FunctionValue.java b/src/me/topchetoeu/jscript/engine/values/FunctionValue.java index aaf1061..612f9b8 100644 --- a/src/me/topchetoeu/jscript/engine/values/FunctionValue.java +++ b/src/me/topchetoeu/jscript/engine/values/FunctionValue.java @@ -2,7 +2,7 @@ package me.topchetoeu.jscript.engine.values; import java.util.List; -import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Context; public abstract class FunctionValue extends ObjectValue { public String name = ""; @@ -11,29 +11,29 @@ public abstract class FunctionValue extends ObjectValue { @Override public String toString() { - return "function(...) { ... }"; + return "function(...) { ...}"; } - public abstract Object call(CallContext ctx, Object thisArg, Object... args) throws InterruptedException; - public Object call(CallContext ctx) throws InterruptedException { + public abstract Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException; + public Object call(Context ctx) throws InterruptedException { return call(ctx, null); } @Override - protected Object getField(CallContext ctx, Object key) throws InterruptedException { + protected Object getField(Context ctx, Object key) throws InterruptedException { if (key.equals("name")) return name; if (key.equals("length")) return length; return super.getField(ctx, key); } @Override - protected boolean setField(CallContext ctx, Object key, Object val) throws InterruptedException { + protected boolean setField(Context ctx, Object key, Object val) throws InterruptedException { if (key.equals("name")) name = Values.toString(ctx, val); else if (key.equals("length")) length = (int)Values.toNumber(ctx, val); else return super.setField(ctx, key, val); return true; } @Override - protected boolean hasField(CallContext ctx, Object key) throws InterruptedException { + protected boolean hasField(Context ctx, Object key) throws InterruptedException { if (key.equals("name")) return true; if (key.equals("length")) return true; return super.hasField(ctx, key); diff --git a/src/me/topchetoeu/jscript/engine/values/NativeFunction.java b/src/me/topchetoeu/jscript/engine/values/NativeFunction.java index c6a8be2..2056717 100644 --- a/src/me/topchetoeu/jscript/engine/values/NativeFunction.java +++ b/src/me/topchetoeu/jscript/engine/values/NativeFunction.java @@ -1,16 +1,16 @@ package me.topchetoeu.jscript.engine.values; -import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Context; public class NativeFunction extends FunctionValue { public static interface NativeFunctionRunner { - Object run(CallContext ctx, Object thisArg, Object[] args) throws InterruptedException; + Object run(Context ctx, Object thisArg, Object[] args) throws InterruptedException; } public final NativeFunctionRunner action; @Override - public Object call(CallContext ctx, Object thisArg, Object... args) throws InterruptedException { + public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException { return action.run(ctx, thisArg, args); } diff --git a/src/me/topchetoeu/jscript/engine/values/NativeWrapper.java b/src/me/topchetoeu/jscript/engine/values/NativeWrapper.java index 50efae2..753521d 100644 --- a/src/me/topchetoeu/jscript/engine/values/NativeWrapper.java +++ b/src/me/topchetoeu/jscript/engine/values/NativeWrapper.java @@ -1,14 +1,14 @@ package me.topchetoeu.jscript.engine.values; -import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Context; public class NativeWrapper extends ObjectValue { private static final Object NATIVE_PROTO = new Object(); public final Object wrapped; @Override - public ObjectValue getPrototype(CallContext ctx) throws InterruptedException { - if (prototype == NATIVE_PROTO) return ctx.environment.wrappersProvider.getProto(wrapped.getClass()); + public ObjectValue getPrototype(Context ctx) throws InterruptedException { + if (prototype == NATIVE_PROTO) return ctx.function.wrappersProvider.getProto(wrapped.getClass()); else return super.getPrototype(ctx); } diff --git a/src/me/topchetoeu/jscript/engine/values/ObjectValue.java b/src/me/topchetoeu/jscript/engine/values/ObjectValue.java index ca0fb55..9986cc8 100644 --- a/src/me/topchetoeu/jscript/engine/values/ObjectValue.java +++ b/src/me/topchetoeu/jscript/engine/values/ObjectValue.java @@ -6,7 +6,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; -import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Context; public class ObjectValue { public static enum PlaceholderProto { @@ -78,7 +78,7 @@ public class ObjectValue { state = State.FROZEN; } - public final boolean defineProperty(CallContext ctx, Object key, Object val, boolean writable, boolean configurable, boolean enumerable) { + public final boolean defineProperty(Context ctx, Object key, Object val, boolean writable, boolean configurable, boolean enumerable) { key = Values.normalize(ctx, key); val = Values.normalize(ctx, val); boolean reconfigured = writable != memberWritable(key) || @@ -118,10 +118,10 @@ public class ObjectValue { values.put(key, val); return true; } - public final boolean defineProperty(CallContext ctx, Object key, Object val) { + public final boolean defineProperty(Context ctx, Object key, Object val) { return defineProperty(ctx, key, val, true, true, true); } - public final boolean defineProperty(CallContext ctx, Object key, FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) { + public final boolean defineProperty(Context ctx, Object key, FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) { key = Values.normalize(ctx, key); if ( properties.containsKey(key) && @@ -145,15 +145,15 @@ public class ObjectValue { return true; } - public ObjectValue getPrototype(CallContext ctx) throws InterruptedException { + public ObjectValue getPrototype(Context ctx) throws InterruptedException { try { - if (prototype == OBJ_PROTO) return ctx.environment.proto("object"); - if (prototype == ARR_PROTO) return ctx.environment.proto("array"); - if (prototype == FUNC_PROTO) return ctx.environment.proto("function"); - if (prototype == ERR_PROTO) return ctx.environment.proto("error"); - if (prototype == RANGE_ERR_PROTO) return ctx.environment.proto("rangeErr"); - if (prototype == SYNTAX_ERR_PROTO) return ctx.environment.proto("syntaxErr"); - if (prototype == TYPE_ERR_PROTO) return ctx.environment.proto("typeErr"); + if (prototype == OBJ_PROTO) return ctx.function.proto("object"); + if (prototype == ARR_PROTO) return ctx.function.proto("array"); + if (prototype == FUNC_PROTO) return ctx.function.proto("function"); + if (prototype == ERR_PROTO) return ctx.function.proto("error"); + if (prototype == RANGE_ERR_PROTO) return ctx.function.proto("rangeErr"); + if (prototype == SYNTAX_ERR_PROTO) return ctx.function.proto("syntaxErr"); + if (prototype == TYPE_ERR_PROTO) return ctx.function.proto("typeErr"); } catch (NullPointerException e) { return null; @@ -161,7 +161,7 @@ public class ObjectValue { return (ObjectValue)prototype; } - public final boolean setPrototype(CallContext ctx, Object val) { + public final boolean setPrototype(Context ctx, Object val) { val = Values.normalize(ctx, val); if (!extensible()) return false; @@ -172,14 +172,14 @@ public class ObjectValue { else if (Values.isObject(val)) { var obj = Values.object(val); - if (ctx != null && ctx.environment != null) { - if (obj == ctx.environment.proto("object")) prototype = OBJ_PROTO; - else if (obj == ctx.environment.proto("array")) prototype = ARR_PROTO; - else if (obj == ctx.environment.proto("function")) prototype = FUNC_PROTO; - else if (obj == ctx.environment.proto("error")) prototype = ERR_PROTO; - else if (obj == ctx.environment.proto("syntaxErr")) prototype = SYNTAX_ERR_PROTO; - else if (obj == ctx.environment.proto("typeErr")) prototype = TYPE_ERR_PROTO; - else if (obj == ctx.environment.proto("rangeErr")) prototype = RANGE_ERR_PROTO; + if (ctx != null && ctx.function != null) { + if (obj == ctx.function.proto("object")) prototype = OBJ_PROTO; + else if (obj == ctx.function.proto("array")) prototype = ARR_PROTO; + else if (obj == ctx.function.proto("function")) prototype = FUNC_PROTO; + else if (obj == ctx.function.proto("error")) prototype = ERR_PROTO; + else if (obj == ctx.function.proto("syntaxErr")) prototype = SYNTAX_ERR_PROTO; + else if (obj == ctx.function.proto("typeErr")) prototype = TYPE_ERR_PROTO; + else if (obj == ctx.function.proto("rangeErr")) prototype = RANGE_ERR_PROTO; else prototype = obj; } else prototype = obj; @@ -203,19 +203,19 @@ public class ObjectValue { return true; } - protected Property getProperty(CallContext ctx, Object key) throws InterruptedException { + protected Property getProperty(Context ctx, Object key) throws InterruptedException { if (properties.containsKey(key)) return properties.get(key); var proto = getPrototype(ctx); if (proto != null) return proto.getProperty(ctx, key); else return null; } - protected Object getField(CallContext ctx, Object key) throws InterruptedException { + protected Object getField(Context ctx, Object key) throws InterruptedException { if (values.containsKey(key)) return values.get(key); var proto = getPrototype(ctx); if (proto != null) return proto.getField(ctx, key); else return null; } - protected boolean setField(CallContext ctx, Object key, Object val) throws InterruptedException { + protected boolean setField(Context ctx, Object key, Object val) throws InterruptedException { if (val instanceof FunctionValue && ((FunctionValue)val).name.equals("")) { ((FunctionValue)val).name = Values.toString(ctx, key); } @@ -223,14 +223,14 @@ public class ObjectValue { values.put(key, val); return true; } - protected void deleteField(CallContext ctx, Object key) throws InterruptedException { + protected void deleteField(Context ctx, Object key) throws InterruptedException { values.remove(key); } - protected boolean hasField(CallContext ctx, Object key) throws InterruptedException { + protected boolean hasField(Context ctx, Object key) throws InterruptedException { return values.containsKey(key); } - public final Object getMember(CallContext ctx, Object key, Object thisArg) throws InterruptedException { + public final Object getMember(Context ctx, Object key, Object thisArg) throws InterruptedException { key = Values.normalize(ctx, key); if (key.equals("__proto__")) { @@ -246,11 +246,11 @@ public class ObjectValue { } else return getField(ctx, key); } - public final Object getMember(CallContext ctx, Object key) throws InterruptedException { + public final Object getMember(Context ctx, Object key) throws InterruptedException { return getMember(ctx, key, this); } - public final boolean setMember(CallContext ctx, Object key, Object val, Object thisArg, boolean onlyProps) throws InterruptedException { + public final boolean setMember(Context ctx, Object key, Object val, Object thisArg, boolean onlyProps) throws InterruptedException { key = Values.normalize(ctx, key); val = Values.normalize(ctx, val); var prop = getProperty(ctx, key); @@ -269,11 +269,11 @@ public class ObjectValue { else if (nonWritableSet.contains(key)) return false; else return setField(ctx, key, val); } - public final boolean setMember(CallContext ctx, Object key, Object val, boolean onlyProps) throws InterruptedException { + public final boolean setMember(Context ctx, Object key, Object val, boolean onlyProps) throws InterruptedException { return setMember(ctx, Values.normalize(ctx, key), Values.normalize(ctx, val), this, onlyProps); } - public final boolean hasMember(CallContext ctx, Object key, boolean own) throws InterruptedException { + public final boolean hasMember(Context ctx, Object key, boolean own) throws InterruptedException { key = Values.normalize(ctx, key); if (key != null && key.equals("__proto__")) return true; @@ -283,7 +283,7 @@ public class ObjectValue { var proto = getPrototype(ctx); return proto != null && proto.hasMember(ctx, key, own); } - public final boolean deleteMember(CallContext ctx, Object key) throws InterruptedException { + public final boolean deleteMember(Context ctx, Object key) throws InterruptedException { key = Values.normalize(ctx, key); if (!memberConfigurable(key)) return false; @@ -294,7 +294,7 @@ public class ObjectValue { return true; } - public final ObjectValue getMemberDescriptor(CallContext ctx, Object key) throws InterruptedException { + public final ObjectValue getMemberDescriptor(Context ctx, Object key) throws InterruptedException { key = Values.normalize(ctx, key); var prop = properties.get(key); @@ -330,7 +330,7 @@ public class ObjectValue { return res; } - public ObjectValue(CallContext ctx, Map values) { + public ObjectValue(Context ctx, Map values) { this(PlaceholderProto.OBJECT); for (var el : values.entrySet()) { defineProperty(ctx, el.getKey(), el.getValue()); diff --git a/src/me/topchetoeu/jscript/engine/values/Values.java b/src/me/topchetoeu/jscript/engine/values/Values.java index 5f7f4ca..73f7029 100644 --- a/src/me/topchetoeu/jscript/engine/values/Values.java +++ b/src/me/topchetoeu/jscript/engine/values/Values.java @@ -10,7 +10,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.frame.ConvertHint; import me.topchetoeu.jscript.exceptions.EngineException; @@ -65,7 +65,7 @@ public class Values { return "object"; } - private static Object tryCallConvertFunc(CallContext ctx, Object obj, String name) throws InterruptedException { + private static Object tryCallConvertFunc(Context ctx, Object obj, String name) throws InterruptedException { var func = getMember(ctx, obj, name); if (func != null) { @@ -87,7 +87,7 @@ public class Values { obj == NULL; } - public static Object toPrimitive(CallContext ctx, Object obj, ConvertHint hint) throws InterruptedException { + public static Object toPrimitive(Context ctx, Object obj, ConvertHint hint) throws InterruptedException { obj = normalize(ctx, obj); if (isPrimitive(obj)) return obj; @@ -112,7 +112,7 @@ public class Values { if (obj instanceof Boolean) return (Boolean)obj; return true; } - public static double toNumber(CallContext ctx, Object obj) throws InterruptedException { + public static double toNumber(Context ctx, Object obj) throws InterruptedException { var val = toPrimitive(ctx, obj, ConvertHint.VALUEOF); if (val instanceof Number) return number(obj); @@ -125,7 +125,7 @@ public class Values { } return Double.NaN; } - public static String toString(CallContext ctx, Object obj) throws InterruptedException { + public static String toString(Context ctx, Object obj) throws InterruptedException { var val = toPrimitive(ctx, obj, ConvertHint.VALUEOF); if (val == null) return "undefined"; @@ -146,47 +146,47 @@ public class Values { return "Unknown value"; } - public static Object add(CallContext ctx, Object a, Object b) throws InterruptedException { + public static Object add(Context ctx, Object a, Object b) throws InterruptedException { if (a instanceof String || b instanceof String) return toString(ctx, a) + toString(ctx, b); else return toNumber(ctx, a) + toNumber(ctx, b); } - public static double subtract(CallContext ctx, Object a, Object b) throws InterruptedException { + public static double subtract(Context ctx, Object a, Object b) throws InterruptedException { return toNumber(ctx, a) - toNumber(ctx, b); } - public static double multiply(CallContext ctx, Object a, Object b) throws InterruptedException { + public static double multiply(Context ctx, Object a, Object b) throws InterruptedException { return toNumber(ctx, a) * toNumber(ctx, b); } - public static double divide(CallContext ctx, Object a, Object b) throws InterruptedException { + public static double divide(Context ctx, Object a, Object b) throws InterruptedException { return toNumber(ctx, a) / toNumber(ctx, b); } - public static double modulo(CallContext ctx, Object a, Object b) throws InterruptedException { + public static double modulo(Context ctx, Object a, Object b) throws InterruptedException { return toNumber(ctx, a) % toNumber(ctx, b); } - public static double negative(CallContext ctx, Object obj) throws InterruptedException { + public static double negative(Context ctx, Object obj) throws InterruptedException { return -toNumber(ctx, obj); } - public static int and(CallContext ctx, Object a, Object b) throws InterruptedException { + public static int and(Context ctx, Object a, Object b) throws InterruptedException { return (int)toNumber(ctx, a) & (int)toNumber(ctx, b); } - public static int or(CallContext ctx, Object a, Object b) throws InterruptedException { + public static int or(Context ctx, Object a, Object b) throws InterruptedException { return (int)toNumber(ctx, a) | (int)toNumber(ctx, b); } - public static int xor(CallContext ctx, Object a, Object b) throws InterruptedException { + public static int xor(Context ctx, Object a, Object b) throws InterruptedException { return (int)toNumber(ctx, a) ^ (int)toNumber(ctx, b); } - public static int bitwiseNot(CallContext ctx, Object obj) throws InterruptedException { + public static int bitwiseNot(Context ctx, Object obj) throws InterruptedException { return ~(int)toNumber(ctx, obj); } - public static int shiftLeft(CallContext ctx, Object a, Object b) throws InterruptedException { + public static int shiftLeft(Context ctx, Object a, Object b) throws InterruptedException { return (int)toNumber(ctx, a) << (int)toNumber(ctx, b); } - public static int shiftRight(CallContext ctx, Object a, Object b) throws InterruptedException { + public static int shiftRight(Context ctx, Object a, Object b) throws InterruptedException { return (int)toNumber(ctx, a) >> (int)toNumber(ctx, b); } - public static long unsignedShiftRight(CallContext ctx, Object a, Object b) throws InterruptedException { + public static long unsignedShiftRight(Context ctx, Object a, Object b) throws InterruptedException { long _a = (long)toNumber(ctx, a); long _b = (long)toNumber(ctx, b); @@ -195,7 +195,7 @@ public class Values { return _a >>> _b; } - public static int compare(CallContext ctx, Object a, Object b) throws InterruptedException { + public static int compare(Context ctx, Object a, Object b) throws InterruptedException { a = toPrimitive(ctx, a, ConvertHint.VALUEOF); b = toPrimitive(ctx, b, ConvertHint.VALUEOF); @@ -207,7 +207,7 @@ public class Values { return !toBoolean(obj); } - public static boolean isInstanceOf(CallContext ctx, Object obj, Object proto) throws InterruptedException { + public static boolean isInstanceOf(Context ctx, Object obj, Object proto) throws InterruptedException { if (obj == null || obj == NULL || proto == null || proto == NULL) return false; var val = getPrototype(ctx, obj); @@ -219,7 +219,7 @@ public class Values { return false; } - public static Object operation(CallContext ctx, Operation op, Object... args) throws InterruptedException { + public static Object operation(Context ctx, Operation op, Object ...args) throws InterruptedException { switch (op) { case ADD: return add(ctx, args[0], args[1]); case SUBTRACT: return subtract(ctx, args[0], args[1]); @@ -260,7 +260,7 @@ public class Values { } } - public static Object getMember(CallContext ctx, Object obj, Object key) throws InterruptedException { + public static Object getMember(Context ctx, Object obj, Object key) throws InterruptedException { obj = normalize(ctx, obj); key = normalize(ctx, key); if (obj == null) throw new IllegalArgumentException("Tried to access member of undefined."); if (obj == NULL) throw new IllegalArgumentException("Tried to access member of null."); @@ -280,7 +280,7 @@ public class Values { else if (key != null && key.equals("__proto__")) return proto; else return proto.getMember(ctx, key, obj); } - public static boolean setMember(CallContext ctx, Object obj, Object key, Object val) throws InterruptedException { + public static boolean setMember(Context ctx, Object obj, Object key, Object val) throws InterruptedException { obj = normalize(ctx, obj); key = normalize(ctx, key); val = normalize(ctx, val); if (obj == null) throw EngineException.ofType("Tried to access member of undefined."); if (obj == NULL) throw EngineException.ofType("Tried to access member of null."); @@ -290,7 +290,7 @@ public class Values { var proto = getPrototype(ctx, obj); return proto.setMember(ctx, key, val, obj, true); } - public static boolean hasMember(CallContext ctx, Object obj, Object key, boolean own) throws InterruptedException { + public static boolean hasMember(Context ctx, Object obj, Object key, boolean own) throws InterruptedException { if (obj == null || obj == NULL) return false; obj = normalize(ctx, obj); key = normalize(ctx, key); @@ -308,31 +308,31 @@ public class Values { var proto = getPrototype(ctx, obj); return proto != null && proto.hasMember(ctx, key, own); } - public static boolean deleteMember(CallContext ctx, Object obj, Object key) throws InterruptedException { + public static boolean deleteMember(Context ctx, Object obj, Object key) throws InterruptedException { if (obj == null || obj == NULL) return false; obj = normalize(ctx, obj); key = normalize(ctx, key); if (isObject(obj)) return object(obj).deleteMember(ctx, key); else return false; } - public static ObjectValue getPrototype(CallContext ctx, Object obj) throws InterruptedException { + public static ObjectValue getPrototype(Context ctx, Object obj) throws InterruptedException { if (obj == null || obj == NULL) return null; obj = normalize(ctx, obj); if (isObject(obj)) return object(obj).getPrototype(ctx); if (ctx == null) return null; - if (obj instanceof String) return ctx.environment.proto("string"); - else if (obj instanceof Number) return ctx.environment.proto("number"); - else if (obj instanceof Boolean) return ctx.environment.proto("bool"); - else if (obj instanceof Symbol) return ctx.environment.proto("symbol"); + if (obj instanceof String) return ctx.function.proto("string"); + else if (obj instanceof Number) return ctx.function.proto("number"); + else if (obj instanceof Boolean) return ctx.function.proto("bool"); + else if (obj instanceof Symbol) return ctx.function.proto("symbol"); return null; } - public static boolean setPrototype(CallContext ctx, Object obj, Object proto) throws InterruptedException { + public static boolean setPrototype(Context ctx, Object obj, Object proto) throws InterruptedException { obj = normalize(ctx, obj); return isObject(obj) && object(obj).setPrototype(ctx, proto); } - public static List getMembers(CallContext ctx, Object obj, boolean own, boolean includeNonEnumerable) throws InterruptedException { + public static List getMembers(Context ctx, Object obj, boolean own, boolean includeNonEnumerable) throws InterruptedException { List res = new ArrayList<>(); if (isObject(obj)) res = object(obj).keys(includeNonEnumerable); @@ -353,13 +353,13 @@ public class Values { return res; } - public static Object call(CallContext ctx, Object func, Object thisArg, Object ...args) throws InterruptedException { + public static Object call(Context ctx, Object func, Object thisArg, Object ...args) throws InterruptedException { if (!isFunction(func)) throw EngineException.ofType("Tried to call a non-function value."); return function(func).call(ctx, thisArg, args); } - public static boolean strictEquals(CallContext ctx, Object a, Object b) { + public static boolean strictEquals(Context ctx, Object a, Object b) { a = normalize(ctx, a); b = normalize(ctx, b); if (a == null || b == null) return a == null && b == null; @@ -369,7 +369,7 @@ public class Values { return a == b || a.equals(b); } - public static boolean looseEqual(CallContext ctx, Object a, Object b) throws InterruptedException { + public static boolean looseEqual(Context ctx, Object a, Object b) throws InterruptedException { a = normalize(ctx, a); b = normalize(ctx, b); // In loose equality, null is equivalent to undefined @@ -393,7 +393,7 @@ public class Values { return toString(ctx, a).equals(toString(ctx, b)); } - public static Object normalize(CallContext ctx, Object val) { + public static Object normalize(Context ctx, Object val) { if (val instanceof Number) return number(val); if (isPrimitive(val) || val instanceof ObjectValue) return val; if (val instanceof Character) return val + ""; @@ -420,14 +420,14 @@ public class Values { if (val instanceof Class) { if (ctx == null) return null; - else return ctx.environment.wrappersProvider.getConstr((Class)val); + else return ctx.function.wrappersProvider.getConstr((Class)val); } return new NativeWrapper(val); } @SuppressWarnings("unchecked") - public static T convert(CallContext ctx, Object obj, Class clazz) throws InterruptedException { + public static T convert(Context ctx, Object obj, Class clazz) throws InterruptedException { if (clazz == Void.class) return null; if (clazz == null || clazz == Object.class) return (T)obj; @@ -495,10 +495,10 @@ public class Values { throw err; } - public static Iterable toJavaIterable(CallContext ctx, Object obj) throws InterruptedException { + public static Iterable toJavaIterable(Context ctx, Object obj) throws InterruptedException { return () -> { try { - var constr = getMember(ctx, ctx.environment.proto("symbol"), "constructor"); + var constr = getMember(ctx, ctx.function.proto("symbol"), "constructor"); var symbol = getMember(ctx, constr, "iterator"); var iteratorFunc = getMember(ctx, obj, symbol); @@ -562,12 +562,12 @@ public class Values { }; } - public static ObjectValue fromJavaIterable(CallContext ctx, Iterable iterable) throws InterruptedException { + public static ObjectValue fromJavaIterable(Context ctx, Iterable iterable) throws InterruptedException { var res = new ObjectValue(); var it = iterable.iterator(); try { - var key = getMember(ctx, getMember(ctx, ctx.environment.proto("symbol"), "constructor"), "iterator"); + var key = getMember(ctx, getMember(ctx, ctx.function.proto("symbol"), "constructor"), "iterator"); res.defineProperty(ctx, key, new NativeFunction("", (_ctx, thisArg, args) -> thisArg)); } catch (IllegalArgumentException | NullPointerException e) { } @@ -580,7 +580,7 @@ public class Values { return res; } - private static void printValue(CallContext ctx, Object val, HashSet passed, int tab) throws InterruptedException { + private static void printValue(Context ctx, Object val, HashSet passed, int tab) throws InterruptedException { if (passed.contains(val)) { System.out.print("[circular]"); return; @@ -652,7 +652,7 @@ public class Values { else if (val instanceof String) System.out.print("'" + val + "'"); else System.out.print(Values.toString(ctx, val)); } - public static void printValue(CallContext ctx, Object val) throws InterruptedException { + public static void printValue(Context ctx, Object val) throws InterruptedException { printValue(ctx, val, new HashSet<>(), 0); } } diff --git a/src/me/topchetoeu/jscript/exceptions/EngineException.java b/src/me/topchetoeu/jscript/exceptions/EngineException.java index ccdece6..75adb6a 100644 --- a/src/me/topchetoeu/jscript/exceptions/EngineException.java +++ b/src/me/topchetoeu/jscript/exceptions/EngineException.java @@ -4,7 +4,7 @@ import java.util.ArrayList; import java.util.List; import me.topchetoeu.jscript.Location; -import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto; @@ -28,9 +28,14 @@ public class EngineException extends RuntimeException { return this; } - public String toString(CallContext ctx) throws InterruptedException { + public String toString(Context ctx) throws InterruptedException { var ss = new StringBuilder(); - ss.append(Values.toString(ctx, value)).append('\n'); + try { + ss.append(Values.toString(ctx, value)).append('\n'); + } + catch (EngineException e) { + ss.append("[Error while stringifying]\n"); + } for (var line : stackTrace) { ss.append(" ").append(line).append('\n'); } diff --git a/src/me/topchetoeu/jscript/filesystem/FilesystemRegister.java b/src/me/topchetoeu/jscript/filesystem/FilesystemRegister.java deleted file mode 100644 index 03940f5..0000000 --- a/src/me/topchetoeu/jscript/filesystem/FilesystemRegister.java +++ /dev/null @@ -1,7 +0,0 @@ -package me.topchetoeu.jscript.filesystem; - -import java.util.HashMap; - -public class FilesystemRegister { - private HashMap filesystems = new HashMap<>(); -} diff --git a/src/me/topchetoeu/jscript/interop/Overload.java b/src/me/topchetoeu/jscript/interop/Overload.java index b37a894..191f0e4 100644 --- a/src/me/topchetoeu/jscript/interop/Overload.java +++ b/src/me/topchetoeu/jscript/interop/Overload.java @@ -5,11 +5,11 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Context; public class Overload { public static interface OverloadRunner { - Object run(CallContext ctx, Object thisArg, Object[] args) throws + Object run(Context ctx, Object thisArg, Object[] args) throws InterruptedException, ReflectiveOperationException, IllegalArgumentException; diff --git a/src/me/topchetoeu/jscript/interop/OverloadFunction.java b/src/me/topchetoeu/jscript/interop/OverloadFunction.java index e8c78d2..321b04d 100644 --- a/src/me/topchetoeu/jscript/interop/OverloadFunction.java +++ b/src/me/topchetoeu/jscript/interop/OverloadFunction.java @@ -5,7 +5,7 @@ import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; -import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.exceptions.EngineException; @@ -13,9 +13,9 @@ import me.topchetoeu.jscript.exceptions.EngineException; public class OverloadFunction extends FunctionValue { public final List overloads = new ArrayList<>(); - public Object call(CallContext ctx, Object thisArg, Object... args) throws InterruptedException { + public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException { for (var overload : overloads) { - boolean consumesEngine = overload.params.length > 0 && overload.params[0] == CallContext.class; + boolean consumesEngine = overload.params.length > 0 && overload.params[0] == Context.class; int start = consumesEngine ? 1 : 0; int end = overload.params.length - (overload.variadic ? 1 : 0); diff --git a/src/me/topchetoeu/jscript/parsing/ParseRes.java b/src/me/topchetoeu/jscript/parsing/ParseRes.java index 307b405..1b8e2d0 100644 --- a/src/me/topchetoeu/jscript/parsing/ParseRes.java +++ b/src/me/topchetoeu/jscript/parsing/ParseRes.java @@ -68,7 +68,7 @@ public class ParseRes { } @SafeVarargs - public static ParseRes any(ParseRes... parsers) { + public static ParseRes any(ParseRes ...parsers) { ParseRes best = null; ParseRes error = ParseRes.failed(); @@ -83,7 +83,7 @@ public class ParseRes { else return error; } @SafeVarargs - public static ParseRes first(String filename, List tokens, Map> named, Parser... parsers) { + public static ParseRes first(String filename, List tokens, Map> named, Parser ...parsers) { ParseRes error = ParseRes.failed(); for (var parser : parsers) { diff --git a/src/me/topchetoeu/jscript/parsing/Parsing.java b/src/me/topchetoeu/jscript/parsing/Parsing.java index ea10930..cf95238 100644 --- a/src/me/topchetoeu/jscript/parsing/Parsing.java +++ b/src/me/topchetoeu/jscript/parsing/Parsing.java @@ -13,7 +13,7 @@ import me.topchetoeu.jscript.compilation.VariableDeclareStatement.Pair; import me.topchetoeu.jscript.compilation.control.*; import me.topchetoeu.jscript.compilation.control.SwitchStatement.SwitchCase; import me.topchetoeu.jscript.compilation.values.*; -import me.topchetoeu.jscript.engine.Environment; +import me.topchetoeu.jscript.engine.FunctionContext; import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.scope.ValueVariable; import me.topchetoeu.jscript.engine.values.CodeFunction; @@ -1842,7 +1842,7 @@ public class Parsing { return list.toArray(Statement[]::new); } - public static CodeFunction compile(Environment environment, Statement... statements) { + public static CodeFunction compile(FunctionContext environment, Statement ...statements) { var target = environment.global.globalChild(); var subscope = target.child(); var res = new ArrayList(); @@ -1870,14 +1870,14 @@ public class Parsing { } else res.add(Instruction.ret()); - return new CodeFunction("", subscope.localsCount(), 0, environment, new ValueVariable[0], res.toArray(Instruction[]::new)); + return new CodeFunction(environment, "", subscope.localsCount(), 0, new ValueVariable[0], res.toArray(Instruction[]::new)); } - public static CodeFunction compile(Environment environment, String filename, String raw) { + public static CodeFunction compile(FunctionContext environment, String filename, String raw) { try { return compile(environment, parse(filename, raw)); } catch (SyntaxException e) { - return new CodeFunction(null, 2, 0, environment, new ValueVariable[0], new Instruction[] { Instruction.throwSyntax(e) }); + return new CodeFunction(environment, null, 2, 0, new ValueVariable[0], new Instruction[] { Instruction.throwSyntax(e) }); } } } diff --git a/src/me/topchetoeu/jscript/polyfills/Date.java b/src/me/topchetoeu/jscript/polyfills/Date.java index 8582d11..8ce78a4 100644 --- a/src/me/topchetoeu/jscript/polyfills/Date.java +++ b/src/me/topchetoeu/jscript/polyfills/Date.java @@ -3,7 +3,7 @@ package me.topchetoeu.jscript.polyfills; import java.util.Calendar; import java.util.TimeZone; -import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.interop.Native; @@ -47,7 +47,7 @@ public class Date { return normal.get(Calendar.YEAR) - 1900; } @Native - public double setYear(CallContext ctx, Object val) throws InterruptedException { + public double setYear(Context ctx, Object val) throws InterruptedException { var real = Values.toNumber(ctx, val); if (real >= 0 && real <= 99) real = real + 1900; if (Double.isNaN(real)) invalidate(); @@ -139,7 +139,7 @@ public class Date { } @Native - public double setFullYear(CallContext ctx, Object val) throws InterruptedException { + public double setFullYear(Context ctx, Object val) throws InterruptedException { var real = Values.toNumber(ctx, val); if (Double.isNaN(real)) invalidate(); else normal.set(Calendar.YEAR, (int)real); @@ -147,7 +147,7 @@ public class Date { return getTime(); } @Native - public double setMonth(CallContext ctx, Object val) throws InterruptedException { + public double setMonth(Context ctx, Object val) throws InterruptedException { var real = Values.toNumber(ctx, val); if (Double.isNaN(real)) invalidate(); else normal.set(Calendar.MONTH, (int)real); @@ -155,7 +155,7 @@ public class Date { return getTime(); } @Native - public double setDate(CallContext ctx, Object val) throws InterruptedException { + public double setDate(Context ctx, Object val) throws InterruptedException { var real = Values.toNumber(ctx, val); if (Double.isNaN(real)) invalidate(); else normal.set(Calendar.DAY_OF_MONTH, (int)real); @@ -163,7 +163,7 @@ public class Date { return getTime(); } @Native - public double setDay(CallContext ctx, Object val) throws InterruptedException { + public double setDay(Context ctx, Object val) throws InterruptedException { var real = Values.toNumber(ctx, val); if (Double.isNaN(real)) invalidate(); else normal.set(Calendar.DAY_OF_WEEK, (int)real); @@ -171,7 +171,7 @@ public class Date { return getTime(); } @Native - public double setHours(CallContext ctx, Object val) throws InterruptedException { + public double setHours(Context ctx, Object val) throws InterruptedException { var real = Values.toNumber(ctx, val); if (Double.isNaN(real)) invalidate(); else normal.set(Calendar.HOUR_OF_DAY, (int)real); @@ -179,7 +179,7 @@ public class Date { return getTime(); } @Native - public double setMinutes(CallContext ctx, Object val) throws InterruptedException { + public double setMinutes(Context ctx, Object val) throws InterruptedException { var real = Values.toNumber(ctx, val); if (Double.isNaN(real)) invalidate(); else normal.set(Calendar.MINUTE, (int)real); @@ -187,7 +187,7 @@ public class Date { return getTime(); } @Native - public double setSeconds(CallContext ctx, Object val) throws InterruptedException { + public double setSeconds(Context ctx, Object val) throws InterruptedException { var real = Values.toNumber(ctx, val); if (Double.isNaN(real)) invalidate(); else normal.set(Calendar.SECOND, (int)real); @@ -195,7 +195,7 @@ public class Date { return getTime(); } @Native - public double setMilliseconds(CallContext ctx, Object val) throws InterruptedException { + public double setMilliseconds(Context ctx, Object val) throws InterruptedException { var real = Values.toNumber(ctx, val); if (Double.isNaN(real)) invalidate(); else normal.set(Calendar.MILLISECOND, (int)real); @@ -204,7 +204,7 @@ public class Date { } @Native - public double setUTCFullYear(CallContext ctx, Object val) throws InterruptedException { + public double setUTCFullYear(Context ctx, Object val) throws InterruptedException { var real = Values.toNumber(ctx, val); if (Double.isNaN(real)) invalidate(); else utc.set(Calendar.YEAR, (int)real); @@ -212,7 +212,7 @@ public class Date { return getTime(); } @Native - public double setUTCMonth(CallContext ctx, Object val) throws InterruptedException { + public double setUTCMonth(Context ctx, Object val) throws InterruptedException { var real = Values.toNumber(ctx, val); if (Double.isNaN(real)) invalidate(); else utc.set(Calendar.MONTH, (int)real); @@ -220,7 +220,7 @@ public class Date { return getTime(); } @Native - public double setUTCDate(CallContext ctx, Object val) throws InterruptedException { + public double setUTCDate(Context ctx, Object val) throws InterruptedException { var real = Values.toNumber(ctx, val); if (Double.isNaN(real)) invalidate(); else utc.set(Calendar.DAY_OF_MONTH, (int)real); @@ -228,7 +228,7 @@ public class Date { return getTime(); } @Native - public double setUTCDay(CallContext ctx, Object val) throws InterruptedException { + public double setUTCDay(Context ctx, Object val) throws InterruptedException { var real = Values.toNumber(ctx, val); if (Double.isNaN(real)) invalidate(); else utc.set(Calendar.DAY_OF_WEEK, (int)real); @@ -236,7 +236,7 @@ public class Date { return getTime(); } @Native - public double setUTCHours(CallContext ctx, Object val) throws InterruptedException { + public double setUTCHours(Context ctx, Object val) throws InterruptedException { var real = Values.toNumber(ctx, val); if (Double.isNaN(real)) invalidate(); else utc.set(Calendar.HOUR_OF_DAY, (int)real); @@ -244,7 +244,7 @@ public class Date { return getTime(); } @Native - public double setUTCMinutes(CallContext ctx, Object val) throws InterruptedException { + public double setUTCMinutes(Context ctx, Object val) throws InterruptedException { var real = Values.toNumber(ctx, val); if (Double.isNaN(real)) invalidate(); else utc.set(Calendar.MINUTE, (int)real); @@ -252,7 +252,7 @@ public class Date { return getTime(); } @Native - public double setUTCSeconds(CallContext ctx, Object val) throws InterruptedException { + public double setUTCSeconds(Context ctx, Object val) throws InterruptedException { var real = Values.toNumber(ctx, val); if (Double.isNaN(real)) invalidate(); else utc.set(Calendar.SECOND, (int)real); @@ -260,7 +260,7 @@ public class Date { return getTime(); } @Native - public double setUTCMilliseconds(CallContext ctx, Object val) throws InterruptedException { + public double setUTCMilliseconds(Context ctx, Object val) throws InterruptedException { var real = Values.toNumber(ctx, val); if (Double.isNaN(real)) invalidate(); else utc.set(Calendar.MILLISECOND, (int)real); diff --git a/src/me/topchetoeu/jscript/polyfills/GeneratorFunction.java b/src/me/topchetoeu/jscript/polyfills/GeneratorFunction.java index 159a01a..17b098f 100644 --- a/src/me/topchetoeu/jscript/polyfills/GeneratorFunction.java +++ b/src/me/topchetoeu/jscript/polyfills/GeneratorFunction.java @@ -1,6 +1,6 @@ package me.topchetoeu.jscript.polyfills; -import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.frame.CodeFrame; import me.topchetoeu.jscript.engine.frame.Runners; import me.topchetoeu.jscript.engine.values.CodeFunction; @@ -18,7 +18,7 @@ public class GeneratorFunction extends FunctionValue { private boolean done = false; public CodeFrame frame; - private ObjectValue next(CallContext ctx, Object inducedValue, Object inducedReturn, Object inducedError) throws InterruptedException { + private ObjectValue next(Context ctx, Object inducedValue, Object inducedReturn, Object inducedError) throws InterruptedException { if (done) { if (inducedError != Runners.NO_RETURN) throw new EngineException(inducedError); var res = new ObjectValue(); @@ -29,7 +29,7 @@ public class GeneratorFunction extends FunctionValue { Object res = null; if (inducedValue != Runners.NO_RETURN) frame.push(ctx, inducedValue); - frame.start(ctx); + ctx.message.pushFrame(frame); yielding = false; while (!yielding) { try { @@ -46,7 +46,7 @@ public class GeneratorFunction extends FunctionValue { } } - frame.end(ctx); + ctx.message.popFrame(frame); if (done) frame = null; else res = frame.pop(); @@ -57,16 +57,16 @@ public class GeneratorFunction extends FunctionValue { } @Native - public ObjectValue next(CallContext ctx, Object... args) throws InterruptedException { + public ObjectValue next(Context ctx, Object ...args) throws InterruptedException { if (args.length == 0) return next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, Runners.NO_RETURN); else return next(ctx, args[0], Runners.NO_RETURN, Runners.NO_RETURN); } @Native("throw") - public ObjectValue _throw(CallContext ctx, Object error) throws InterruptedException { + public ObjectValue _throw(Context ctx, Object error) throws InterruptedException { return next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, error); } @Native("return") - public ObjectValue _return(CallContext ctx, Object value) throws InterruptedException { + public ObjectValue _return(Context ctx, Object value) throws InterruptedException { return next(ctx, Runners.NO_RETURN, value, Runners.NO_RETURN); } @@ -77,14 +77,14 @@ public class GeneratorFunction extends FunctionValue { return "Generator " + (done ? "[closed]" : "[suspended]"); } - public Object yield(CallContext ctx, Object thisArg, Object[] args) { + public Object yield(Context ctx, Object thisArg, Object[] args) { this.yielding = true; return args.length > 0 ? args[0] : null; } } @Override - public Object call(CallContext ctx, Object thisArg, Object... args) throws InterruptedException { + public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException { var handler = new Generator(); var func = factory.call(ctx, thisArg, new NativeFunction("yield", handler::yield)); if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function."); diff --git a/src/me/topchetoeu/jscript/polyfills/Internals.java b/src/me/topchetoeu/jscript/polyfills/Internals.java index c6bee13..71fbbab 100644 --- a/src/me/topchetoeu/jscript/polyfills/Internals.java +++ b/src/me/topchetoeu/jscript/polyfills/Internals.java @@ -1,7 +1,7 @@ package me.topchetoeu.jscript.polyfills; -import me.topchetoeu.jscript.engine.CallContext; -import me.topchetoeu.jscript.engine.Environment; +import me.topchetoeu.jscript.engine.Context; +import me.topchetoeu.jscript.engine.FunctionContext; import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.CodeFunction; import me.topchetoeu.jscript.engine.values.FunctionValue; @@ -12,23 +12,23 @@ import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.interop.Native; public class Internals { - @Native public void markSpecial(FunctionValue... funcs) { + @Native public void markSpecial(FunctionValue ...funcs) { for (var func : funcs) { func.special = true; } } - @Native public Environment getEnv(Object func) { + @Native public FunctionContext getEnv(Object func) { if (func instanceof CodeFunction) return ((CodeFunction)func).environment; else return null; } - @Native public Object setEnv(Object func, Environment env) { + @Native public Object setEnv(Object func, FunctionContext env) { if (func instanceof CodeFunction) ((CodeFunction)func).environment = env; return func; } - @Native public Object apply(CallContext ctx, FunctionValue func, Object thisArg, ArrayValue args) throws InterruptedException { + @Native public Object apply(Context ctx, FunctionValue func, Object thisArg, ArrayValue args) throws InterruptedException { return func.call(ctx, thisArg, args.toArray()); } - @Native public FunctionValue delay(CallContext ctx, double delay, FunctionValue callback) throws InterruptedException { + @Native public FunctionValue delay(Context ctx, double delay, FunctionValue callback) throws InterruptedException { var thread = new Thread((Runnable)() -> { var ms = (long)delay; var ns = (int)((delay - ms) * 10000000); @@ -38,7 +38,7 @@ public class Internals { } catch (InterruptedException e) { return; } - ctx.engine.pushMsg(false, ctx.data(), ctx.environment, callback, null); + ctx.message.engine.pushMsg(false, ctx.message, callback, null); }); thread.start(); @@ -47,8 +47,8 @@ public class Internals { return null; }); } - @Native public void pushMessage(CallContext ctx, boolean micro, FunctionValue func, Object thisArg, Object[] args) { - ctx.engine.pushMsg(micro, ctx.data(), ctx.environment, func, thisArg, args); + @Native public void pushMessage(Context ctx, boolean micro, FunctionValue func, Object thisArg, Object[] args) { + ctx.message.engine.pushMsg(micro, ctx.message, func, thisArg, args); } @Native public int strlen(String str) { @@ -74,6 +74,13 @@ public class Internals { return str.value; } + @Native public static void log(Context ctx, Object ...args) throws InterruptedException { + for (var arg : args) { + Values.printValue(ctx, arg); + } + System.out.println(); + } + @Native public boolean isArray(Object obj) { return obj instanceof ArrayValue; } @@ -81,14 +88,14 @@ public class Internals { return new GeneratorFunction(obj); } - @Native public boolean defineField(CallContext ctx, ObjectValue obj, Object key, Object val, boolean writable, boolean enumerable, boolean configurable) { + @Native public boolean defineField(Context ctx, ObjectValue obj, Object key, Object val, boolean writable, boolean enumerable, boolean configurable) { return obj.defineProperty(ctx, key, val, writable, configurable, enumerable); } - @Native public boolean defineProp(CallContext ctx, ObjectValue obj, Object key, FunctionValue getter, FunctionValue setter, boolean enumerable, boolean configurable) { + @Native public boolean defineProp(Context ctx, ObjectValue obj, Object key, FunctionValue getter, FunctionValue setter, boolean enumerable, boolean configurable) { return obj.defineProperty(ctx, key, getter, setter, configurable, enumerable); } - @Native public ArrayValue keys(CallContext ctx, Object obj, boolean onlyString) throws InterruptedException { + @Native public ArrayValue keys(Context ctx, Object obj, boolean onlyString) throws InterruptedException { var res = new ArrayValue(); var i = 0; @@ -98,7 +105,7 @@ public class Internals { return res; } - @Native public ArrayValue ownPropKeys(CallContext ctx, Object obj, boolean symbols) throws InterruptedException { + @Native public ArrayValue ownPropKeys(Context ctx, Object obj, boolean symbols) throws InterruptedException { var res = new ArrayValue(); if (Values.isObject(obj)) { @@ -110,7 +117,7 @@ public class Internals { return res; } - @Native public ObjectValue ownProp(CallContext ctx, ObjectValue val, Object key) throws InterruptedException { + @Native public ObjectValue ownProp(Context ctx, ObjectValue val, Object key) throws InterruptedException { return val.getMemberDescriptor(ctx, key); } @Native public void lock(ObjectValue val, String type) { @@ -124,7 +131,7 @@ public class Internals { return val.extensible(); } - @Native public void sort(CallContext ctx, ArrayValue arr, FunctionValue cmp) { + @Native public void sort(Context ctx, ArrayValue arr, FunctionValue cmp) { arr.sort((a, b) -> { try { var res = Values.toNumber(ctx, cmp.call(ctx, null, a, b)); diff --git a/src/me/topchetoeu/jscript/polyfills/JSON.java b/src/me/topchetoeu/jscript/polyfills/JSON.java index 755c633..53873a5 100644 --- a/src/me/topchetoeu/jscript/polyfills/JSON.java +++ b/src/me/topchetoeu/jscript/polyfills/JSON.java @@ -3,7 +3,7 @@ package me.topchetoeu.jscript.polyfills; import java.util.HashSet; import java.util.stream.Collectors; -import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Values; @@ -30,7 +30,7 @@ public class JSON { if (val.isNull()) return Values.NULL; return null; } - private static JSONElement toJSON(CallContext ctx, Object val, HashSet prev) throws InterruptedException { + private static JSONElement toJSON(Context ctx, Object val, HashSet prev) throws InterruptedException { if (val instanceof Boolean) return JSONElement.bool((boolean)val); if (val instanceof Number) return JSONElement.number(((Number)val).doubleValue()); if (val instanceof String) return JSONElement.string((String)val); @@ -70,7 +70,7 @@ public class JSON { } @Native - public static Object parse(CallContext ctx, String val) throws InterruptedException { + public static Object parse(Context ctx, String val) throws InterruptedException { try { return toJS(me.topchetoeu.jscript.json.JSON.parse("", val)); } @@ -79,7 +79,7 @@ public class JSON { } } @Native - public static String stringify(CallContext ctx, Object val) throws InterruptedException { + public static String stringify(Context ctx, Object val) throws InterruptedException { return me.topchetoeu.jscript.json.JSON.stringify(toJSON(ctx, val, new HashSet<>())); } } diff --git a/src/me/topchetoeu/jscript/polyfills/Math.java b/src/me/topchetoeu/jscript/polyfills/Math.java index 8a4380f..fe6f0d3 100644 --- a/src/me/topchetoeu/jscript/polyfills/Math.java +++ b/src/me/topchetoeu/jscript/polyfills/Math.java @@ -1,6 +1,6 @@ package me.topchetoeu.jscript.polyfills; -import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.MessageContext; import me.topchetoeu.jscript.interop.Native; public class Math { @@ -22,19 +22,19 @@ public class Math { public static final double LOG10E = java.lang.Math.log10(java.lang.Math.E); @Native - public static double asin(CallContext ctx, double x) throws InterruptedException { + public static double asin(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.asin(x); } @Native - public static double acos(CallContext ctx, double x) throws InterruptedException { + public static double acos(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.acos(x); } @Native - public static double atan(CallContext ctx, double x) throws InterruptedException { + public static double atan(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.atan(x); } @Native - public static double atan2(CallContext ctx, double y, double x) throws InterruptedException { + public static double atan2(MessageContext ctx, double y, double x) throws InterruptedException { double _y = y; double _x = x; if (_x == 0) { @@ -51,59 +51,59 @@ public class Math { } @Native - public static double asinh(CallContext ctx, double x) throws InterruptedException { + public static double asinh(MessageContext ctx, double x) throws InterruptedException { double _x = x; return java.lang.Math.log(_x + java.lang.Math.sqrt(_x * _x + 1)); } @Native - public static double acosh(CallContext ctx, double x) throws InterruptedException { + public static double acosh(MessageContext ctx, double x) throws InterruptedException { double _x = x; return java.lang.Math.log(_x + java.lang.Math.sqrt(_x * _x - 1)); } @Native - public static double atanh(CallContext ctx, double x) throws InterruptedException { + public static double atanh(MessageContext ctx, double x) throws InterruptedException { double _x = x; if (_x <= -1 || _x >= 1) return Double.NaN; return .5 * java.lang.Math.log((1 + _x) / (1 - _x)); } @Native - public static double sin(CallContext ctx, double x) throws InterruptedException { + public static double sin(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.sin(x); } @Native - public static double cos(CallContext ctx, double x) throws InterruptedException { + public static double cos(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.cos(x); } @Native - public static double tan(CallContext ctx, double x) throws InterruptedException { + public static double tan(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.tan(x); } @Native - public static double sinh(CallContext ctx, double x) throws InterruptedException { + public static double sinh(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.sinh(x); } @Native - public static double cosh(CallContext ctx, double x) throws InterruptedException { + public static double cosh(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.cosh(x); } @Native - public static double tanh(CallContext ctx, double x) throws InterruptedException { + public static double tanh(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.tanh(x); } @Native - public static double sqrt(CallContext ctx, double x) throws InterruptedException { + public static double sqrt(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.sqrt(x); } @Native - public static double cbrt(CallContext ctx, double x) throws InterruptedException { + public static double cbrt(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.cbrt(x); } @Native - public static double hypot(CallContext ctx, double... vals) throws InterruptedException { + public static double hypot(MessageContext ctx, double ...vals) throws InterruptedException { var res = 0.; for (var el : vals) { var val = el; @@ -112,68 +112,68 @@ public class Math { return java.lang.Math.sqrt(res); } @Native - public static int imul(CallContext ctx, double a, double b) throws InterruptedException { + public static int imul(MessageContext ctx, double a, double b) throws InterruptedException { return (int)a * (int)b; } @Native - public static double exp(CallContext ctx, double x) throws InterruptedException { + public static double exp(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.exp(x); } @Native - public static double expm1(CallContext ctx, double x) throws InterruptedException { + public static double expm1(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.expm1(x); } @Native - public static double pow(CallContext ctx, double x, double y) throws InterruptedException { + public static double pow(MessageContext ctx, double x, double y) throws InterruptedException { return java.lang.Math.pow(x, y); } @Native - public static double log(CallContext ctx, double x) throws InterruptedException { + public static double log(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.log(x); } @Native - public static double log10(CallContext ctx, double x) throws InterruptedException { + public static double log10(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.log10(x); } @Native - public static double log1p(CallContext ctx, double x) throws InterruptedException { + public static double log1p(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.log1p(x); } @Native - public static double log2(CallContext ctx, double x) throws InterruptedException { + public static double log2(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.log(x) / LN2; } @Native - public static double ceil(CallContext ctx, double x) throws InterruptedException { + public static double ceil(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.ceil(x); } @Native - public static double floor(CallContext ctx, double x) throws InterruptedException { + public static double floor(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.floor(x); } @Native - public static double round(CallContext ctx, double x) throws InterruptedException { + public static double round(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.round(x); } @Native - public static float fround(CallContext ctx, double x) throws InterruptedException { + public static float fround(MessageContext ctx, double x) throws InterruptedException { return (float)x; } @Native - public static double trunc(CallContext ctx, double x) throws InterruptedException { + public static double trunc(MessageContext ctx, double x) throws InterruptedException { var _x = x; return java.lang.Math.floor(java.lang.Math.abs(_x)) * java.lang.Math.signum(_x); } @Native - public static double abs(CallContext ctx, double x) throws InterruptedException { + public static double abs(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.abs(x); } @Native - public static double max(CallContext ctx, double... vals) throws InterruptedException { + public static double max(MessageContext ctx, double ...vals) throws InterruptedException { var res = Double.NEGATIVE_INFINITY; for (var el : vals) { @@ -184,7 +184,7 @@ public class Math { return res; } @Native - public static double min(CallContext ctx, double... vals) throws InterruptedException { + public static double min(MessageContext ctx, double ...vals) throws InterruptedException { var res = Double.POSITIVE_INFINITY; for (var el : vals) { @@ -196,7 +196,7 @@ public class Math { } @Native - public static double sign(CallContext ctx, double x) throws InterruptedException { + public static double sign(MessageContext ctx, double x) throws InterruptedException { return java.lang.Math.signum(x); } @@ -205,7 +205,7 @@ public class Math { return java.lang.Math.random(); } @Native - public static int clz32(CallContext ctx, double x) throws InterruptedException { + public static int clz32(MessageContext ctx, double x) throws InterruptedException { return Integer.numberOfLeadingZeros((int)x); } } diff --git a/src/me/topchetoeu/jscript/polyfills/PolyfillEngine.java- b/src/me/topchetoeu/jscript/polyfills/PolyfillEngine.java- deleted file mode 100644 index 0ea635e..0000000 --- a/src/me/topchetoeu/jscript/polyfills/PolyfillEngine.java- +++ /dev/null @@ -1,105 +0,0 @@ -package me.topchetoeu.jscript.polyfills; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; - -import me.topchetoeu.jscript.engine.Engine; -import me.topchetoeu.jscript.engine.modules.ModuleManager; - -public class PolyfillEngine extends Engine { - public static String streamToString(InputStream in) { - try { - StringBuilder out = new StringBuilder(); - BufferedReader br = new BufferedReader(new InputStreamReader(in)); - - for(var line = br.readLine(); line != null; line = br.readLine()) { - out.append(line).append('\n'); - } - - br.close(); - return out.toString(); - } - catch (IOException e) { - return null; - } - } - public static String resourceToString(String name) { - var str = PolyfillEngine.class.getResourceAsStream("/me/topchetoeu/jscript/" + name); - if (str == null) return null; - return streamToString(str); - } - - public final ModuleManager modules; - - @Override - public Object makeRegex(String pattern, String flags) { - return new RegExp(pattern, flags); - } - - @Override - public ModuleManager modules() { - return modules; - } - public PolyfillEngine(File root) { - super(); - - this.modules = new ModuleManager(root); - - // exposeNamespace("Math", Math.class); - // exposeNamespace("JSON", JSON.class); - // exposeClass("Promise", Promise.class); - // exposeClass("RegExp", RegExp.class); - // exposeClass("Date", Date.class); - // exposeClass("Map", Map.class); - // exposeClass("Set", Set.class); - - // global().define("Object", "Function", "String", "Number", "Boolean", "Symbol"); - // global().define("Array", "require"); - // global().define("Error", "SyntaxError", "TypeError", "RangeError"); - // global().define("setTimeout", "setInterval", "clearTimeout", "clearInterval"); - // global().define("debugger"); - - // global().define(true, new NativeFunction("measure", (ctx, thisArg, values) -> { - // var start = System.nanoTime(); - // try { - // return Values.call(ctx, values[0], ctx); - // } - // finally { - // System.out.println(String.format("Function took %s ms", (System.nanoTime() - start) / 1000000.)); - // } - // })); - // global().define(true, new NativeFunction("isNaN", (ctx, thisArg, args) -> { - // if (args.length == 0) return true; - // else return Double.isNaN(Values.toNumber(ctx, args[0])); - // })); - // global().define(true, new NativeFunction("log", (el, t, args) -> { - // for (var obj : args) Values.printValue(el, obj); - // System.out.println(); - // return null; - // })); - - // var scope = global().globalChild(); - // scope.define("gt", true, global().obj); - // scope.define("lgt", true, scope.obj); - // scope.define("setProps", "setConstr"); - // scope.define("internals", true, new Internals()); - // scope.define(true, new NativeFunction("run", (ctx, thisArg, args) -> { - // var filename = (String)args[0]; - // boolean pollute = args.length > 1 && args[1].equals(true); - // FunctionValue func; - // var src = resourceToString("js/" + filename); - // if (src == null) throw new RuntimeException("The source '" + filename + "' doesn't exist."); - - // if (pollute) func = Parsing.compile(global(), filename, src); - // else func = Parsing.compile(scope.globalChild(), filename, src); - - // func.call(ctx); - // return null; - // })); - - // pushMsg(false, scope.globalChild(), java.util.Map.of(), "core.js", resourceToString("js/core.js"), null); - } -} diff --git a/src/me/topchetoeu/jscript/polyfills/RegExp.java b/src/me/topchetoeu/jscript/polyfills/RegExp.java index 5f7dca6..804d0e7 100644 --- a/src/me/topchetoeu/jscript/polyfills/RegExp.java +++ b/src/me/topchetoeu/jscript/polyfills/RegExp.java @@ -3,7 +3,7 @@ package me.topchetoeu.jscript.polyfills; import java.util.ArrayList; import java.util.regex.Pattern; -import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.NativeWrapper; import me.topchetoeu.jscript.engine.values.ObjectValue; @@ -17,7 +17,7 @@ public class RegExp { private static final Pattern NAMED_PATTERN = Pattern.compile("\\(\\?<([^=!].*?)>", Pattern.DOTALL); private static final Pattern ESCAPE_PATTERN = Pattern.compile("[/\\-\\\\^$*+?.()|\\[\\]{}]"); - private static String cleanupPattern(CallContext ctx, Object val) throws InterruptedException { + private static String cleanupPattern(Context ctx, Object val) throws InterruptedException { if (val == null) return "(?:)"; if (val instanceof RegExp) return ((RegExp)val).source; if (val instanceof NativeWrapper && ((NativeWrapper)val).wrapped instanceof RegExp) { @@ -27,7 +27,7 @@ public class RegExp { if (res.equals("")) return "(?:)"; return res; } - private static String cleanupFlags(CallContext ctx, Object val) throws InterruptedException { + private static String cleanupFlags(Context ctx, Object val) throws InterruptedException { if (val == null) return ""; return Values.toString(ctx, val); } @@ -46,7 +46,7 @@ public class RegExp { } @Native - public static RegExp escape(CallContext ctx, Object raw, Object flags) throws InterruptedException { + public static RegExp escape(Context ctx, Object raw, Object flags) throws InterruptedException { return escape(Values.toString(ctx, raw), cleanupFlags(ctx, flags)); } public static RegExp escape(String raw, String flags) { @@ -79,7 +79,7 @@ public class RegExp { @NativeGetter("lastIndex") public int lastIndex() { return lastI; } @NativeSetter("lastIndex") - public void setLastIndex(CallContext ctx, Object i) throws InterruptedException { + public void setLastIndex(Context ctx, Object i) throws InterruptedException { lastI = (int)Values.toNumber(ctx, i); } public void setLastIndex(int i) { @@ -100,7 +100,7 @@ public class RegExp { } @Native - public Object exec(CallContext ctx, Object str) throws InterruptedException { + public Object exec(Context ctx, Object str) throws InterruptedException { return exec(Values.toString(ctx, str)); } public Object exec(String str) { @@ -150,7 +150,7 @@ public class RegExp { } @Native - public RegExp(CallContext ctx, Object pattern, Object flags) throws InterruptedException { + public RegExp(Context ctx, Object pattern, Object flags) throws InterruptedException { this(cleanupPattern(ctx, pattern), cleanupFlags(ctx, flags)); } public RegExp(String pattern, String flags) { diff --git a/src/me/topchetoeu/jscript/polyfills/TypescriptEngine.java- b/src/me/topchetoeu/jscript/polyfills/TypescriptEngine.java- deleted file mode 100644 index 54b677e..0000000 --- a/src/me/topchetoeu/jscript/polyfills/TypescriptEngine.java- +++ /dev/null @@ -1,61 +0,0 @@ -package me.topchetoeu.jscript.polyfills; - -import java.io.File; -import java.util.ArrayList; -import java.util.Map; - -import me.topchetoeu.jscript.engine.scope.GlobalScope; -import me.topchetoeu.jscript.engine.values.ArrayValue; -import me.topchetoeu.jscript.engine.values.FunctionValue; -import me.topchetoeu.jscript.engine.values.NativeFunction; -import me.topchetoeu.jscript.engine.values.Values; - -public class TypescriptEngine extends PolyfillEngine { - private FunctionValue ts; - - @Override - public FunctionValue compile(GlobalScope scope, String filename, String raw) throws InterruptedException { - if (ts != null) { - var res = Values.array(ts.call(context(), null, filename, raw)); - var src = Values.toString(context(), res.get(0)); - var func = Values.function(res.get(1)); - - var compiled = super.compile(scope, filename, src); - - return new NativeFunction(null, (ctx, thisArg, args) -> { - return func.call(context(), null, compiled, thisArg, new ArrayValue(args)); - }); - } - return super.compile(scope, filename, raw); - } - - public TypescriptEngine(File root) { - super(root); - var scope = global().globalChild(); - - var decls = new ArrayList(); - decls.add(resourceToString("dts/core.d.ts")); - decls.add(resourceToString("dts/iterators.d.ts")); - decls.add(resourceToString("dts/map.d.ts")); - decls.add(resourceToString("dts/promise.d.ts")); - decls.add(resourceToString("dts/regex.d.ts")); - decls.add(resourceToString("dts/require.d.ts")); - decls.add(resourceToString("dts/set.d.ts")); - decls.add(resourceToString("dts/values/array.d.ts")); - decls.add(resourceToString("dts/values/boolean.d.ts")); - decls.add(resourceToString("dts/values/number.d.ts")); - decls.add(resourceToString("dts/values/errors.d.ts")); - decls.add(resourceToString("dts/values/function.d.ts")); - decls.add(resourceToString("dts/values/object.d.ts")); - decls.add(resourceToString("dts/values/string.d.ts")); - decls.add(resourceToString("dts/values/symbol.d.ts")); - - scope.define("libs", true, ArrayValue.of(decls)); - scope.define(true, new NativeFunction("init", (el, t, args) -> { - ts = Values.function(args[0]); - return null; - })); - - pushMsg(false, scope, Map.of(), "bootstrap.js", resourceToString("js/bootstrap.js"), null); - } -} -- 2.45.2 From 1ce5fc9d993fe96e3b618e5f290673e54d5e2c7b Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 9 Sep 2023 18:09:20 +0300 Subject: [PATCH 07/14] refactor: move parse to Contetx --- src/me/topchetoeu/jscript/Main.java | 2 +- src/me/topchetoeu/jscript/engine/Context.java | 9 +++++++++ src/me/topchetoeu/jscript/engine/Engine.java | 17 ++--------------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/me/topchetoeu/jscript/Main.java b/src/me/topchetoeu/jscript/Main.java index 9de34f3..09657b8 100644 --- a/src/me/topchetoeu/jscript/Main.java +++ b/src/me/topchetoeu/jscript/Main.java @@ -96,7 +96,7 @@ public class Main { }); env.global.define("go", ctx -> { try { - var func = engine.compile(ctx, "do.js", new String(Files.readAllBytes(Path.of("do.js")))); + var func = ctx.compile("do.js", new String(Files.readAllBytes(Path.of("do.js")))); return func.call(ctx); } catch (IOException e) { diff --git a/src/me/topchetoeu/jscript/engine/Context.java b/src/me/topchetoeu/jscript/engine/Context.java index adcc37a..417b115 100644 --- a/src/me/topchetoeu/jscript/engine/Context.java +++ b/src/me/topchetoeu/jscript/engine/Context.java @@ -1,9 +1,18 @@ package me.topchetoeu.jscript.engine; +import me.topchetoeu.jscript.engine.values.FunctionValue; +import me.topchetoeu.jscript.engine.values.Values; +import me.topchetoeu.jscript.parsing.Parsing; + public class Context { public final FunctionContext function; public final MessageContext message; + public FunctionValue compile(String filename, String raw) throws InterruptedException { + var res = Values.toString(this, function.compile.call(this, null, raw, filename)); + return Parsing.compile(function, filename, res); + } + public Context(FunctionContext funcCtx, MessageContext msgCtx) { this.function = funcCtx; this.message = msgCtx; diff --git a/src/me/topchetoeu/jscript/engine/Engine.java b/src/me/topchetoeu/jscript/engine/Engine.java index e98e000..3e0b388 100644 --- a/src/me/topchetoeu/jscript/engine/Engine.java +++ b/src/me/topchetoeu/jscript/engine/Engine.java @@ -1,14 +1,11 @@ package me.topchetoeu.jscript.engine; -import java.util.Map; import java.util.concurrent.LinkedBlockingDeque; import me.topchetoeu.jscript.engine.values.FunctionValue; -import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.events.Awaitable; import me.topchetoeu.jscript.events.DataNotifier; import me.topchetoeu.jscript.exceptions.EngineException; -import me.topchetoeu.jscript.parsing.Parsing; public class Engine { private class UncompiledFunction extends FunctionValue { @@ -19,7 +16,7 @@ public class Engine { @Override public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException { ctx = new Context(this.ctx, ctx.message); - return compile(ctx, filename, raw).call(ctx, thisArg, args); + return ctx.compile(filename, raw).call(ctx, thisArg, args); } public UncompiledFunction(FunctionContext ctx, String filename, String raw) { @@ -47,20 +44,15 @@ public class Engine { private static int nextId = 0; - // private Map, Object> callCtxVals = new HashMap<>(); - // private NativeTypeRegister typeRegister; private Thread thread; - private LinkedBlockingDeque macroTasks = new LinkedBlockingDeque<>(); private LinkedBlockingDeque microTasks = new LinkedBlockingDeque<>(); public final int id = ++nextId; - // public NativeTypeRegister typeRegister() { return typeRegister; } - private void runTask(Task task) throws InterruptedException { try { - task.notifier.next(task.func.call(new Context(null, new MessageContext(this)), task.thisArg, task.args)); + task.notifier.next(task.func.call(new Context(null, task.ctx), task.thisArg, task.args)); } catch (InterruptedException e) { task.notifier.error(new RuntimeException(e)); @@ -120,11 +112,6 @@ public class Engine { return pushMsg(micro, ctx.message, new UncompiledFunction(ctx.function, filename, raw), thisArg, args); } - public FunctionValue compile(Context ctx, String filename, String raw) throws InterruptedException { - var res = Values.toString(ctx, ctx.function.compile.call(ctx, null, raw, filename)); - return Parsing.compile(ctx.function, filename, res); - } - // public Engine() { // this.typeRegister = new NativeTypeRegister(); // } -- 2.45.2 From 8fb01e10910d5a801bc5b5e59de1dc7a75b07287 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 9 Sep 2023 18:12:20 +0300 Subject: [PATCH 08/14] fix: remove try_break and try_cont (dead code) --- .../jscript/compilation/control/TryStatement.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/me/topchetoeu/jscript/compilation/control/TryStatement.java b/src/me/topchetoeu/jscript/compilation/control/TryStatement.java index a0c763b..0c23230 100644 --- a/src/me/topchetoeu/jscript/compilation/control/TryStatement.java +++ b/src/me/topchetoeu/jscript/compilation/control/TryStatement.java @@ -49,18 +49,6 @@ public class TryStatement extends Statement { finN = target.size() - tmp; } - // for (int i = start; i < target.size(); i++) { - // if (target.get(i).type == Type.NOP) { - // var instr = target.get(i); - // if (instr.is(0, "break")) { - // target.set(i, Instruction.nop("try_break", instr.get(1), target.size()).locate(instr.location)); - // } - // else if (instr.is(0, "cont")) { - // target.set(i, Instruction.nop("try_cont", instr.get(1), target.size()).locate(instr.location)); - // } - // } - // } - target.set(start - 1, Instruction.tryInstr(tryN, catchN, finN).locate(loc())); } -- 2.45.2 From fea17d944beffde1209287e54f509f143fe75857 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 9 Sep 2023 18:12:24 +0300 Subject: [PATCH 09/14] fuck --- .../jscript/compilation/control/SwitchStatement.java | 3 --- .../jscript/compilation/control/WhileStatement.java | 8 -------- .../jscript/compilation/values/FunctionStatement.java | 4 ++-- 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/me/topchetoeu/jscript/compilation/control/SwitchStatement.java b/src/me/topchetoeu/jscript/compilation/control/SwitchStatement.java index 8e6b521..1151a35 100644 --- a/src/me/topchetoeu/jscript/compilation/control/SwitchStatement.java +++ b/src/me/topchetoeu/jscript/compilation/control/SwitchStatement.java @@ -66,9 +66,6 @@ public class SwitchStatement extends Statement { if (instr.type == Type.NOP && instr.is(0, "break") && instr.get(1) == null) { target.set(i, Instruction.jmp(target.size() - i).locate(instr.location)); } - if (instr.type == Type.NOP && instr.is(0, "try_break") && instr.get(1) == null) { - target.set(i, Instruction.signal("jmp_" + (target.size() - (Integer)instr.get(2))).locate(instr.location)); - } } for (var el : caseMap.entrySet()) { var loc = target.get(el.getKey()).location; diff --git a/src/me/topchetoeu/jscript/compilation/control/WhileStatement.java b/src/me/topchetoeu/jscript/compilation/control/WhileStatement.java index d7f04a2..af7738b 100644 --- a/src/me/topchetoeu/jscript/compilation/control/WhileStatement.java +++ b/src/me/topchetoeu/jscript/compilation/control/WhileStatement.java @@ -81,14 +81,6 @@ public class WhileStatement extends Statement { target.set(i, Instruction.jmp(breakPoint - i)); target.get(i).location = instr.location; } - if (instr.type == Type.NOP && instr.is(0, "try_cont") && (instr.get(1) == null || instr.is(1, label))) { - target.set(i, Instruction.signal("jmp_" + (continuePoint - (Integer)instr.get(2)))); - target.get(i).location = instr.location; - } - if (instr.type == Type.NOP && instr.is(0, "try_break") && (instr.get(1) == null || instr.is(1, label))) { - target.set(i, Instruction.signal("jmp_" + (breakPoint - (Integer)instr.get(2)))); - target.get(i).location = instr.location; - } } } diff --git a/src/me/topchetoeu/jscript/compilation/values/FunctionStatement.java b/src/me/topchetoeu/jscript/compilation/values/FunctionStatement.java index eb52f84..c0937db 100644 --- a/src/me/topchetoeu/jscript/compilation/values/FunctionStatement.java +++ b/src/me/topchetoeu/jscript/compilation/values/FunctionStatement.java @@ -28,10 +28,10 @@ public class FunctionStatement extends Statement { public static void checkBreakAndCont(List target, int start) { for (int i = start; i < target.size(); i++) { if (target.get(i).type == Type.NOP) { - if (target.get(i).is(0, "break") || target.get(i).is(0, "try_break")) { + if (target.get(i).is(0, "break") ) { throw new SyntaxException(target.get(i).location, "Break was placed outside a loop."); } - if (target.get(i).is(0, "cont") || target.get(i).is(0, "try_cont")) { + if (target.get(i).is(0, "cont")) { throw new SyntaxException(target.get(i).location, "Continue was placed outside a loop."); } } -- 2.45.2 From 0343c97d5eea267a61dea331386ecb526750fece Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 9 Sep 2023 18:16:56 +0300 Subject: [PATCH 10/14] fix script --- .github/workflows/tagged-release.yml | 2 +- build.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tagged-release.yml b/.github/workflows/tagged-release.yml index 7983a85..4b61fc4 100644 --- a/.github/workflows/tagged-release.yml +++ b/.github/workflows/tagged-release.yml @@ -18,7 +18,7 @@ jobs: repository: 'java-jscript' - name: "Build" run: | - cd java-jscript; node ./build.js + cd java-jscript; node ./build.js ${{ github.ref }} - uses: "marvinpinto/action-automatic-releases@latest" with: diff --git a/build.js b/build.js index f4f3b5f..01bb916 100644 --- a/build.js +++ b/build.js @@ -3,6 +3,7 @@ const fs = require('fs/promises'); const pt = require('path'); const conf = require('./meta'); const { argv } = require('process'); +conf.version = argv[2]; async function* find(src, dst, wildcard) { const stat = await fs.stat(src); -- 2.45.2 From 8f0c0226a8601ea0ced7b4bbc14bf6fb5f2bb1c1 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 9 Sep 2023 18:17:10 +0300 Subject: [PATCH 11/14] oops --- .github/workflows/tagged-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tagged-release.yml b/.github/workflows/tagged-release.yml index 4b61fc4..3ab534c 100644 --- a/.github/workflows/tagged-release.yml +++ b/.github/workflows/tagged-release.yml @@ -18,7 +18,7 @@ jobs: repository: 'java-jscript' - name: "Build" run: | - cd java-jscript; node ./build.js ${{ github.ref }} + cd java-jscript; node ./build.js release ${{ github.ref }} - uses: "marvinpinto/action-automatic-releases@latest" with: -- 2.45.2 From 7a226c49d3e0cba2863b4d4db4a8fd446d295122 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 9 Sep 2023 18:37:24 +0300 Subject: [PATCH 12/14] some build script fixes --- build.js | 8 ++++---- lib/core.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build.js b/build.js index 01bb916..fcadb10 100644 --- a/build.js +++ b/build.js @@ -3,7 +3,7 @@ const fs = require('fs/promises'); const pt = require('path'); const conf = require('./meta'); const { argv } = require('process'); -conf.version = argv[2]; +conf.version = argv[3].substring(1); async function* find(src, dst, wildcard) { const stat = await fs.stat(src); @@ -45,7 +45,7 @@ async function compileJava() { .replace('${NAME}', conf.name) .replace('${AUTHOR}', conf.author) ); - const args = ['--release', '10', ]; + const args = ['--release', '11', ]; if (argv[1] === 'debug') args.push('-g'); args.push('-d', 'dst/classes', 'Metadata.java'); @@ -59,14 +59,14 @@ async function compileJava() { (async () => { try { - fs.rm('dst', { recursive: true }); + try { await fs.rm('dst', { recursive: true }); } catch {} await copy('src', 'dst/classes', v => !v.endsWith('.java')); await run('tsc', '-p', 'lib/tsconfig.json', '--outFile', 'dst/classes/me/topchetoeu/jscript/js/core.js'), await compileJava(); await run('jar', '-c', '-f', 'dst/jscript.jar', '-e', 'me.topchetoeu.jscript.Main', '-C', 'dst/classes', '.'); } catch (e) { - if (argv[1] === 'debug') throw e; + if (argv[2] === 'debug') throw e; else console.log(e.toString()); } })(); diff --git a/lib/core.ts b/lib/core.ts index 4e1b9d9..2dc232a 100644 --- a/lib/core.ts +++ b/lib/core.ts @@ -35,8 +35,8 @@ interface Internals { } } -// @ts-ignore -var env: Environment = arguments[0], internals: Internals = arguments[1], log = internals.constructor.log; +var env: Environment = arguments[0], internals: Internals = arguments[1]; +globalThis.log = internals.constructor.log; try { run('values/object'); -- 2.45.2 From 3824d11c9725f08ff811427a28692e1b4eacb020 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 9 Sep 2023 18:42:10 +0300 Subject: [PATCH 13/14] debugging again --- .github/workflows/tagged-release.yml | 2 +- build.js | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tagged-release.yml b/.github/workflows/tagged-release.yml index 3ab534c..484fcf5 100644 --- a/.github/workflows/tagged-release.yml +++ b/.github/workflows/tagged-release.yml @@ -18,7 +18,7 @@ jobs: repository: 'java-jscript' - name: "Build" run: | - cd java-jscript; node ./build.js release ${{ github.ref }} + ls -R; cd java-jscript; node ./build.js release ${{ github.ref }} - uses: "marvinpinto/action-automatic-releases@latest" with: diff --git a/build.js b/build.js index fcadb10..754eea5 100644 --- a/build.js +++ b/build.js @@ -3,7 +3,10 @@ const fs = require('fs/promises'); const pt = require('path'); const conf = require('./meta'); const { argv } = require('process'); -conf.version = argv[3].substring(1); +conf.version ??= argv[3]; + +if (conf.version.startsWith('refs/tags/')) conf.version = conf.version.substring(10); +if (conf.version.startsWith('v')) conf.version = conf.version.substring(1); async function* find(src, dst, wildcard) { const stat = await fs.stat(src); -- 2.45.2 From 692fae40490d310c2965eec00ea7b25118c4839f Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 9 Sep 2023 18:55:20 +0300 Subject: [PATCH 14/14] feat: some cleanup --- .github/workflows/tagged-release.yml | 2 +- .gitignore | 12 +-- build.js | 9 ++- meta.json | 5 -- .../topchetoeu/jscript/filesystem/File.java | 22 ------ .../jscript/filesystem/Filesystem.java | 17 ----- .../jscript/filesystem/InaccessibleFile.java | 27 ------- .../jscript/filesystem/MemoryFile.java | 51 ------------- .../jscript/filesystem/Permissions.java | 17 ----- .../filesystem/PermissionsProvider.java | 7 -- .../jscript/filesystem/PhysicalFile.java | 53 ------------- .../filesystem/PhysicalFilesystem.java | 75 ------------------- 12 files changed, 15 insertions(+), 282 deletions(-) delete mode 100644 meta.json delete mode 100644 src/me/topchetoeu/jscript/filesystem/File.java delete mode 100644 src/me/topchetoeu/jscript/filesystem/Filesystem.java delete mode 100644 src/me/topchetoeu/jscript/filesystem/InaccessibleFile.java delete mode 100644 src/me/topchetoeu/jscript/filesystem/MemoryFile.java delete mode 100644 src/me/topchetoeu/jscript/filesystem/Permissions.java delete mode 100644 src/me/topchetoeu/jscript/filesystem/PermissionsProvider.java delete mode 100644 src/me/topchetoeu/jscript/filesystem/PhysicalFile.java delete mode 100644 src/me/topchetoeu/jscript/filesystem/PhysicalFilesystem.java diff --git a/.github/workflows/tagged-release.yml b/.github/workflows/tagged-release.yml index 484fcf5..3ab534c 100644 --- a/.github/workflows/tagged-release.yml +++ b/.github/workflows/tagged-release.yml @@ -18,7 +18,7 @@ jobs: repository: 'java-jscript' - name: "Build" run: | - ls -R; cd java-jscript; node ./build.js release ${{ github.ref }} + cd java-jscript; node ./build.js release ${{ github.ref }} - uses: "marvinpinto/action-automatic-releases@latest" with: diff --git a/.gitignore b/.gitignore index 9e46f3f..87e4e4d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,11 @@ .vscode .gradle .ignore -out -build -bin -dst +/out +/build +/bin +/dst /*.js -!/build.js \ No newline at end of file +!/build.js +/dead-code +/Metadata.java \ No newline at end of file diff --git a/build.js b/build.js index 754eea5..20a1f23 100644 --- a/build.js +++ b/build.js @@ -1,9 +1,14 @@ const { spawn } = require('child_process'); const fs = require('fs/promises'); const pt = require('path'); -const conf = require('./meta'); const { argv } = require('process'); -conf.version ??= argv[3]; + +const conf = { + name: "java-jscript", + author: "TopchetoEU", + javahome: "", + version: argv[3] +}; if (conf.version.startsWith('refs/tags/')) conf.version = conf.version.substring(10); if (conf.version.startsWith('v')) conf.version = conf.version.substring(1); diff --git a/meta.json b/meta.json deleted file mode 100644 index fb3c57f..0000000 --- a/meta.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "java-jscript", - "author": "TopchetoEU", - "javahome": "" -} \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/filesystem/File.java b/src/me/topchetoeu/jscript/filesystem/File.java deleted file mode 100644 index 1f57cee..0000000 --- a/src/me/topchetoeu/jscript/filesystem/File.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.topchetoeu.jscript.filesystem; - -import java.io.IOException; - -public interface File { - int read(byte[] buff) throws IOException; - void write(byte[] buff) throws IOException; - long tell() throws IOException; - void seek(long offset, int pos) throws IOException; - void close() throws IOException; - Permissions perms(); - - default String readToString() throws IOException { - seek(0, 2); - long len = tell(); - if (len < 0) return null; - seek(0, 0); - byte[] res = new byte[(int)len]; - if (read(res) < 0) return null; - return new String(res); - } -} \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/filesystem/Filesystem.java b/src/me/topchetoeu/jscript/filesystem/Filesystem.java deleted file mode 100644 index 8986220..0000000 --- a/src/me/topchetoeu/jscript/filesystem/Filesystem.java +++ /dev/null @@ -1,17 +0,0 @@ -package me.topchetoeu.jscript.filesystem; - -import java.io.IOException; -import java.nio.file.Path; - -public interface Filesystem extends PermissionsProvider { - public static enum EntryType { - NONE, - FILE, - FOLDER, - } - - File open(Path path) throws IOException; - EntryType type(Path path); - boolean mkdir(Path path); - boolean rm(Path path) throws IOException; -} diff --git a/src/me/topchetoeu/jscript/filesystem/InaccessibleFile.java b/src/me/topchetoeu/jscript/filesystem/InaccessibleFile.java deleted file mode 100644 index e2e9b74..0000000 --- a/src/me/topchetoeu/jscript/filesystem/InaccessibleFile.java +++ /dev/null @@ -1,27 +0,0 @@ -package me.topchetoeu.jscript.filesystem; - -import java.io.IOException; - -public class InaccessibleFile implements File { - @Override - public int read(byte[] buff) throws IOException { - return -1; - } - @Override - public void write(byte[] buff) throws IOException { - } - @Override - public long tell() throws IOException { - return -1; - } - @Override - public void seek(long offset, int pos) throws IOException { - } - @Override - public void close() throws IOException { - } - @Override - public Permissions perms() { - return Permissions.NONE; - } -} diff --git a/src/me/topchetoeu/jscript/filesystem/MemoryFile.java b/src/me/topchetoeu/jscript/filesystem/MemoryFile.java deleted file mode 100644 index 0f3a7da..0000000 --- a/src/me/topchetoeu/jscript/filesystem/MemoryFile.java +++ /dev/null @@ -1,51 +0,0 @@ -package me.topchetoeu.jscript.filesystem; - -import java.io.IOException; - -public class MemoryFile implements File { - public byte[] data; - private int ptr = 0; - - @Override - public void close() { - data = null; - ptr = -1; - } - - @Override - public int read(byte[] buff) throws IOException { - if (data == null) return -1; - if (ptr == data.length) return -1; - int n = Math.min(buff.length, data.length - ptr); - System.arraycopy(data, ptr, buff, 0, n); - return n; - } - - @Override - public void seek(long offset, int pos) throws IOException { - if (data == null) return; - if (pos == 0) ptr = (int)offset; - else if (pos == 1) ptr += (int)offset; - else ptr = data.length - (int)offset; - - ptr = (int)Math.max(Math.min(ptr, data.length), 0); - } - - @Override - public long tell() throws IOException { - if (data == null) return -1; - return ptr; - } - - @Override - public void write(byte[] buff) throws IOException { } - @Override - public Permissions perms() { - if (data == null) return Permissions.NONE; - else return Permissions.READ; - } - - public MemoryFile(byte[] data) { - this.data = data; - } -} diff --git a/src/me/topchetoeu/jscript/filesystem/Permissions.java b/src/me/topchetoeu/jscript/filesystem/Permissions.java deleted file mode 100644 index c9112ad..0000000 --- a/src/me/topchetoeu/jscript/filesystem/Permissions.java +++ /dev/null @@ -1,17 +0,0 @@ -package me.topchetoeu.jscript.filesystem; - -public enum Permissions { - NONE("", false, false), - READ("r", true, false), - READ_WRITE("rw", true, true); - - public final String readMode; - public final boolean readable; - public final boolean writable; - - private Permissions(String mode, boolean r, boolean w) { - this.readMode = mode; - this.readable = r; - this.writable = w; - } -} diff --git a/src/me/topchetoeu/jscript/filesystem/PermissionsProvider.java b/src/me/topchetoeu/jscript/filesystem/PermissionsProvider.java deleted file mode 100644 index 06998c0..0000000 --- a/src/me/topchetoeu/jscript/filesystem/PermissionsProvider.java +++ /dev/null @@ -1,7 +0,0 @@ -package me.topchetoeu.jscript.filesystem; - -import java.nio.file.Path; - -public interface PermissionsProvider { - Permissions perms(Path file); -} diff --git a/src/me/topchetoeu/jscript/filesystem/PhysicalFile.java b/src/me/topchetoeu/jscript/filesystem/PhysicalFile.java deleted file mode 100644 index 9b88a18..0000000 --- a/src/me/topchetoeu/jscript/filesystem/PhysicalFile.java +++ /dev/null @@ -1,53 +0,0 @@ -package me.topchetoeu.jscript.filesystem; - -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.file.Path; - -public class PhysicalFile implements File { - private RandomAccessFile file; - private Permissions perms; - - @Override - public int read(byte[] buff) throws IOException { - if (!perms.readable) return -1; - return file.read(buff); - } - @Override - public void write(byte[] buff) throws IOException { - if (!perms.writable) return; - file.write(buff); - } - @Override - public long tell() throws IOException { - if (!perms.readable) return -1; - return file.getFilePointer(); - } - @Override - public void seek(long offset, int pos) throws IOException { - if (!perms.readable) return; - if (pos == 0) file.seek(offset); - else if (pos == 1) file.seek(tell() + offset); - else file.seek(file.length() + offset); - } - @Override - public void close() throws IOException { - if (!perms.readable) return; - file.close(); - file = null; - perms = Permissions.NONE; - } - @Override - public Permissions perms() { - return perms; - } - - public PhysicalFile(Path path, Permissions perms) throws IOException { - if (!path.toFile().canWrite() && perms.writable) perms = Permissions.READ; - if (!path.toFile().canRead() && perms.readable) perms = Permissions.NONE; - - this.perms = perms; - if (perms == Permissions.NONE) this.file = null; - else this.file = new RandomAccessFile(path.toString(), perms.readMode); - } -} diff --git a/src/me/topchetoeu/jscript/filesystem/PhysicalFilesystem.java b/src/me/topchetoeu/jscript/filesystem/PhysicalFilesystem.java deleted file mode 100644 index 043b7b7..0000000 --- a/src/me/topchetoeu/jscript/filesystem/PhysicalFilesystem.java +++ /dev/null @@ -1,75 +0,0 @@ -package me.topchetoeu.jscript.filesystem; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.stream.Collectors; - -public class PhysicalFilesystem implements Filesystem { - public final Path root; - public final PermissionsProvider perms; - - private static Path joinPaths(Path root, Path file) { - if (file.isAbsolute()) file = file.getRoot().relativize(file); - file = file.normalize(); - - while (true) { - if (file.startsWith("..")) file = file.subpath(1, file.getNameCount()); - else if (file.startsWith(".")) file = file.subpath(1, file.getNameCount()); - else break; - } - - return Path.of(root.toString(), file.toString()); - } - - @Override - public boolean mkdir(Path path) { - if (!perms(path).writable) return false; - path = joinPaths(root, path); - return path.toFile().mkdirs(); - } - @Override - public File open(Path path) throws IOException { - var perms = perms(path); - if (perms == Permissions.NONE) return new InaccessibleFile(); - path = joinPaths(root, path); - - if (path.toFile().isDirectory()) { - return new MemoryFile(String.join("\n", Files.list(path).map(Path::toString).collect(Collectors.toList())).getBytes()); - } - else if (path.toFile().isFile()) { - return new PhysicalFile(path, perms); - } - else return new InaccessibleFile(); - } - @Override - public boolean rm(Path path) { - if (!perms(path).writable) return false; - return joinPaths(root, path).toFile().delete(); - } - @Override - public EntryType type(Path path) { - if (!perms(path).readable) return EntryType.NONE; - path = joinPaths(root, path); - - if (!path.toFile().exists()) return EntryType.NONE; - if (path.toFile().isFile()) return EntryType.FILE; - else return EntryType.FOLDER; - - } - @Override - public Permissions perms(Path path) { - path = joinPaths(root, path); - var res = perms.perms(path); - - if (!path.toFile().canWrite() && res.writable) res = Permissions.READ; - if (!path.toFile().canRead() && res.readable) res = Permissions.NONE; - - return res; - } - - public PhysicalFilesystem(Path root, PermissionsProvider perms) { - this.root = root; - this.perms = perms; - } -} -- 2.45.2