major changes in preparations for environments
This commit is contained in:
parent
d1b37074a6
commit
29d3f378a5
78
lib/core.ts
78
lib/core.ts
@ -1,19 +1,38 @@
|
||||
var env: Environment;
|
||||
interface Environment {
|
||||
global: typeof globalThis & Record<string, any>;
|
||||
proto(name: string): object;
|
||||
setProto(name: string, val: object): void;
|
||||
}
|
||||
interface Internals {
|
||||
markSpecial(...funcs: Function[]): void;
|
||||
getEnv(func: Function): Environment | undefined;
|
||||
setEnv<T>(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;
|
||||
}
|
||||
strlen(val: string): number;
|
||||
char(val: string): number;
|
||||
stringFromStrings(arr: string[]): string;
|
||||
stringFromChars(arr: number[]): string;
|
||||
symbol(name?: string): symbol;
|
||||
symbolToString(sym: symbol): string;
|
||||
|
||||
isArray(obj: any): boolean;
|
||||
generator(func: (_yield: <T>(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<any, any>;
|
||||
ownPropKeys(obj: any): any[];
|
||||
lock(obj: object, type: 'ext' | 'seal' | 'freeze'): void;
|
||||
extensible(obj: object): boolean;
|
||||
|
||||
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');
|
||||
@ -23,46 +42,23 @@ return (_env: Environment) => {
|
||||
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');
|
||||
run('timeout');
|
||||
|
||||
log('Loaded polyfills!');
|
||||
}
|
||||
catch (e: any) {
|
||||
if (!_env.captureErr) throw e;
|
||||
var err = 'Uncaught error while loading polyfills: ';
|
||||
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;
|
||||
}
|
||||
else err += e;
|
||||
|
||||
log(e);
|
||||
}
|
||||
};
|
66
lib/lib.d.ts
vendored
66
lib/lib.d.ts
vendored
@ -46,8 +46,8 @@ type IteratorReturnResult<TReturn> =
|
||||
type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;
|
||||
|
||||
interface Thenable<T> {
|
||||
then<NextT>(this: Promise<T>, onFulfilled: PromiseThenFunc<T, NextT>, onRejected?: PromiseRejectFunc): Promise<Awaited<NextT>>;
|
||||
then(this: Promise<T>, onFulfilled: undefined, onRejected?: PromiseRejectFunc): Promise<T>;
|
||||
then<NextT>(onFulfilled: PromiseThenFunc<T, NextT>, onRejected?: PromiseRejectFunc): Promise<Awaited<NextT>>;
|
||||
then(onFulfilled: undefined, onRejected?: PromiseRejectFunc): Promise<T>;
|
||||
}
|
||||
|
||||
interface RegExpResultIndices extends Array<[number, number]> {
|
||||
@ -100,7 +100,6 @@ interface IterableIterator<T> extends Iterator<T> {
|
||||
}
|
||||
|
||||
interface AsyncIterator<T, TReturn = any, TNext = undefined> {
|
||||
// NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
|
||||
next(...args: [] | [TNext]): Promise<IteratorResult<T, TReturn>>;
|
||||
return?(value?: TReturn | Thenable<TReturn>): Promise<IteratorResult<T, TReturn>>;
|
||||
throw?(e?: any): Promise<IteratorResult<T, TReturn>>;
|
||||
@ -112,65 +111,29 @@ interface AsyncIterableIterator<T> extends AsyncIterator<T> {
|
||||
[Symbol.asyncIterator](): AsyncIterableIterator<T>;
|
||||
}
|
||||
|
||||
interface Generator<T = unknown, TReturn = any, TNext = unknown> extends Iterator<T, TReturn, TNext> {
|
||||
interface Generator<T = unknown, TReturn = unknown, TNext = unknown> extends Iterator<T, TReturn, TNext> {
|
||||
[Symbol.iterator](): Generator<T, TReturn, TNext>;
|
||||
return(value?: TReturn): IteratorResult<T, TReturn>;
|
||||
throw(e?: any): IteratorResult<T, TReturn>;
|
||||
return(value: TReturn): IteratorResult<T, TReturn>;
|
||||
throw(e: any): IteratorResult<T, TReturn>;
|
||||
}
|
||||
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<T = unknown, TReturn = any, TNext = unknown> extends AsyncIterator<T, TReturn, TNext> {
|
||||
// NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
|
||||
next(...args: [] | [TNext]): Promise<IteratorResult<T, TReturn>>;
|
||||
interface AsyncGenerator<T = unknown, TReturn = unknown, TNext = unknown> extends AsyncIterator<T, TReturn, TNext> {
|
||||
return(value: TReturn | Thenable<TReturn>): Promise<IteratorResult<T, TReturn>>;
|
||||
throw(e: any): Promise<IteratorResult<T, TReturn>>;
|
||||
[Symbol.asyncIterator](): AsyncGenerator<T, TReturn, TNext>;
|
||||
}
|
||||
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<T> extends IterableIterator<T> {
|
||||
[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<T> extends Thenable<T> {
|
||||
constructor: PromiseConstructor;
|
||||
catch(func: PromiseRejectFunc): Promise<T>;
|
||||
finally(func: () => void): Promise<T>;
|
||||
}
|
||||
@ -522,7 +474,8 @@ interface PromiseConstructor {
|
||||
resolve<T>(val: T): Promise<Awaited<T>>;
|
||||
reject(val: any): Promise<never>;
|
||||
|
||||
any<T>(promises: (Promise<T>|T)[]): Promise<T>;
|
||||
isAwaitable(val: unknown): val is Thenable<any>;
|
||||
any<T>(promises: T[]): Promise<Awaited<T>>;
|
||||
race<T>(promises: (Promise<T>|T)[]): Promise<T>;
|
||||
all<T extends any[]>(promises: T): Promise<{ [Key in keyof T]: Awaited<T[Key]> }>;
|
||||
allSettled<T extends any[]>(...promises: T): Promise<[...{ [P in keyof T]: PromiseResult<Awaited<T[P]>>}]>;
|
||||
@ -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;
|
||||
|
105
lib/map.ts
105
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() {
|
||||
class Map<KeyT, ValueT> {
|
||||
[syms.values]: any = {};
|
||||
|
||||
public [Symbol.iterator](): IterableIterator<[KeyT, ValueT]> {
|
||||
return this.entries();
|
||||
};
|
||||
}
|
||||
|
||||
var entries = Map.prototype.entries;
|
||||
var keys = Map.prototype.keys;
|
||||
var values = Map.prototype.values;
|
||||
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;
|
||||
}
|
||||
|
||||
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 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<KeyT> {
|
||||
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<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: 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<KeyT, ValueT>) => 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;
|
||||
});
|
||||
|
@ -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 };
|
||||
|
202
lib/promise.ts
202
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<T> = [ PromiseFulfillFunc<T>, PromiseRejectFunc ];
|
||||
enum State {
|
||||
Pending,
|
||||
Fulfilled,
|
||||
Rejected,
|
||||
}
|
||||
|
||||
function isAwaitable(val: unknown): val is Thenable<any> {
|
||||
return (
|
||||
typeof val === 'object' &&
|
||||
val !== null &&
|
||||
'then' in val &&
|
||||
typeof val.then === 'function'
|
||||
);
|
||||
}
|
||||
function resolve(promise: Promise<any>, 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<T> {
|
||||
public static isAwaitable(val: unknown): val is Thenable<any> {
|
||||
return isAwaitable(val);
|
||||
}
|
||||
|
||||
public static resolve<T>(val: T): Promise<Awaited<T>> {
|
||||
return new Promise(res => res(val as any));
|
||||
}
|
||||
public static reject<T>(val: T): Promise<Awaited<T>> {
|
||||
return new Promise((_, rej) => rej(val as any));
|
||||
}
|
||||
|
||||
public static race<T>(vals: T[]): Promise<Awaited<T>> {
|
||||
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<T>(vals: T[]): Promise<Awaited<T>> {
|
||||
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<any[]> {
|
||||
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<any[]> {
|
||||
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<T>[] = [];
|
||||
[syms.handled] = false;
|
||||
[syms.state] = State.Pending;
|
||||
[syms.value]?: T | unknown;
|
||||
|
||||
public then(onFulfil?: PromiseFulfillFunc<T>, 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<T>) {
|
||||
internals.pushMessage(true, func, undefined, [
|
||||
((v) => resolve(this, v, State.Fulfilled)) as PromiseFulfillFunc<T>,
|
||||
((err) => resolve(this, err, State.Rejected)) as PromiseRejectFunc
|
||||
]);
|
||||
}
|
||||
}
|
||||
env.global.Promise = Promise as any;
|
||||
});
|
||||
|
242
lib/regex.ts
242
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<RegExpResult, undefined> => {
|
||||
const val = pattern?.exec(target);
|
||||
// return {
|
||||
// next: (): IteratorResult<RegExpResult, undefined> => {
|
||||
// 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;
|
||||
// }
|
||||
// },
|
||||
// });
|
||||
});
|
98
lib/set.ts
98
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<T> {
|
||||
[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<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] }
|
||||
},
|
||||
[Symbol.iterator]() { return this; }
|
||||
}
|
||||
}
|
||||
public values(): IterableIterator<T> {
|
||||
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<T>) => 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<T>) {
|
||||
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;
|
||||
});
|
||||
|
38
lib/timeout.ts
Normal file
38
lib/timeout.ts
Normal file
@ -0,0 +1,38 @@
|
||||
define("timeout", () => {
|
||||
const timeouts: Record<number, () => void> = { };
|
||||
const intervals: Record<number, () => 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!;
|
||||
};
|
||||
});
|
@ -15,6 +15,7 @@
|
||||
"map.ts",
|
||||
"set.ts",
|
||||
"regex.ts",
|
||||
"timeout.ts",
|
||||
"core.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
|
16
lib/utils.ts
16
lib/utils.ts
@ -1,9 +1,3 @@
|
||||
interface Environment {
|
||||
global: typeof globalThis & Record<string, any>;
|
||||
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<ConstrT, T extends { constructor: ConstrT }>(target: T, constr: ConstrT, env: Environment) {
|
||||
env.internals.defineField(
|
||||
function setConstr(target: object, constr: Function) {
|
||||
internals.defineField(
|
||||
target, 'constructor', constr,
|
||||
true, // writable
|
||||
false, // enumerable
|
||||
|
@ -15,15 +15,15 @@ define("values/array", () => {
|
||||
return res;
|
||||
} as ArrayConstructor;
|
||||
|
||||
Array.prototype = ([] as any).__proto__ as Array<any>;
|
||||
setConstr(Array.prototype, Array, env);
|
||||
|
||||
env.setProto('array', Array.prototype);
|
||||
(Array.prototype as any)[Symbol.typeName] = "Array";
|
||||
setConstr(Array.prototype, Array);
|
||||
|
||||
setProps(Array.prototype, env, {
|
||||
setProps(Array.prototype, {
|
||||
[Symbol.iterator]: function() {
|
||||
return this.values();
|
||||
},
|
||||
[Symbol.typeName]: "Array",
|
||||
|
||||
values() {
|
||||
var i = 0;
|
||||
@ -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) {
|
||||
@ -329,7 +329,8 @@ define("values/array", () => {
|
||||
}
|
||||
});
|
||||
|
||||
setProps(Array, env, {
|
||||
isArray(val: any) { return env.internals.isArr(val); }
|
||||
setProps(Array, {
|
||||
isArray(val: any) { return internals.isArray(val); }
|
||||
});
|
||||
internals.markSpecial(Array);
|
||||
});
|
@ -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);
|
||||
});
|
||||
|
@ -9,35 +9,38 @@ define("values/errors", () => {
|
||||
}, Error.prototype);
|
||||
} as ErrorConstructor;
|
||||
|
||||
Error.prototype = env.internals.err ?? {};
|
||||
Error.prototype.name = 'Error';
|
||||
setConstr(Error.prototype, Error, env);
|
||||
|
||||
Error.prototype.toString = function() {
|
||||
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<T extends ErrorConstructor>(name: string, proto: any): T {
|
||||
var err = function (msg: string) {
|
||||
function makeError<T1 extends ErrorConstructor>(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);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
env.global.RangeError = makeError('RangeError', env.internals.range ?? {});
|
||||
env.global.TypeError = makeError('TypeError', env.internals.type ?? {});
|
||||
env.global.SyntaxError = makeError('SyntaxError', env.internals.syntax ?? {});
|
||||
(constr as any).__proto__ = Error;
|
||||
(constr.prototype as any).__proto__ = env.proto('error');
|
||||
setConstr(constr.prototype, constr as ErrorConstructor);
|
||||
setProps(constr.prototype, { name: name });
|
||||
|
||||
internals.markSpecial(constr);
|
||||
env.setProto(proto, constr.prototype);
|
||||
|
||||
return constr as T1;
|
||||
}
|
||||
|
||||
env.global.RangeError = makeError('RangeError', 'rangeErr');
|
||||
env.global.TypeError = makeError('TypeError', 'typeErr');
|
||||
env.global.SyntaxError = makeError('SyntaxError', 'syntaxErr');
|
||||
});
|
@ -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<any[], ['await' | 'yield', any]>((_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);
|
||||
});
|
@ -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 });
|
||||
});
|
@ -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];
|
||||
@ -43,7 +44,7 @@ define("values/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,
|
||||
@ -55,7 +56,7 @@ define("values/object", () => {
|
||||
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,
|
||||
@ -78,35 +79,72 @@ define("values/object", () => {
|
||||
},
|
||||
|
||||
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);
|
||||
});
|
@ -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,
|
||||
|
@ -1,31 +1,34 @@
|
||||
define("values/symbol", () => {
|
||||
const symbols: Record<string, 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);
|
||||
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' });
|
||||
|
6
package-lock.json
generated
Normal file
6
package-lock.json
generated
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "java-jscript",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
1
package.json
Normal file
1
package.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
@ -2,34 +2,64 @@ 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<Object> valuePrinter = new Observer<Object>() {
|
||||
public void next(Object data) {
|
||||
try {
|
||||
Values.printValue(engine.context(), data);
|
||||
Values.printValue(null, data);
|
||||
}
|
||||
catch (InterruptedException e) { }
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
public void error(RuntimeException err) {
|
||||
try {
|
||||
try {
|
||||
if (err instanceof EngineException) {
|
||||
System.out.println("Uncaught " + ((EngineException)err).toString(engine.context()));
|
||||
System.out.println("Uncaught " + ((EngineException)err).toString(new CallContext(engine, env)));
|
||||
}
|
||||
else if (err instanceof SyntaxException) {
|
||||
System.out.println("Syntax error:" + ((SyntaxException)err).msg);
|
||||
@ -41,7 +71,10 @@ public class Main {
|
||||
}
|
||||
}
|
||||
catch (EngineException ex) {
|
||||
System.out.println("Uncaught [error while converting to string]");
|
||||
System.out.println("Uncaught ");
|
||||
Values.printValue(null, ((EngineException)err).value);
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
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(), "<stdio>", raw, null).toObservable().once(valuePrinter);
|
||||
engine.pushMsg(false, Map.of(), env, "<stdio>", 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]");
|
||||
|
@ -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<T> {}
|
||||
|
||||
public final Engine engine;
|
||||
private final Map<DataKey<?>, Object> data = new Hashtable<>();
|
||||
public Environment environment;
|
||||
private final Map<DataKey<?>, Object> data;
|
||||
|
||||
public Engine engine() { return engine; }
|
||||
public Map<DataKey<?>, 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<DataKey<?>, 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> T addData(DataKey<T> 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> T getData(DataKey<T> key) {
|
||||
return getData(key, null);
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getData(DataKey<T> 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<Integer> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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<DataKey<?>, Object> data;
|
||||
public final DataNotifier<Object> notifier = new DataNotifier<>();
|
||||
public final Environment env;
|
||||
|
||||
public Task(Object func, Map<DataKey<?>, Object> data, Object thisArg, Object[] args) {
|
||||
public Task(Environment env, FunctionValue func, Map<DataKey<?>, 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<DebugState> DEBUG_STATE_KEY = new DataKey<>();
|
||||
private static int nextId = 0;
|
||||
|
||||
private Map<DataKey<?>, 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<DataKey<?>, Object> callCtxVals = new HashMap<>();
|
||||
// private NativeTypeRegister typeRegister;
|
||||
private Thread thread;
|
||||
|
||||
private LinkedBlockingDeque<Task> macroTasks = new LinkedBlockingDeque<>();
|
||||
private LinkedBlockingDeque<Task> 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<Object> pushMsg(boolean micro, FunctionValue func, Map<DataKey<?>, Object> data, Object thisArg, Object... args) {
|
||||
var msg = new Task(func, data, thisArg, args);
|
||||
public Awaitable<Object> pushMsg(boolean micro, Map<DataKey<?>, 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<Object> pushMsg(boolean micro, GlobalScope scope, Map<DataKey<?>, 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<Object> pushMsg(boolean micro, Map<DataKey<?>, 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();
|
||||
// }
|
||||
}
|
||||
|
81
src/me/topchetoeu/jscript/engine/Environment.java
Normal file
81
src/me/topchetoeu/jscript/engine/Environment.java
Normal file
@ -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<String, ObjectValue> 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;
|
||||
}
|
||||
}
|
8
src/me/topchetoeu/jscript/engine/WrappersProvider.java
Normal file
8
src/me/topchetoeu/jscript/engine/WrappersProvider.java
Normal file
@ -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);
|
||||
}
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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<ValueVariable> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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<Location, Integer> breakableLocToIndex = new LinkedHashMap<>();
|
||||
public final LinkedHashMap<Integer, Location> 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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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<Object> 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<Object> 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) { }
|
||||
|
@ -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<Class<?>, FunctionValue> constructors = new HashMap<>();
|
||||
private final HashMap<Class<?>, 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);
|
||||
}
|
||||
|
@ -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<Instruction>();
|
||||
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) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<Integer, Thread> intervals = new HashMap<>();
|
||||
private HashMap<Integer, Thread> timeouts = new HashMap<>();
|
||||
private HashMap<String, Symbol> symbols = new HashMap<>();
|
||||
private int nextId = 0;
|
||||
|
||||
@Native
|
||||
public double parseFloat(CallContext ctx, Object val) throws InterruptedException {
|
||||
return Values.toNumber(ctx, val);
|
||||
@Native public void markSpecial(FunctionValue... funcs) {
|
||||
for (var func : funcs) {
|
||||
func.special = true;
|
||||
}
|
||||
|
||||
@Native
|
||||
public boolean isArr(Object val) {
|
||||
return val instanceof ArrayValue;
|
||||
}
|
||||
|
||||
@NativeGetter("symbolProto")
|
||||
public ObjectValue symbolProto(CallContext ctx) {
|
||||
return ctx.engine().symbolProto();
|
||||
@Native public Environment getEnv(Object func) {
|
||||
if (func instanceof CodeFunction) return ((CodeFunction)func).environment;
|
||||
else return null;
|
||||
}
|
||||
@Native
|
||||
public final Object apply(CallContext ctx, FunctionValue func, Object th, ArrayValue args) throws InterruptedException {
|
||||
return func.call(ctx, th, args.toArray());
|
||||
@Native public Object setEnv(Object func, Environment env) {
|
||||
if (func instanceof CodeFunction) ((CodeFunction)func).environment = env;
|
||||
return func;
|
||||
}
|
||||
@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 Object apply(CallContext ctx, FunctionValue func, Object thisArg, ArrayValue args) throws InterruptedException {
|
||||
return func.call(ctx, thisArg, args.toArray());
|
||||
}
|
||||
@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);
|
||||
}
|
||||
catch (IndexOutOfBoundsException e) { return false; }
|
||||
|
||||
}
|
||||
|
||||
@Native
|
||||
public int setInterval(CallContext ctx, FunctionValue func, double delay) {
|
||||
@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);
|
||||
}
|
||||
ctx.engine.pushMsg(false, ctx.data(), ctx.environment, callback, 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);
|
||||
return new NativeFunction((_ctx, thisArg, args) -> {
|
||||
thread.interrupt();
|
||||
return null;
|
||||
});
|
||||
thread.start();
|
||||
|
||||
timeouts.put(++nextId, thread);
|
||||
|
||||
return nextId;
|
||||
}
|
||||
@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();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user