Core library reprogramming #5
2
build.js
2
build.js
@ -54,7 +54,7 @@ async function compileJava() {
|
|||||||
.replace('${AUTHOR}', conf.author)
|
.replace('${AUTHOR}', conf.author)
|
||||||
);
|
);
|
||||||
const args = ['--release', '11', ];
|
const args = ['--release', '11', ];
|
||||||
if (argv[1] === 'debug') args.push('-g');
|
if (argv[2] === 'debug') args.push('-g');
|
||||||
args.push('-d', 'dst/classes', 'Metadata.java');
|
args.push('-d', 'dst/classes', 'Metadata.java');
|
||||||
|
|
||||||
for await (const path of find('src', undefined, v => v.endsWith('.java') && !v.endsWith('Metadata.java'))) args.push(path);
|
for await (const path of find('src', undefined, v => v.endsWith('.java') && !v.endsWith('Metadata.java'))) args.push(path);
|
||||||
|
71
lib/core.ts
71
lib/core.ts
@ -1,71 +0,0 @@
|
|||||||
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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
constructor: {
|
|
||||||
log(...args: any[]): void;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var env: Environment = arguments[0], internals: Internals = arguments[1];
|
|
||||||
globalThis.log = internals.constructor.log;
|
|
||||||
|
|
||||||
try {
|
|
||||||
run('values/object');
|
|
||||||
run('values/symbol');
|
|
||||||
run('values/function');
|
|
||||||
run('values/errors');
|
|
||||||
run('values/string');
|
|
||||||
run('values/number');
|
|
||||||
run('values/boolean');
|
|
||||||
run('values/array');
|
|
||||||
run('promise');
|
|
||||||
run('map');
|
|
||||||
run('set');
|
|
||||||
run('regex');
|
|
||||||
run('timeout');
|
|
||||||
|
|
||||||
env.global.log = log;
|
|
||||||
|
|
||||||
log('Loaded polyfills!');
|
|
||||||
}
|
|
||||||
catch (e: any) {
|
|
||||||
let err = 'Uncaught error while loading polyfills: ';
|
|
||||||
|
|
||||||
if (typeof Error !== 'undefined' && e instanceof Error && e.toString !== {}.toString) err += e;
|
|
||||||
else if ('message' in e) {
|
|
||||||
if ('name' in e) err += e.name + ": " + e.message;
|
|
||||||
else err += 'Error: ' + e.message;
|
|
||||||
}
|
|
||||||
else err += e;
|
|
||||||
|
|
||||||
log(e);
|
|
||||||
}
|
|
93
lib/map.ts
93
lib/map.ts
@ -1,93 +0,0 @@
|
|||||||
define("map", () => {
|
|
||||||
const syms = { values: internals.symbol('Map.values') } as { readonly values: unique symbol };
|
|
||||||
const Object = env.global.Object;
|
|
||||||
|
|
||||||
class Map<KeyT, ValueT> {
|
|
||||||
[syms.values]: any = {};
|
|
||||||
|
|
||||||
public [env.global.Symbol.iterator](): IterableIterator<[KeyT, ValueT]> {
|
|
||||||
return this.entries();
|
|
||||||
}
|
|
||||||
|
|
||||||
public clear() {
|
|
||||||
this[syms.values] = {};
|
|
||||||
}
|
|
||||||
public delete(key: KeyT) {
|
|
||||||
if ((key as any) in this[syms.values]) {
|
|
||||||
delete this[syms.values];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public entries(): IterableIterator<[KeyT, ValueT]> {
|
|
||||||
const keys = internals.ownPropKeys(this[syms.values]);
|
|
||||||
let i = 0;
|
|
||||||
|
|
||||||
return {
|
|
||||||
next: () => {
|
|
||||||
if (i >= keys.length) return { done: true };
|
|
||||||
else return { done: false, value: [ keys[i], this[syms.values][keys[i++]] ] }
|
|
||||||
},
|
|
||||||
[env.global.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] }
|
|
||||||
},
|
|
||||||
[env.global.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++]] }
|
|
||||||
},
|
|
||||||
[env.global.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[env.global.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;
|
|
||||||
});
|
|
@ -1,13 +0,0 @@
|
|||||||
var { define, run } = (() => {
|
|
||||||
const modules: Record<string, Function> = {};
|
|
||||||
|
|
||||||
function define(name: string, func: Function) {
|
|
||||||
modules[name] = func;
|
|
||||||
}
|
|
||||||
function run(name: string) {
|
|
||||||
if (typeof modules[name] === 'function') return modules[name]();
|
|
||||||
else throw "The module '" + name + "' doesn't exist.";
|
|
||||||
}
|
|
||||||
|
|
||||||
return { define, run };
|
|
||||||
})();
|
|
203
lib/promise.ts
203
lib/promise.ts
@ -1,203 +0,0 @@
|
|||||||
define("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;
|
|
||||||
});
|
|
143
lib/regex.ts
143
lib/regex.ts
@ -1,143 +0,0 @@
|
|||||||
define("regex", () => {
|
|
||||||
// var RegExp = env.global.RegExp = env.internals.RegExp;
|
|
||||||
|
|
||||||
// setProps(RegExp.prototype as RegExp, env, {
|
|
||||||
// [Symbol.typeName]: 'RegExp',
|
|
||||||
|
|
||||||
// test(val) {
|
|
||||||
// return !!this.exec(val);
|
|
||||||
// },
|
|
||||||
// toString() {
|
|
||||||
// return '/' + this.source + '/' + this.flags;
|
|
||||||
// },
|
|
||||||
|
|
||||||
// [Symbol.match](target) {
|
|
||||||
// if (this.global) {
|
|
||||||
// const res: string[] = [];
|
|
||||||
// let val;
|
|
||||||
// while (val = this.exec(target)) {
|
|
||||||
// res.push(val[0]);
|
|
||||||
// }
|
|
||||||
// this.lastIndex = 0;
|
|
||||||
// return res;
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// const res = this.exec(target);
|
|
||||||
// if (!this.sticky) this.lastIndex = 0;
|
|
||||||
// return res;
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// [Symbol.matchAll](target) {
|
|
||||||
// let pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp;
|
|
||||||
|
|
||||||
// return {
|
|
||||||
// next: (): IteratorResult<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[] = [];
|
|
||||||
|
|
||||||
// while ((match = pattern.exec(target)) !== null) {
|
|
||||||
// let added: string[] = [];
|
|
||||||
|
|
||||||
// if (match.index >= target.length) break;
|
|
||||||
|
|
||||||
// if (match[0].length === 0) {
|
|
||||||
// added = [ target.substring(lastEnd, pattern.lastIndex), ];
|
|
||||||
// if (pattern.lastIndex < target.length) added.push(...match.slice(1));
|
|
||||||
// }
|
|
||||||
// else if (match.index - lastEnd > 0) {
|
|
||||||
// added = [ target.substring(lastEnd, match.index), ...match.slice(1) ];
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// for (let i = 1; i < match.length; i++) {
|
|
||||||
// res[res.length - match.length + i] = match[i];
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (sensible) {
|
|
||||||
// if (limit !== undefined && res.length + added.length >= limit) break;
|
|
||||||
// else res.push(...added);
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// for (let i = 0; i < added.length; i++) {
|
|
||||||
// if (limit !== undefined && res.length >= limit) return res;
|
|
||||||
// else res.push(added[i]);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// lastEnd = pattern.lastIndex;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (lastEnd < target.length) {
|
|
||||||
// res.push(target.substring(lastEnd));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return res;
|
|
||||||
// },
|
|
||||||
// [Symbol.replace](target, replacement) {
|
|
||||||
// const pattern = new this.constructor(this, this.flags + "d") as RegExp;
|
|
||||||
// let match: RegExpResult | null;
|
|
||||||
// let lastEnd = 0;
|
|
||||||
// const res: string[] = [];
|
|
||||||
|
|
||||||
// // log(pattern.toString());
|
|
||||||
|
|
||||||
// while ((match = pattern.exec(target)) !== null) {
|
|
||||||
// const indices = match.indices![0];
|
|
||||||
// res.push(target.substring(lastEnd, indices[0]));
|
|
||||||
// if (replacement instanceof Function) {
|
|
||||||
// res.push(replacement(target.substring(indices[0], indices[1]), ...match.slice(1), indices[0], target));
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// res.push(replacement);
|
|
||||||
// }
|
|
||||||
// lastEnd = indices[1];
|
|
||||||
// if (!pattern.global) break;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (lastEnd < target.length) {
|
|
||||||
// res.push(target.substring(lastEnd));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return res.join('');
|
|
||||||
// },
|
|
||||||
// [Symbol.search](target, reverse, start) {
|
|
||||||
// const pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp;
|
|
||||||
|
|
||||||
|
|
||||||
// if (!reverse) {
|
|
||||||
// pattern.lastIndex = (start as any) | 0;
|
|
||||||
// const res = pattern.exec(target);
|
|
||||||
// if (res) return res.index;
|
|
||||||
// else return -1;
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// start ??= target.length;
|
|
||||||
// start |= 0;
|
|
||||||
// let res: RegExpResult | null = null;
|
|
||||||
|
|
||||||
// while (true) {
|
|
||||||
// const tmp = pattern.exec(target);
|
|
||||||
// if (tmp === null || tmp.index > start) break;
|
|
||||||
// res = tmp;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (res && res.index <= start) return res.index;
|
|
||||||
// else return -1;
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
});
|
|
81
lib/set.ts
81
lib/set.ts
@ -1,81 +0,0 @@
|
|||||||
define("set", () => {
|
|
||||||
const syms = { values: internals.symbol('Map.values') } as { readonly values: unique symbol };
|
|
||||||
const Object = env.global.Object;
|
|
||||||
|
|
||||||
class Set<T> {
|
|
||||||
[syms.values]: any = {};
|
|
||||||
|
|
||||||
public [env.global.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] ] }
|
|
||||||
},
|
|
||||||
[env.global.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] }
|
|
||||||
},
|
|
||||||
[env.global.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[env.global.Symbol.iterator]();
|
|
||||||
|
|
||||||
for (let el = it.next(); !el.done; el = it.next()) {
|
|
||||||
this[syms.values][el.value] = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
env.global.Set = Set;
|
|
||||||
});
|
|
@ -1,38 +0,0 @@
|
|||||||
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!;
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,34 +0,0 @@
|
|||||||
{
|
|
||||||
"files": [
|
|
||||||
"lib.d.ts",
|
|
||||||
"modules.ts",
|
|
||||||
"utils.ts",
|
|
||||||
"values/object.ts",
|
|
||||||
"values/symbol.ts",
|
|
||||||
"values/function.ts",
|
|
||||||
"values/errors.ts",
|
|
||||||
"values/string.ts",
|
|
||||||
"values/number.ts",
|
|
||||||
"values/boolean.ts",
|
|
||||||
"values/array.ts",
|
|
||||||
"promise.ts",
|
|
||||||
"map.ts",
|
|
||||||
"set.ts",
|
|
||||||
"regex.ts",
|
|
||||||
"timeout.ts",
|
|
||||||
"core.ts"
|
|
||||||
],
|
|
||||||
"compilerOptions": {
|
|
||||||
"outFile": "../bin/me/topchetoeu/jscript/js/core.js",
|
|
||||||
// "declarationDir": "",
|
|
||||||
// "declarationDir": "bin/me/topchetoeu/jscript/dts",
|
|
||||||
"target": "ES5",
|
|
||||||
"lib": [],
|
|
||||||
"module": "None",
|
|
||||||
"stripInternal": true,
|
|
||||||
"downlevelIteration": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"strict": true,
|
|
||||||
}
|
|
||||||
}
|
|
38
lib/utils.ts
38
lib/utils.ts
@ -1,38 +0,0 @@
|
|||||||
function setProps<
|
|
||||||
TargetT extends object,
|
|
||||||
DescT extends {
|
|
||||||
[x in Exclude<keyof TargetT, 'constructor'> ]?: TargetT[x] extends ((...args: infer ArgsT) => infer RetT) ?
|
|
||||||
((this: TargetT, ...args: ArgsT) => RetT) :
|
|
||||||
TargetT[x]
|
|
||||||
}
|
|
||||||
>(target: TargetT, desc: DescT) {
|
|
||||||
var props = internals.keys(desc, false);
|
|
||||||
for (var i = 0; i < props.length; i++) {
|
|
||||||
var key = props[i];
|
|
||||||
internals.defineField(
|
|
||||||
target, key, (desc as any)[key],
|
|
||||||
true, // writable
|
|
||||||
false, // enumerable
|
|
||||||
true // configurable
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function setConstr(target: object, constr: Function) {
|
|
||||||
internals.defineField(
|
|
||||||
target, 'constructor', constr,
|
|
||||||
true, // writable
|
|
||||||
false, // enumerable
|
|
||||||
true // configurable
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function wrapI(max: number, i: number) {
|
|
||||||
i |= 0;
|
|
||||||
if (i < 0) i = max + i;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
function clampI(max: number, i: number) {
|
|
||||||
if (i < 0) i = 0;
|
|
||||||
if (i > max) i = max;
|
|
||||||
return i;
|
|
||||||
}
|
|
@ -1,336 +0,0 @@
|
|||||||
define("values/array", () => {
|
|
||||||
var Array = env.global.Array = function(len?: number) {
|
|
||||||
var res = [];
|
|
||||||
|
|
||||||
if (typeof len === 'number' && arguments.length === 1) {
|
|
||||||
if (len < 0) throw 'Invalid array length.';
|
|
||||||
res.length = len;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
|
||||||
res[i] = arguments[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
} as ArrayConstructor;
|
|
||||||
|
|
||||||
env.setProto('array', Array.prototype);
|
|
||||||
(Array.prototype as any)[env.global.Symbol.typeName] = "Array";
|
|
||||||
setConstr(Array.prototype, Array);
|
|
||||||
|
|
||||||
setProps(Array.prototype, {
|
|
||||||
[env.global.Symbol.iterator]: function() {
|
|
||||||
return this.values();
|
|
||||||
},
|
|
||||||
[env.global.Symbol.typeName]: "Array",
|
|
||||||
|
|
||||||
values() {
|
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
return {
|
|
||||||
next: () => {
|
|
||||||
while (i < this.length) {
|
|
||||||
if (i++ in this) return { done: false, value: this[i - 1] };
|
|
||||||
}
|
|
||||||
return { done: true, value: undefined };
|
|
||||||
},
|
|
||||||
[env.global.Symbol.iterator]() { return this; }
|
|
||||||
};
|
|
||||||
},
|
|
||||||
keys() {
|
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
return {
|
|
||||||
next: () => {
|
|
||||||
while (i < this.length) {
|
|
||||||
if (i++ in this) return { done: false, value: i - 1 };
|
|
||||||
}
|
|
||||||
return { done: true, value: undefined };
|
|
||||||
},
|
|
||||||
[env.global.Symbol.iterator]() { return this; }
|
|
||||||
};
|
|
||||||
},
|
|
||||||
entries() {
|
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
return {
|
|
||||||
next: () => {
|
|
||||||
while (i < this.length) {
|
|
||||||
if (i++ in this) return { done: false, value: [i - 1, this[i - 1]] };
|
|
||||||
}
|
|
||||||
return { done: true, value: undefined };
|
|
||||||
},
|
|
||||||
[env.global.Symbol.iterator]() { return this; }
|
|
||||||
};
|
|
||||||
},
|
|
||||||
concat() {
|
|
||||||
var res = [] as any[];
|
|
||||||
res.push.apply(res, this);
|
|
||||||
|
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
|
||||||
var arg = arguments[i];
|
|
||||||
if (arg instanceof Array) {
|
|
||||||
res.push.apply(res, arg);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
res.push(arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
every(func, thisArg) {
|
|
||||||
if (typeof func !== 'function') throw new TypeError("Given argument not a function.");
|
|
||||||
func = func.bind(thisArg);
|
|
||||||
|
|
||||||
for (var i = 0; i < this.length; i++) {
|
|
||||||
if (!func(this[i], i, this)) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
some(func, thisArg) {
|
|
||||||
if (typeof func !== 'function') throw new TypeError("Given argument not a function.");
|
|
||||||
func = func.bind(thisArg);
|
|
||||||
|
|
||||||
for (var i = 0; i < this.length; i++) {
|
|
||||||
if (func(this[i], i, this)) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
fill(val, start, end) {
|
|
||||||
if (arguments.length < 3) end = this.length;
|
|
||||||
if (arguments.length < 2) start = 0;
|
|
||||||
|
|
||||||
start = clampI(this.length, wrapI(this.length + 1, start ?? 0));
|
|
||||||
end = clampI(this.length, wrapI(this.length + 1, end ?? this.length));
|
|
||||||
|
|
||||||
for (; start < end; start++) {
|
|
||||||
this[start] = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
filter(func, thisArg) {
|
|
||||||
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
|
|
||||||
|
|
||||||
var res = [];
|
|
||||||
for (var i = 0; i < this.length; i++) {
|
|
||||||
if (i in this && func.call(thisArg, this[i], i, this)) res.push(this[i]);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
find(func, thisArg) {
|
|
||||||
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
|
|
||||||
|
|
||||||
for (var i = 0; i < this.length; i++) {
|
|
||||||
if (i in this && func.call(thisArg, this[i], i, this)) return this[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
},
|
|
||||||
findIndex(func, thisArg) {
|
|
||||||
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
|
|
||||||
|
|
||||||
for (var i = 0; i < this.length; i++) {
|
|
||||||
if (i in this && func.call(thisArg, this[i], i, this)) return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
},
|
|
||||||
findLast(func, thisArg) {
|
|
||||||
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
|
|
||||||
|
|
||||||
for (var i = this.length - 1; i >= 0; i--) {
|
|
||||||
if (i in this && func.call(thisArg, this[i], i, this)) return this[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
},
|
|
||||||
findLastIndex(func, thisArg) {
|
|
||||||
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
|
|
||||||
|
|
||||||
for (var i = this.length - 1; i >= 0; i--) {
|
|
||||||
if (i in this && func.call(thisArg, this[i], i, this)) return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
},
|
|
||||||
flat(depth) {
|
|
||||||
var res = [] as any[];
|
|
||||||
var buff = [];
|
|
||||||
res.push(...this);
|
|
||||||
|
|
||||||
for (var i = 0; i < (depth ?? 1); i++) {
|
|
||||||
var anyArrays = false;
|
|
||||||
for (var el of res) {
|
|
||||||
if (el instanceof Array) {
|
|
||||||
buff.push(...el);
|
|
||||||
anyArrays = true;
|
|
||||||
}
|
|
||||||
else buff.push(el);
|
|
||||||
}
|
|
||||||
|
|
||||||
res = buff;
|
|
||||||
buff = [];
|
|
||||||
if (!anyArrays) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
flatMap(func, th) {
|
|
||||||
return this.map(func, th).flat();
|
|
||||||
},
|
|
||||||
forEach(func, thisArg) {
|
|
||||||
for (var i = 0; i < this.length; i++) {
|
|
||||||
if (i in this) func.call(thisArg, this[i], i, this);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
map(func, thisArg) {
|
|
||||||
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
|
|
||||||
|
|
||||||
var res = [];
|
|
||||||
for (var i = 0; i < this.length; i++) {
|
|
||||||
if (i in this) res[i] = func.call(thisArg, this[i], i, this);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
pop() {
|
|
||||||
if (this.length === 0) return undefined;
|
|
||||||
var val = this[this.length - 1];
|
|
||||||
this.length--;
|
|
||||||
return val;
|
|
||||||
},
|
|
||||||
push() {
|
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
|
||||||
this[this.length] = arguments[i];
|
|
||||||
}
|
|
||||||
return arguments.length;
|
|
||||||
},
|
|
||||||
shift() {
|
|
||||||
if (this.length === 0) return undefined;
|
|
||||||
var res = this[0];
|
|
||||||
|
|
||||||
for (var i = 0; i < this.length - 1; i++) {
|
|
||||||
this[i] = this[i + 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.length--;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
unshift() {
|
|
||||||
for (var i = this.length - 1; i >= 0; i--) {
|
|
||||||
this[i + arguments.length] = this[i];
|
|
||||||
}
|
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
|
||||||
this[i] = arguments[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return arguments.length;
|
|
||||||
},
|
|
||||||
slice(start, end) {
|
|
||||||
start = clampI(this.length, wrapI(this.length + 1, start ?? 0));
|
|
||||||
end = clampI(this.length, wrapI(this.length + 1, end ?? this.length));
|
|
||||||
|
|
||||||
var res: any[] = [];
|
|
||||||
var n = end - start;
|
|
||||||
if (n <= 0) return res;
|
|
||||||
|
|
||||||
for (var i = 0; i < n; i++) {
|
|
||||||
res[i] = this[start + i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
toString() {
|
|
||||||
let res = '';
|
|
||||||
for (let i = 0; i < this.length; i++) {
|
|
||||||
if (i > 0) res += ',';
|
|
||||||
if (i in this && this[i] !== undefined && this[i] !== null) res += this[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
indexOf(el, start) {
|
|
||||||
start = start! | 0;
|
|
||||||
for (var i = Math.max(0, start); i < this.length; i++) {
|
|
||||||
if (i in this && this[i] == el) return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
},
|
|
||||||
lastIndexOf(el, start) {
|
|
||||||
start = start! | 0;
|
|
||||||
for (var i = this.length; i >= start; i--) {
|
|
||||||
if (i in this && this[i] == el) return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
},
|
|
||||||
includes(el, start) {
|
|
||||||
return this.indexOf(el, start) >= 0;
|
|
||||||
},
|
|
||||||
join(val = ',') {
|
|
||||||
let res = '', first = true;
|
|
||||||
|
|
||||||
for (let i = 0; i < this.length; i++) {
|
|
||||||
if (!(i in this)) continue;
|
|
||||||
if (!first) res += val;
|
|
||||||
first = false;
|
|
||||||
res += this[i];
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
sort(func) {
|
|
||||||
func ??= (a, b) => {
|
|
||||||
const _a = a + '';
|
|
||||||
const _b = b + '';
|
|
||||||
|
|
||||||
if (_a > _b) return 1;
|
|
||||||
if (_a < _b) return -1;
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (typeof func !== 'function') throw new TypeError('Expected func to be undefined or a function.');
|
|
||||||
|
|
||||||
internals.sort(this, func);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
splice(start, deleteCount, ...items) {
|
|
||||||
start = clampI(this.length, wrapI(this.length, start ?? 0));
|
|
||||||
deleteCount = (deleteCount ?? Infinity | 0);
|
|
||||||
if (start + deleteCount >= this.length) deleteCount = this.length - start;
|
|
||||||
|
|
||||||
const res = this.slice(start, start + deleteCount);
|
|
||||||
const moveN = items.length - deleteCount;
|
|
||||||
const len = this.length;
|
|
||||||
|
|
||||||
if (moveN < 0) {
|
|
||||||
for (let i = start - moveN; i < len; i++) {
|
|
||||||
this[i + moveN] = this[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (moveN > 0) {
|
|
||||||
for (let i = len - 1; i >= start; i--) {
|
|
||||||
this[i + moveN] = this[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
|
||||||
this[i + start] = items[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.length = len + moveN;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setProps(Array, {
|
|
||||||
isArray(val: any) { return internals.isArray(val); }
|
|
||||||
});
|
|
||||||
internals.markSpecial(Array);
|
|
||||||
});
|
|
@ -1,12 +0,0 @@
|
|||||||
define("values/boolean", () => {
|
|
||||||
var Boolean = env.global.Boolean = function (this: Boolean | undefined, arg) {
|
|
||||||
var val;
|
|
||||||
if (arguments.length === 0) val = false;
|
|
||||||
else val = !!arg;
|
|
||||||
if (this === undefined || this === null) return val;
|
|
||||||
else (this as any).value = val;
|
|
||||||
} as BooleanConstructor;
|
|
||||||
|
|
||||||
env.setProto('bool', Boolean.prototype);
|
|
||||||
setConstr(Boolean.prototype, Boolean);
|
|
||||||
});
|
|
@ -1,46 +0,0 @@
|
|||||||
define("values/errors", () => {
|
|
||||||
var Error = env.global.Error = function Error(msg: string) {
|
|
||||||
if (msg === undefined) msg = '';
|
|
||||||
else msg += '';
|
|
||||||
|
|
||||||
return Object.setPrototypeOf({
|
|
||||||
message: msg,
|
|
||||||
stack: [] as string[],
|
|
||||||
}, Error.prototype);
|
|
||||||
} as ErrorConstructor;
|
|
||||||
|
|
||||||
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<T1 extends ErrorConstructor>(name: string, proto: string): T1 {
|
|
||||||
function constr (msg: string) {
|
|
||||||
var res = new Error(msg);
|
|
||||||
(res as any).__proto__ = constr.prototype;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
(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');
|
|
||||||
});
|
|
@ -1,140 +0,0 @@
|
|||||||
define("values/function", () => {
|
|
||||||
var Function = env.global.Function = function() {
|
|
||||||
throw 'Using the constructor Function() is forbidden.';
|
|
||||||
} as unknown as FunctionConstructor;
|
|
||||||
|
|
||||||
env.setProto('function', Function.prototype);
|
|
||||||
setConstr(Function.prototype, Function);
|
|
||||||
|
|
||||||
setProps(Function.prototype, {
|
|
||||||
apply(thisArg, args) {
|
|
||||||
if (typeof args !== 'object') throw 'Expected arguments to be an array-like object.';
|
|
||||||
var len = args.length - 0;
|
|
||||||
let newArgs: any[];
|
|
||||||
if (internals.isArray(args)) newArgs = args;
|
|
||||||
else {
|
|
||||||
newArgs = [];
|
|
||||||
|
|
||||||
while (len >= 0) {
|
|
||||||
len--;
|
|
||||||
newArgs[len] = args[len];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return internals.apply(this, thisArg, newArgs);
|
|
||||||
},
|
|
||||||
call(thisArg, ...args) {
|
|
||||||
return this.apply(thisArg, args);
|
|
||||||
},
|
|
||||||
bind(thisArg, ...args) {
|
|
||||||
const func = this;
|
|
||||||
const res = function() {
|
|
||||||
const resArgs = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < args.length; i++) {
|
|
||||||
resArgs[i] = args[i];
|
|
||||||
}
|
|
||||||
for (let i = 0; i < arguments.length; i++) {
|
|
||||||
resArgs[i + args.length] = arguments[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return func.apply(thisArg, resArgs);
|
|
||||||
};
|
|
||||||
res.name = "<bound> " + func.name;
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
toString() {
|
|
||||||
return 'function (...) { ... }';
|
|
||||||
},
|
|
||||||
});
|
|
||||||
setProps(Function, {
|
|
||||||
async(func) {
|
|
||||||
if (typeof func !== 'function') throw new TypeError('Expected func to be function.');
|
|
||||||
|
|
||||||
return function (this: any) {
|
|
||||||
const args = arguments;
|
|
||||||
|
|
||||||
return new Promise((res, rej) => {
|
|
||||||
const gen = internals.apply(internals.generator(func as any), this, args as any);
|
|
||||||
|
|
||||||
(function next(type: 'none' | 'err' | 'ret', val?: any) {
|
|
||||||
try {
|
|
||||||
let result;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case 'err': result = gen.throw(val); break;
|
|
||||||
case 'ret': result = gen.next(val); break;
|
|
||||||
case 'none': result = gen.next(); break;
|
|
||||||
}
|
|
||||||
if (result.done) res(result.value);
|
|
||||||
else Promise.resolve(result.value).then(
|
|
||||||
v => next('ret', v),
|
|
||||||
v => next('err', v)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
rej(e);
|
|
||||||
}
|
|
||||||
})('none');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
},
|
|
||||||
asyncGenerator(func) {
|
|
||||||
if (typeof func !== 'function') throw new TypeError('Expected func to be function.');
|
|
||||||
|
|
||||||
return function(this: any, ...args: any[]) {
|
|
||||||
const gen = internals.apply(internals.generator((_yield) => func(
|
|
||||||
val => _yield(['await', val]) as any,
|
|
||||||
val => _yield(['yield', val])
|
|
||||||
)), this, args) as Generator<['await' | 'yield', any]>;
|
|
||||||
|
|
||||||
const next = (resolve: Function, reject: Function, type: 'none' | 'val' | 'ret' | 'err', val?: any) => {
|
|
||||||
let res;
|
|
||||||
|
|
||||||
try {
|
|
||||||
switch (type) {
|
|
||||||
case 'val': res = gen.next(val); break;
|
|
||||||
case 'ret': res = gen.return(val); break;
|
|
||||||
case 'err': res = gen.throw(val); break;
|
|
||||||
default: res = gen.next(); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (e) { return reject(e); }
|
|
||||||
|
|
||||||
if (res.done) return { done: true, res: <any>res };
|
|
||||||
else if (res.value[0] === 'await') Promise.resolve(res.value[1]).then(
|
|
||||||
v => next(resolve, reject, 'val', v),
|
|
||||||
v => next(resolve, reject, 'err', v),
|
|
||||||
)
|
|
||||||
else resolve({ done: false, value: res.value[1] });
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
next() {
|
|
||||||
const args = arguments;
|
|
||||||
if (arguments.length === 0) return new Promise((res, rej) => next(res, rej, 'none'));
|
|
||||||
else return new Promise((res, rej) => next(res, rej, 'val', args[0]));
|
|
||||||
},
|
|
||||||
return: (value) => new Promise((res, rej) => next(res, rej, 'ret', value)),
|
|
||||||
throw: (value) => new Promise((res, rej) => next(res, rej, 'err', value)),
|
|
||||||
[env.global.Symbol.asyncIterator]() { return this; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
generator(func) {
|
|
||||||
if (typeof func !== 'function') throw new TypeError('Expected func to be function.');
|
|
||||||
const gen = internals.generator(func);
|
|
||||||
return function(this: any, ...args: any[]) {
|
|
||||||
const it = internals.apply(gen, this, args);
|
|
||||||
|
|
||||||
return {
|
|
||||||
next: (...args) => internals.apply(it.next, it, args),
|
|
||||||
return: (val) => internals.apply(it.next, it, [val]),
|
|
||||||
throw: (val) => internals.apply(it.next, it, [val]),
|
|
||||||
[env.global.Symbol.iterator]() { return this; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
internals.markSpecial(Function);
|
|
||||||
});
|
|
@ -1,33 +0,0 @@
|
|||||||
define("values/number", () => {
|
|
||||||
var Number = env.global.Number = function(this: Number | undefined, arg: any) {
|
|
||||||
var val;
|
|
||||||
if (arguments.length === 0) val = 0;
|
|
||||||
else val = arg - 0;
|
|
||||||
if (this === undefined || this === null) return val;
|
|
||||||
else (this as any).value = val;
|
|
||||||
} as NumberConstructor;
|
|
||||||
|
|
||||||
env.setProto('number', Number.prototype);
|
|
||||||
setConstr(Number.prototype, Number);
|
|
||||||
|
|
||||||
setProps(Number.prototype, {
|
|
||||||
valueOf() {
|
|
||||||
if (typeof this === 'number') return this;
|
|
||||||
else return (this as any).value;
|
|
||||||
},
|
|
||||||
toString() {
|
|
||||||
if (typeof this === 'number') return this + '';
|
|
||||||
else return (this as any).value + '';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setProps(Number, {
|
|
||||||
parseInt(val) { return Math.trunc(val as any - 0); },
|
|
||||||
parseFloat(val) { return val as any - 0; },
|
|
||||||
});
|
|
||||||
|
|
||||||
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 });
|
|
||||||
});
|
|
@ -1,226 +0,0 @@
|
|||||||
define("values/object", () => {
|
|
||||||
var Object = env.global.Object = function(arg: any) {
|
|
||||||
if (arg === undefined || arg === null) return {};
|
|
||||||
else if (typeof arg === 'boolean') return new Boolean(arg);
|
|
||||||
else if (typeof arg === 'number') return new Number(arg);
|
|
||||||
else if (typeof arg === 'string') return new String(arg);
|
|
||||||
return arg;
|
|
||||||
} as ObjectConstructor;
|
|
||||||
|
|
||||||
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') {
|
|
||||||
throw new TypeError(`Object.${name} may only be used for objects.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function check(obj: any) {
|
|
||||||
return typeof obj === 'object' && obj !== null || typeof obj === 'function';
|
|
||||||
}
|
|
||||||
|
|
||||||
setProps(Object, {
|
|
||||||
assign(dst, ...src) {
|
|
||||||
throwNotObject(dst, 'assign');
|
|
||||||
for (let i = 0; i < src.length; i++) {
|
|
||||||
const obj = src[i];
|
|
||||||
throwNotObject(obj, 'assign');
|
|
||||||
for (const key of Object.keys(obj)) {
|
|
||||||
(dst as any)[key] = (obj as any)[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dst;
|
|
||||||
},
|
|
||||||
create(obj, props) {
|
|
||||||
props ??= {};
|
|
||||||
return Object.defineProperties({ __proto__: obj }, props as any) as any;
|
|
||||||
},
|
|
||||||
|
|
||||||
defineProperty(obj, key, attrib) {
|
|
||||||
throwNotObject(obj, 'defineProperty');
|
|
||||||
if (typeof attrib !== 'object') throw new TypeError('Expected attributes to be an object.');
|
|
||||||
|
|
||||||
if ('value' in attrib) {
|
|
||||||
if ('get' in attrib || 'set' in attrib) throw new TypeError('Cannot specify a value and accessors for a property.');
|
|
||||||
if (!internals.defineField(
|
|
||||||
obj, key,
|
|
||||||
attrib.value,
|
|
||||||
!!attrib.writable,
|
|
||||||
!!attrib.enumerable,
|
|
||||||
!!attrib.configurable
|
|
||||||
)) throw new TypeError('Can\'t define property \'' + key + '\'.');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (typeof attrib.get !== 'function' && attrib.get !== undefined) throw new TypeError('Get accessor must be a function.');
|
|
||||||
if (typeof attrib.set !== 'function' && attrib.set !== undefined) throw new TypeError('Set accessor must be a function.');
|
|
||||||
|
|
||||||
if (!internals.defineProp(
|
|
||||||
obj, key,
|
|
||||||
attrib.get,
|
|
||||||
attrib.set,
|
|
||||||
!!attrib.enumerable,
|
|
||||||
!!attrib.configurable
|
|
||||||
)) throw new TypeError('Can\'t define property \'' + key + '\'.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
},
|
|
||||||
defineProperties(obj, attrib) {
|
|
||||||
throwNotObject(obj, 'defineProperties');
|
|
||||||
if (typeof attrib !== 'object' && typeof attrib !== 'function') throw 'Expected second argument to be an object.';
|
|
||||||
|
|
||||||
for (var key in attrib) {
|
|
||||||
Object.defineProperty(obj, key, attrib[key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
},
|
|
||||||
|
|
||||||
keys(obj, onlyString) {
|
|
||||||
return internals.keys(obj, !!(onlyString ?? true));
|
|
||||||
},
|
|
||||||
entries(obj, onlyString) {
|
|
||||||
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) {
|
|
||||||
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 internals.ownProp(obj, key) as any;
|
|
||||||
},
|
|
||||||
getOwnPropertyDescriptors(obj) {
|
|
||||||
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) {
|
|
||||||
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) {
|
|
||||||
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) {
|
|
||||||
const keys = internals.ownPropKeys(obj);
|
|
||||||
|
|
||||||
for (let i = 0; i < keys.length; i++) {
|
|
||||||
if (keys[i] === key) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
getPrototypeOf(obj) {
|
|
||||||
return obj.__proto__;
|
|
||||||
},
|
|
||||||
setPrototypeOf(obj, proto) {
|
|
||||||
(obj as any).__proto__ = proto;
|
|
||||||
return obj;
|
|
||||||
},
|
|
||||||
|
|
||||||
fromEntries(iterable) {
|
|
||||||
const res = {} as any;
|
|
||||||
|
|
||||||
for (const el of iterable) {
|
|
||||||
res[el[0]] = el[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
|
|
||||||
preventExtensions(obj) {
|
|
||||||
throwNotObject(obj, 'preventExtensions');
|
|
||||||
internals.lock(obj, 'ext');
|
|
||||||
return obj;
|
|
||||||
},
|
|
||||||
seal(obj) {
|
|
||||||
throwNotObject(obj, 'seal');
|
|
||||||
internals.lock(obj, 'seal');
|
|
||||||
return obj;
|
|
||||||
},
|
|
||||||
freeze(obj) {
|
|
||||||
throwNotObject(obj, 'freeze');
|
|
||||||
internals.lock(obj, 'freeze');
|
|
||||||
return obj;
|
|
||||||
},
|
|
||||||
|
|
||||||
isExtensible(obj) {
|
|
||||||
if (!check(obj)) return false;
|
|
||||||
return internals.extensible(obj);
|
|
||||||
},
|
|
||||||
isSealed(obj) {
|
|
||||||
if (!check(obj)) return true;
|
|
||||||
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 (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 true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setProps(Object.prototype, {
|
|
||||||
valueOf() {
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
toString() {
|
|
||||||
return '[object ' + (this[env.global.Symbol.typeName] ?? 'Unknown') + ']';
|
|
||||||
},
|
|
||||||
hasOwnProperty(key) {
|
|
||||||
return Object.hasOwn(this, key);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
internals.markSpecial(Object);
|
|
||||||
});
|
|
@ -1,267 +0,0 @@
|
|||||||
define("values/string", () => {
|
|
||||||
var String = env.global.String = function(this: String | undefined, arg: any) {
|
|
||||||
var val;
|
|
||||||
if (arguments.length === 0) val = '';
|
|
||||||
else val = arg + '';
|
|
||||||
if (this === undefined || this === null) return val;
|
|
||||||
else (this as any).value = val;
|
|
||||||
} as StringConstructor;
|
|
||||||
|
|
||||||
env.setProto('string', String.prototype);
|
|
||||||
setConstr(String.prototype, String);
|
|
||||||
|
|
||||||
setProps(String.prototype, {
|
|
||||||
toString() {
|
|
||||||
if (typeof this === 'string') return this;
|
|
||||||
else return (this as any).value;
|
|
||||||
},
|
|
||||||
valueOf() {
|
|
||||||
if (typeof this === 'string') return this;
|
|
||||||
else return (this as any).value;
|
|
||||||
},
|
|
||||||
|
|
||||||
substring(start, end) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.substring(start, end);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
start = start ?? 0 | 0;
|
|
||||||
end = (end ?? this.length) | 0;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
if (start >= this.length) start = this.length - 1;
|
|
||||||
if (start < 0) start = 0;
|
|
||||||
|
|
||||||
length = (length ?? this.length - start) | 0;
|
|
||||||
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() {
|
|
||||||
// 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() {
|
|
||||||
// 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) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.charAt(pos);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = pos | 0;
|
|
||||||
if (pos < 0 || pos >= this.length) return '';
|
|
||||||
return this[pos];
|
|
||||||
},
|
|
||||||
charCodeAt(pos) {
|
|
||||||
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) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.startsWith(term, pos);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
pos = pos! | 0;
|
|
||||||
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') {
|
|
||||||
if (this instanceof String) return (this as any).value.endsWith(term, pos);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
pos = (pos ?? this.length) | 0;
|
|
||||||
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) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.indexOf(term, start);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof term[env.global.Symbol.search] !== 'function') term = RegExp.escape(term);
|
|
||||||
|
|
||||||
return term[env.global.Symbol.search](this, false, start);
|
|
||||||
},
|
|
||||||
lastIndexOf(term: any, start) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.indexOf(term, start);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof term[env.global.Symbol.search] !== 'function') term = RegExp.escape(term);
|
|
||||||
|
|
||||||
return term[env.global.Symbol.search](this, true, start);
|
|
||||||
},
|
|
||||||
includes(term, start) {
|
|
||||||
return this.indexOf(term, start) >= 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
replace(pattern: any, val) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.replace(pattern, val);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof pattern[env.global.Symbol.replace] !== 'function') pattern = RegExp.escape(pattern);
|
|
||||||
|
|
||||||
return pattern[env.global.Symbol.replace](this, val);
|
|
||||||
},
|
|
||||||
replaceAll(pattern: any, val) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.replace(pattern, val);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof pattern[env.global.Symbol.replace] !== 'function') pattern = RegExp.escape(pattern, "g");
|
|
||||||
if (pattern instanceof RegExp && !pattern.global) pattern = new pattern.constructor(pattern.source, pattern.flags + "g");
|
|
||||||
|
|
||||||
return pattern[env.global.Symbol.replace](this, val);
|
|
||||||
},
|
|
||||||
|
|
||||||
match(pattern: any) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.match(pattern);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof pattern[env.global.Symbol.match] !== 'function') pattern = RegExp.escape(pattern);
|
|
||||||
|
|
||||||
return pattern[env.global.Symbol.match](this);
|
|
||||||
},
|
|
||||||
matchAll(pattern: any) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.matchAll(pattern);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof pattern[env.global.Symbol.match] !== 'function') pattern = RegExp.escape(pattern, "g");
|
|
||||||
if (pattern instanceof RegExp && !pattern.global) pattern = new pattern.constructor(pattern.source, pattern.flags + "g");
|
|
||||||
|
|
||||||
return pattern[env.global.Symbol.match](this);
|
|
||||||
},
|
|
||||||
|
|
||||||
split(pattern: any, lim, sensible) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.split(pattern, lim, sensible);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof pattern[env.global.Symbol.split] !== 'function') pattern = RegExp.escape(pattern, "g");
|
|
||||||
|
|
||||||
return pattern[env.global.Symbol.split](this, lim, sensible);
|
|
||||||
},
|
|
||||||
slice(start, end) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.slice(start, end);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
start = wrapI(this.length, start ?? 0 | 0);
|
|
||||||
end = wrapI(this.length, end ?? this.length | 0);
|
|
||||||
|
|
||||||
if (start > end) return '';
|
|
||||||
|
|
||||||
return this.substring(start, end);
|
|
||||||
},
|
|
||||||
|
|
||||||
concat(...args) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.concat(...args);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
var res = this;
|
|
||||||
for (var arg of args) res += arg;
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
|
|
||||||
trim() {
|
|
||||||
return this
|
|
||||||
.replace(/^\s+/g, '')
|
|
||||||
.replace(/\s+$/g, '');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setProps(String, {
|
|
||||||
fromCharCode(val) {
|
|
||||||
return internals.stringFromChars([val | 0]);
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
env.global.Object.defineProperty(String.prototype, 'length', {
|
|
||||||
get() {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.length;
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return internals.strlen(this);
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: false,
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,36 +0,0 @@
|
|||||||
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 internals.symbol(val);
|
|
||||||
} as SymbolConstructor;
|
|
||||||
|
|
||||||
env.setProto('symbol', Symbol.prototype);
|
|
||||||
setConstr(Symbol.prototype, Symbol);
|
|
||||||
|
|
||||||
setProps(Symbol, {
|
|
||||||
for(key) {
|
|
||||||
if (typeof key !== 'string' && key !== undefined) throw new TypeError('key must be a string or undefined.');
|
|
||||||
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 internals.symbolToString(sym);
|
|
||||||
},
|
|
||||||
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
|
|
||||||
internals.defineField(env.global.Object.prototype, Symbol.typeName, 'Object', false, false, false);
|
|
||||||
internals.defineField(env.global, Symbol.typeName, 'Window', false, false, false);
|
|
||||||
});
|
|
6
package-lock.json
generated
6
package-lock.json
generated
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "java-jscript",
|
|
||||||
"lockfileVersion": 3,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {}
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
{}
|
|
@ -7,21 +7,20 @@ import java.io.InputStreamReader;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.MessageContext;
|
import me.topchetoeu.jscript.engine.Message;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
|
||||||
import me.topchetoeu.jscript.engine.Engine;
|
import me.topchetoeu.jscript.engine.Engine;
|
||||||
import me.topchetoeu.jscript.engine.FunctionContext;
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.events.Observer;
|
import me.topchetoeu.jscript.events.Observer;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||||
import me.topchetoeu.jscript.interop.NativeTypeRegister;
|
|
||||||
import me.topchetoeu.jscript.polyfills.Internals;
|
import me.topchetoeu.jscript.polyfills.Internals;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
static Thread task;
|
static Thread task;
|
||||||
static Engine engine;
|
static Engine engine;
|
||||||
static FunctionContext env;
|
static Environment env;
|
||||||
|
|
||||||
public static String streamToString(InputStream in) {
|
public static String streamToString(InputStream in) {
|
||||||
try {
|
try {
|
||||||
@ -47,37 +46,14 @@ public class Main {
|
|||||||
|
|
||||||
private static Observer<Object> valuePrinter = new Observer<Object>() {
|
private static Observer<Object> valuePrinter = new Observer<Object>() {
|
||||||
public void next(Object data) {
|
public void next(Object data) {
|
||||||
try {
|
try { Values.printValue(null, data); }
|
||||||
Values.printValue(null, data);
|
|
||||||
}
|
|
||||||
catch (InterruptedException e) { }
|
catch (InterruptedException e) { }
|
||||||
System.out.println();
|
System.out.println();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void error(RuntimeException err) {
|
public void error(RuntimeException err) {
|
||||||
try {
|
try { Values.printError(err, null); }
|
||||||
try {
|
catch (InterruptedException ex) { return; }
|
||||||
if (err instanceof EngineException) {
|
|
||||||
System.out.println("Uncaught " + ((EngineException)err).toString(new Context(null, new MessageContext(engine))));
|
|
||||||
}
|
|
||||||
else if (err instanceof SyntaxException) {
|
|
||||||
System.out.println("Syntax error:" + ((SyntaxException)err).msg);
|
|
||||||
}
|
|
||||||
else if (err.getCause() instanceof InterruptedException) return;
|
|
||||||
else {
|
|
||||||
System.out.println("Internal error ocurred:");
|
|
||||||
err.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (EngineException ex) {
|
|
||||||
System.out.println("Uncaught ");
|
|
||||||
Values.printValue(null, ((EngineException)err).value);
|
|
||||||
System.out.println();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (InterruptedException ex) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -85,26 +61,30 @@ public class Main {
|
|||||||
System.out.println(String.format("Running %s v%s by %s", Metadata.NAME, Metadata.VERSION, Metadata.AUTHOR));
|
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));
|
var in = new BufferedReader(new InputStreamReader(System.in));
|
||||||
engine = new Engine();
|
engine = new Engine();
|
||||||
env = new FunctionContext(null, null, null);
|
|
||||||
var builderEnv = new FunctionContext(null, new NativeTypeRegister(), null);
|
env = new Environment(null, null, null);
|
||||||
var exited = new boolean[1];
|
var exited = new boolean[1];
|
||||||
|
|
||||||
env.global.define("exit", ctx -> {
|
engine.pushMsg(false, new Message(engine), new NativeFunction((ctx, thisArg, _a) -> {
|
||||||
|
new Internals().apply(env);
|
||||||
|
|
||||||
|
env.global.define("exit", _ctx -> {
|
||||||
exited[0] = true;
|
exited[0] = true;
|
||||||
task.interrupt();
|
task.interrupt();
|
||||||
throw new InterruptedException();
|
throw new InterruptedException();
|
||||||
});
|
});
|
||||||
env.global.define("go", ctx -> {
|
env.global.define("go", _ctx -> {
|
||||||
try {
|
try {
|
||||||
var func = ctx.compile("do.js", new String(Files.readAllBytes(Path.of("do.js"))));
|
var func = _ctx.compile("do.js", new String(Files.readAllBytes(Path.of("do.js"))));
|
||||||
return func.call(ctx);
|
return func.call(_ctx);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
throw new EngineException("Couldn't open do.js");
|
throw new EngineException("Couldn't open do.js");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
engine.pushMsg(false, new Context(builderEnv, new MessageContext(engine)), "core.js", resourceToString("js/core.js"), null, env, new Internals()).toObservable().on(valuePrinter);
|
return null;
|
||||||
|
}), null);
|
||||||
|
|
||||||
task = engine.start();
|
task = engine.start();
|
||||||
var reader = new Thread(() -> {
|
var reader = new Thread(() -> {
|
||||||
@ -114,7 +94,7 @@ public class Main {
|
|||||||
var raw = in.readLine();
|
var raw = in.readLine();
|
||||||
|
|
||||||
if (raw == null) break;
|
if (raw == null) break;
|
||||||
engine.pushMsg(false, new Context(env, new MessageContext(engine)), "<stdio>", raw, null).toObservable().once(valuePrinter);
|
engine.pushMsg(false, env.context(new Message(engine)), "<stdio>", raw, null).toObservable().once(valuePrinter);
|
||||||
}
|
}
|
||||||
catch (EngineException e) {
|
catch (EngineException e) {
|
||||||
try {
|
try {
|
||||||
|
@ -5,16 +5,23 @@ import me.topchetoeu.jscript.engine.values.Values;
|
|||||||
import me.topchetoeu.jscript.parsing.Parsing;
|
import me.topchetoeu.jscript.parsing.Parsing;
|
||||||
|
|
||||||
public class Context {
|
public class Context {
|
||||||
public final FunctionContext function;
|
public final Environment env;
|
||||||
public final MessageContext message;
|
public final Message message;
|
||||||
|
|
||||||
public FunctionValue compile(String filename, String raw) throws InterruptedException {
|
public FunctionValue compile(String filename, String raw) throws InterruptedException {
|
||||||
var res = Values.toString(this, function.compile.call(this, null, raw, filename));
|
var res = Values.toString(this, env.compile.call(this, null, raw, filename));
|
||||||
return Parsing.compile(function, filename, res);
|
return Parsing.compile(env, filename, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Context(FunctionContext funcCtx, MessageContext msgCtx) {
|
public Context setEnv(Environment env) {
|
||||||
this.function = funcCtx;
|
return new Context(env, message);
|
||||||
this.message = msgCtx;
|
}
|
||||||
|
public Context setMsg(Message msg) {
|
||||||
|
return new Context(env, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context(Environment env, Message msg) {
|
||||||
|
this.env = env;
|
||||||
|
this.message = msg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
60
src/me/topchetoeu/jscript/engine/Data.java
Normal file
60
src/me/topchetoeu/jscript/engine/Data.java
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package me.topchetoeu.jscript.engine;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public class Data implements Iterable<Entry<DataKey<?>, ?>> {
|
||||||
|
private HashMap<DataKey<Object>, Object> data = new HashMap<>();
|
||||||
|
|
||||||
|
public Data copy() {
|
||||||
|
return new Data().addAll(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Data addAll(Iterable<Entry<DataKey<?>, ?>> data) {
|
||||||
|
for (var el : data) {
|
||||||
|
add((DataKey<Object>)el.getKey(), (Object)el.getValue());
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Data set(DataKey<T> key, T val) {
|
||||||
|
if (val == null) data.remove(key);
|
||||||
|
else data.put((DataKey<Object>)key, (Object)val);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public <T> T add(DataKey<T> key, T val) {
|
||||||
|
if (data.containsKey(key)) return (T)data.get(key);
|
||||||
|
else {
|
||||||
|
if (val == null) data.remove(key);
|
||||||
|
else data.put((DataKey<Object>)key, (Object)val);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public <T> T get(DataKey<T> key) {
|
||||||
|
return get(key, null);
|
||||||
|
}
|
||||||
|
public <T> T get(DataKey<T> key, T defaultVal) {
|
||||||
|
if (!has(key)) return defaultVal;
|
||||||
|
else return (T)data.get(key);
|
||||||
|
}
|
||||||
|
public boolean has(DataKey<?> key) { return data.containsKey(key); }
|
||||||
|
|
||||||
|
public int increase(DataKey<Integer> key, int n, int start) {
|
||||||
|
int res;
|
||||||
|
set(key, res = get(key, start) + n);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
public int increase(DataKey<Integer> key, int n) {
|
||||||
|
return increase(key, n, 0);
|
||||||
|
}
|
||||||
|
public int increase(DataKey<Integer> key) {
|
||||||
|
return increase(key, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Entry<DataKey<?>, ?>> iterator() {
|
||||||
|
return (Iterator<Entry<DataKey<?>, ?>>)data.entrySet();
|
||||||
|
}
|
||||||
|
}
|
3
src/me/topchetoeu/jscript/engine/DataKey.java
Normal file
3
src/me/topchetoeu/jscript/engine/DataKey.java
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package me.topchetoeu.jscript.engine;
|
||||||
|
|
||||||
|
public class DataKey<T> { }
|
@ -11,19 +11,19 @@ public class Engine {
|
|||||||
private class UncompiledFunction extends FunctionValue {
|
private class UncompiledFunction extends FunctionValue {
|
||||||
public final String filename;
|
public final String filename;
|
||||||
public final String raw;
|
public final String raw;
|
||||||
public final FunctionContext ctx;
|
public final Environment env;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
|
public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
|
||||||
ctx = new Context(this.ctx, ctx.message);
|
ctx = ctx.setEnv(env);
|
||||||
return ctx.compile(filename, raw).call(ctx, thisArg, args);
|
return ctx.compile(filename, raw).call(ctx, thisArg, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UncompiledFunction(FunctionContext ctx, String filename, String raw) {
|
public UncompiledFunction(Environment env, String filename, String raw) {
|
||||||
super(filename, 0);
|
super(filename, 0);
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
this.raw = raw;
|
this.raw = raw;
|
||||||
this.ctx = ctx;
|
this.env = env;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,10 +32,10 @@ public class Engine {
|
|||||||
public final Object thisArg;
|
public final Object thisArg;
|
||||||
public final Object[] args;
|
public final Object[] args;
|
||||||
public final DataNotifier<Object> notifier = new DataNotifier<>();
|
public final DataNotifier<Object> notifier = new DataNotifier<>();
|
||||||
public final MessageContext ctx;
|
public final Message msg;
|
||||||
|
|
||||||
public Task(MessageContext ctx, FunctionValue func, Object thisArg, Object[] args) {
|
public Task(Message ctx, FunctionValue func, Object thisArg, Object[] args) {
|
||||||
this.ctx = ctx;
|
this.msg = ctx;
|
||||||
this.func = func;
|
this.func = func;
|
||||||
this.thisArg = thisArg;
|
this.thisArg = thisArg;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
@ -52,7 +52,7 @@ public class Engine {
|
|||||||
|
|
||||||
private void runTask(Task task) throws InterruptedException {
|
private void runTask(Task task) throws InterruptedException {
|
||||||
try {
|
try {
|
||||||
task.notifier.next(task.func.call(new Context(null, task.ctx), task.thisArg, task.args));
|
task.notifier.next(task.func.call(task.msg.context(null), task.thisArg, task.args));
|
||||||
}
|
}
|
||||||
catch (InterruptedException e) {
|
catch (InterruptedException e) {
|
||||||
task.notifier.error(new RuntimeException(e));
|
task.notifier.error(new RuntimeException(e));
|
||||||
@ -102,14 +102,14 @@ public class Engine {
|
|||||||
return this.thread != null;
|
return this.thread != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Awaitable<Object> pushMsg(boolean micro, MessageContext ctx, FunctionValue func, Object thisArg, Object ...args) {
|
public Awaitable<Object> pushMsg(boolean micro, Message ctx, FunctionValue func, Object thisArg, Object ...args) {
|
||||||
var msg = new Task(ctx, func, thisArg, args);
|
var msg = new Task(ctx, func, thisArg, args);
|
||||||
if (micro) microTasks.addLast(msg);
|
if (micro) microTasks.addLast(msg);
|
||||||
else macroTasks.addLast(msg);
|
else macroTasks.addLast(msg);
|
||||||
return msg.notifier;
|
return msg.notifier;
|
||||||
}
|
}
|
||||||
public Awaitable<Object> pushMsg(boolean micro, Context ctx, String filename, String raw, Object thisArg, Object ...args) {
|
public Awaitable<Object> pushMsg(boolean micro, Context ctx, String filename, String raw, Object thisArg, Object ...args) {
|
||||||
return pushMsg(micro, ctx.message, new UncompiledFunction(ctx.function, filename, raw), thisArg, args);
|
return pushMsg(micro, ctx.message, new UncompiledFunction(ctx.env, filename, raw), thisArg, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
// public Engine() {
|
// public Engine() {
|
||||||
|
84
src/me/topchetoeu/jscript/engine/Environment.java
Normal file
84
src/me/topchetoeu/jscript/engine/Environment.java
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
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.engine.values.Symbol;
|
||||||
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeSetter;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeWrapperProvider;
|
||||||
|
|
||||||
|
public class Environment {
|
||||||
|
private HashMap<String, ObjectValue> prototypes = new HashMap<>();
|
||||||
|
|
||||||
|
public final Data data = new Data();
|
||||||
|
public final HashMap<String, Symbol> symbols = 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.").setContext(ctx);
|
||||||
|
});
|
||||||
|
|
||||||
|
public Environment addData(Data data) {
|
||||||
|
this.data.addAll(data);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public ObjectValue proto(String name) {
|
||||||
|
return prototypes.get(name);
|
||||||
|
}
|
||||||
|
@Native public void setProto(String name, ObjectValue val) {
|
||||||
|
prototypes.put(name, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public Symbol symbol(String name) {
|
||||||
|
if (symbols.containsKey(name))
|
||||||
|
return symbols.get(name);
|
||||||
|
else {
|
||||||
|
var res = new Symbol(name);
|
||||||
|
symbols.put(name, res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 Context context(Message msg) {
|
||||||
|
return new Context(this, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 NativeWrapperProvider(this);
|
||||||
|
if (global == null) global = new GlobalScope();
|
||||||
|
|
||||||
|
this.wrappersProvider = nativeConverter;
|
||||||
|
this.compile = compile;
|
||||||
|
this.global = global;
|
||||||
|
}
|
||||||
|
}
|
@ -1,81 +0,0 @@
|
|||||||
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 FunctionContext {
|
|
||||||
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 FunctionContext fork() {
|
|
||||||
var res = new FunctionContext(compile, wrappersProvider, global);
|
|
||||||
res.regexConstructor = regexConstructor;
|
|
||||||
res.prototypes = new HashMap<>(prototypes);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Native
|
|
||||||
public FunctionContext child() {
|
|
||||||
var res = fork();
|
|
||||||
res.global = res.global.globalChild();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FunctionContext(FunctionValue compile, WrappersProvider nativeConverter, GlobalScope global) {
|
|
||||||
if (compile == null) compile = new NativeFunction("compile", (ctx, thisArg, args) -> args.length == 0 ? "" : args[0]);
|
|
||||||
if (nativeConverter == null) nativeConverter = new WrappersProvider() {
|
|
||||||
public ObjectValue getConstr(Class<?> obj) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,15 +7,22 @@ import java.util.List;
|
|||||||
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
|
||||||
public class MessageContext {
|
public class Message {
|
||||||
public final Engine engine;
|
public final Engine engine;
|
||||||
|
|
||||||
private final ArrayList<CodeFrame> frames = new ArrayList<>();
|
private final ArrayList<CodeFrame> frames = new ArrayList<>();
|
||||||
public int maxStackFrames = 1000;
|
public int maxStackFrames = 1000;
|
||||||
|
|
||||||
|
public final Data data = new Data();
|
||||||
|
|
||||||
public List<CodeFrame> frames() { return Collections.unmodifiableList(frames); }
|
public List<CodeFrame> frames() { return Collections.unmodifiableList(frames); }
|
||||||
|
|
||||||
public MessageContext pushFrame(CodeFrame frame) {
|
public Message addData(Data data) {
|
||||||
|
this.data.addAll(data);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Message pushFrame(Context ctx, CodeFrame frame) throws InterruptedException {
|
||||||
this.frames.add(frame);
|
this.frames.add(frame);
|
||||||
if (this.frames.size() > maxStackFrames) throw EngineException.ofRange("Stack overflow!");
|
if (this.frames.size() > maxStackFrames) throw EngineException.ofRange("Stack overflow!");
|
||||||
return this;
|
return this;
|
||||||
@ -27,7 +34,30 @@ public class MessageContext {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MessageContext(Engine engine) {
|
public List<String> stackTrace() {
|
||||||
|
var res = new ArrayList<String>();
|
||||||
|
|
||||||
|
for (var el : frames) {
|
||||||
|
var name = el.function.name;
|
||||||
|
var loc = el.function.loc();
|
||||||
|
var trace = "";
|
||||||
|
|
||||||
|
if (loc != null) trace += "at " + loc.toString() + " ";
|
||||||
|
if (name != null && !name.equals("")) trace += "in " + name + " ";
|
||||||
|
|
||||||
|
trace = trace.trim();
|
||||||
|
|
||||||
|
if (!res.equals("")) res.add(trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context context(Environment env) {
|
||||||
|
return new Context(env, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Message(Engine engine) {
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,8 +1,9 @@
|
|||||||
package me.topchetoeu.jscript.engine;
|
package me.topchetoeu.jscript.engine;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
|
|
||||||
public interface WrappersProvider {
|
public interface WrappersProvider {
|
||||||
public ObjectValue getProto(Class<?> obj);
|
public ObjectValue getProto(Class<?> obj);
|
||||||
public ObjectValue getConstr(Class<?> obj);
|
public FunctionValue getConstr(Class<?> obj);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package me.topchetoeu.jscript.engine.frame;
|
package me.topchetoeu.jscript.engine.frame;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.Stack;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
@ -9,6 +8,7 @@ import me.topchetoeu.jscript.engine.scope.LocalScope;
|
|||||||
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
|
||||||
@ -24,8 +24,8 @@ public class CodeFrame {
|
|||||||
public final int tryStart, catchStart, finallyStart, end;
|
public final int tryStart, catchStart, finallyStart, end;
|
||||||
public int state;
|
public int state;
|
||||||
public Object retVal;
|
public Object retVal;
|
||||||
public int jumpPtr;
|
|
||||||
public EngineException err;
|
public EngineException err;
|
||||||
|
public int jumpPtr;
|
||||||
|
|
||||||
public TryCtx(int tryStart, int tryN, int catchN, int finallyN) {
|
public TryCtx(int tryStart, int tryN, int catchN, int finallyN) {
|
||||||
hasCatch = catchN >= 0;
|
hasCatch = catchN >= 0;
|
||||||
@ -45,7 +45,7 @@ public class CodeFrame {
|
|||||||
public final LocalScope scope;
|
public final LocalScope scope;
|
||||||
public final Object thisArg;
|
public final Object thisArg;
|
||||||
public final Object[] args;
|
public final Object[] args;
|
||||||
public final List<TryCtx> tryStack = new ArrayList<>();
|
public final Stack<TryCtx> tryStack = new Stack<>();
|
||||||
public final CodeFunction function;
|
public final CodeFunction function;
|
||||||
|
|
||||||
public Object[] stack = new Object[32];
|
public Object[] stack = new Object[32];
|
||||||
@ -93,6 +93,12 @@ public class CodeFrame {
|
|||||||
stack[stackPtr++] = Values.normalize(ctx, val);
|
stack[stackPtr++] = Values.normalize(ctx, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setCause(Context ctx, EngineException err, EngineException cause) throws InterruptedException {
|
||||||
|
if (err.value instanceof ObjectValue) {
|
||||||
|
Values.setMember(ctx, err, ctx.env.symbol("Symbol.cause"), cause);
|
||||||
|
}
|
||||||
|
err.cause = cause;
|
||||||
|
}
|
||||||
private Object nextNoTry(Context ctx) throws InterruptedException {
|
private Object nextNoTry(Context ctx) throws InterruptedException {
|
||||||
if (Thread.currentThread().isInterrupted()) throw new InterruptedException();
|
if (Thread.currentThread().isInterrupted()) throw new InterruptedException();
|
||||||
if (codePtr < 0 || codePtr >= function.body.length) return null;
|
if (codePtr < 0 || codePtr >= function.body.length) return null;
|
||||||
@ -107,163 +113,125 @@ public class CodeFrame {
|
|||||||
return Runners.exec(ctx, instr, this);
|
return Runners.exec(ctx, instr, this);
|
||||||
}
|
}
|
||||||
catch (EngineException e) {
|
catch (EngineException e) {
|
||||||
throw e.add(function.name, prevLoc);
|
throw e.add(function.name, prevLoc).setContext(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object next(Context ctx, Object prevReturn, Object prevError) throws InterruptedException {
|
public Object next(Context ctx, Object value, Object returnValue, EngineException error) throws InterruptedException {
|
||||||
TryCtx tryCtx = null;
|
if (value != Runners.NO_RETURN) push(ctx, value);
|
||||||
if (prevError != Runners.NO_RETURN) prevReturn = Runners.NO_RETURN;
|
|
||||||
|
|
||||||
while (!tryStack.isEmpty()) {
|
if (returnValue == Runners.NO_RETURN && error == null) {
|
||||||
var tmp = tryStack.get(tryStack.size() - 1);
|
try { returnValue = nextNoTry(ctx); }
|
||||||
var remove = false;
|
catch (EngineException e) { error = e; }
|
||||||
|
|
||||||
if (prevError != Runners.NO_RETURN) {
|
|
||||||
remove = true;
|
|
||||||
if (tmp.state == TryCtx.STATE_TRY) {
|
|
||||||
tmp.jumpPtr = tmp.end;
|
|
||||||
|
|
||||||
if (tmp.hasCatch) {
|
|
||||||
tmp.state = TryCtx.STATE_CATCH;
|
|
||||||
scope.catchVars.add(new ValueVariable(false, prevError));
|
|
||||||
prevError = Runners.NO_RETURN;
|
|
||||||
codePtr = tmp.catchStart;
|
|
||||||
remove = false;
|
|
||||||
}
|
|
||||||
else if (tmp.hasFinally) {
|
|
||||||
tmp.state = TryCtx.STATE_FINALLY_THREW;
|
|
||||||
tmp.err = new EngineException(prevError);
|
|
||||||
prevError = Runners.NO_RETURN;
|
|
||||||
codePtr = tmp.finallyStart;
|
|
||||||
remove = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (prevReturn != Runners.NO_RETURN) {
|
|
||||||
remove = true;
|
|
||||||
if (tmp.hasFinally && tmp.state <= TryCtx.STATE_CATCH) {
|
|
||||||
tmp.state = TryCtx.STATE_FINALLY_RETURNED;
|
|
||||||
tmp.retVal = prevReturn;
|
|
||||||
prevReturn = Runners.NO_RETURN;
|
|
||||||
codePtr = tmp.finallyStart;
|
|
||||||
remove = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (tmp.state == TryCtx.STATE_TRY) {
|
|
||||||
if (codePtr < tmp.tryStart || codePtr >= tmp.catchStart) {
|
|
||||||
if (jumpFlag) tmp.jumpPtr = codePtr;
|
|
||||||
else tmp.jumpPtr = tmp.end;
|
|
||||||
|
|
||||||
if (tmp.hasFinally) {
|
|
||||||
tmp.state = TryCtx.STATE_FINALLY_JUMPED;
|
|
||||||
codePtr = tmp.finallyStart;
|
|
||||||
}
|
|
||||||
else codePtr = tmp.jumpPtr;
|
|
||||||
remove = !tmp.hasFinally;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (tmp.state == TryCtx.STATE_CATCH) {
|
|
||||||
if (codePtr < tmp.catchStart || codePtr >= tmp.finallyStart) {
|
|
||||||
if (jumpFlag) tmp.jumpPtr = codePtr;
|
|
||||||
else tmp.jumpPtr = tmp.end;
|
|
||||||
scope.catchVars.remove(scope.catchVars.size() - 1);
|
|
||||||
|
|
||||||
if (tmp.hasFinally) {
|
|
||||||
tmp.state = TryCtx.STATE_FINALLY_JUMPED;
|
|
||||||
codePtr = tmp.finallyStart;
|
|
||||||
}
|
|
||||||
else codePtr = tmp.jumpPtr;
|
|
||||||
remove = !tmp.hasFinally;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (codePtr < tmp.finallyStart || codePtr >= tmp.end) {
|
|
||||||
if (!jumpFlag) {
|
|
||||||
if (tmp.state == TryCtx.STATE_FINALLY_THREW) throw tmp.err;
|
|
||||||
else if (tmp.state == TryCtx.STATE_FINALLY_RETURNED) return tmp.retVal;
|
|
||||||
else if (tmp.state == TryCtx.STATE_FINALLY_JUMPED) codePtr = tmp.jumpPtr;
|
|
||||||
}
|
|
||||||
else codePtr = tmp.jumpPtr;
|
|
||||||
remove = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remove) tryStack.remove(tryStack.size() - 1);
|
while (!tryStack.empty()) {
|
||||||
else {
|
var tryCtx = tryStack.peek();
|
||||||
tryCtx = tmp;
|
var newState = -1;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevError != Runners.NO_RETURN) throw new EngineException(prevError);
|
switch (tryCtx.state) {
|
||||||
if (prevReturn != Runners.NO_RETURN) return prevReturn;
|
case TryCtx.STATE_TRY:
|
||||||
|
if (error != null) {
|
||||||
if (tryCtx == null) return nextNoTry(ctx);
|
|
||||||
else if (tryCtx.state == TryCtx.STATE_TRY) {
|
|
||||||
try {
|
|
||||||
var res = nextNoTry(ctx);
|
|
||||||
if (res != Runners.NO_RETURN && tryCtx.hasFinally) {
|
|
||||||
tryCtx.retVal = res;
|
|
||||||
tryCtx.state = TryCtx.STATE_FINALLY_RETURNED;
|
|
||||||
}
|
|
||||||
|
|
||||||
else return res;
|
|
||||||
}
|
|
||||||
catch (EngineException e) {
|
|
||||||
if (tryCtx.hasCatch) {
|
if (tryCtx.hasCatch) {
|
||||||
tryCtx.state = TryCtx.STATE_CATCH;
|
tryCtx.err = error;
|
||||||
tryCtx.err = e;
|
newState = TryCtx.STATE_CATCH;
|
||||||
codePtr = tryCtx.catchStart;
|
|
||||||
scope.catchVars.add(new ValueVariable(false, e.value));
|
|
||||||
return Runners.NO_RETURN;
|
|
||||||
}
|
}
|
||||||
else if (tryCtx.hasFinally) {
|
else if (tryCtx.hasFinally) {
|
||||||
tryCtx.err = e;
|
tryCtx.err = error;
|
||||||
tryCtx.state = TryCtx.STATE_FINALLY_THREW;
|
newState = TryCtx.STATE_FINALLY_THREW;
|
||||||
}
|
}
|
||||||
else throw e;
|
break;
|
||||||
}
|
}
|
||||||
|
else if (returnValue != Runners.NO_RETURN) {
|
||||||
codePtr = tryCtx.finallyStart;
|
|
||||||
return Runners.NO_RETURN;
|
|
||||||
}
|
|
||||||
else if (tryCtx.state == TryCtx.STATE_CATCH) {
|
|
||||||
try {
|
|
||||||
var res = nextNoTry(ctx);
|
|
||||||
if (res != Runners.NO_RETURN && tryCtx.hasFinally) {
|
|
||||||
tryCtx.retVal = res;
|
|
||||||
tryCtx.state = TryCtx.STATE_FINALLY_RETURNED;
|
|
||||||
}
|
|
||||||
else return res;
|
|
||||||
}
|
|
||||||
catch (EngineException e) {
|
|
||||||
e.cause = tryCtx.err;
|
|
||||||
if (tryCtx.hasFinally) {
|
if (tryCtx.hasFinally) {
|
||||||
tryCtx.err = e;
|
tryCtx.retVal = error;
|
||||||
tryCtx.state = TryCtx.STATE_FINALLY_THREW;
|
newState = TryCtx.STATE_FINALLY_RETURNED;
|
||||||
}
|
}
|
||||||
else throw e;
|
break;
|
||||||
|
}
|
||||||
|
else if (codePtr >= tryCtx.tryStart && codePtr < tryCtx.catchStart) return Runners.NO_RETURN;
|
||||||
|
|
||||||
|
if (tryCtx.hasFinally) {
|
||||||
|
if (jumpFlag) tryCtx.jumpPtr = codePtr;
|
||||||
|
else tryCtx.jumpPtr = tryCtx.end;
|
||||||
|
newState = TryCtx.STATE_FINALLY_JUMPED;
|
||||||
|
}
|
||||||
|
else codePtr = tryCtx.end;
|
||||||
|
break;
|
||||||
|
case TryCtx.STATE_CATCH:
|
||||||
|
if (error != null) {
|
||||||
|
if (tryCtx.hasFinally) {
|
||||||
|
tryCtx.err = error;
|
||||||
|
newState = TryCtx.STATE_FINALLY_THREW;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (returnValue != Runners.NO_RETURN) {
|
||||||
|
if (tryCtx.hasFinally) {
|
||||||
|
tryCtx.retVal = returnValue;
|
||||||
|
newState = TryCtx.STATE_FINALLY_RETURNED;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (codePtr >= tryCtx.catchStart && codePtr < tryCtx.finallyStart) return Runners.NO_RETURN;
|
||||||
|
|
||||||
|
if (tryCtx.hasFinally) {
|
||||||
|
if (jumpFlag) tryCtx.jumpPtr = codePtr;
|
||||||
|
else tryCtx.jumpPtr = tryCtx.end;
|
||||||
|
newState = TryCtx.STATE_FINALLY_JUMPED;
|
||||||
|
}
|
||||||
|
else codePtr = tryCtx.end;
|
||||||
|
break;
|
||||||
|
case TryCtx.STATE_FINALLY_THREW:
|
||||||
|
if (error != null) setCause(ctx, error, tryCtx.err);
|
||||||
|
else if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) error = tryCtx.err;
|
||||||
|
else return Runners.NO_RETURN;
|
||||||
|
break;
|
||||||
|
case TryCtx.STATE_FINALLY_RETURNED:
|
||||||
|
if (returnValue == Runners.NO_RETURN) {
|
||||||
|
if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) returnValue = tryCtx.retVal;
|
||||||
|
else return Runners.NO_RETURN;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TryCtx.STATE_FINALLY_JUMPED:
|
||||||
|
if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) {
|
||||||
|
if (!jumpFlag) codePtr = tryCtx.jumpPtr;
|
||||||
|
else codePtr = tryCtx.end;
|
||||||
|
}
|
||||||
|
else return Runners.NO_RETURN;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tryCtx.state == TryCtx.STATE_CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
|
||||||
|
|
||||||
|
if (newState == -1) {
|
||||||
|
tryStack.pop();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
tryCtx.state = newState;
|
||||||
|
switch (newState) {
|
||||||
|
case TryCtx.STATE_CATCH:
|
||||||
|
scope.catchVars.add(new ValueVariable(false, tryCtx.err.value));
|
||||||
|
codePtr = tryCtx.catchStart;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
codePtr = tryCtx.finallyStart;
|
codePtr = tryCtx.finallyStart;
|
||||||
|
}
|
||||||
|
|
||||||
return Runners.NO_RETURN;
|
return Runners.NO_RETURN;
|
||||||
}
|
}
|
||||||
else if (tryCtx.state == TryCtx.STATE_FINALLY_THREW) {
|
|
||||||
try {
|
if (error != null) throw error.setContext(ctx);
|
||||||
return nextNoTry(ctx);
|
if (returnValue != Runners.NO_RETURN) return returnValue;
|
||||||
}
|
return Runners.NO_RETURN;
|
||||||
catch (EngineException e) {
|
|
||||||
e.cause = tryCtx.err;
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else return nextNoTry(ctx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object run(Context ctx) throws InterruptedException {
|
public Object run(Context ctx) throws InterruptedException {
|
||||||
try {
|
try {
|
||||||
ctx.message.pushFrame(this);
|
ctx.message.pushFrame(ctx, this);
|
||||||
while (true) {
|
while (true) {
|
||||||
var res = next(ctx, Runners.NO_RETURN, Runners.NO_RETURN);
|
var res = next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, null);
|
||||||
if (res != Runners.NO_RETURN) return res;
|
if (res != Runners.NO_RETURN) return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ public class Runners {
|
|||||||
public static Object execThrow(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execThrow(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
throw new EngineException(frame.pop());
|
throw new EngineException(frame.pop());
|
||||||
}
|
}
|
||||||
public static Object execThrowSyntax(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execThrowSyntax(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
|
||||||
throw EngineException.ofSyntax((String)instr.get(0));
|
throw EngineException.ofSyntax((String)instr.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,16 +48,18 @@ public class Runners {
|
|||||||
var callArgs = frame.take(instr.get(0));
|
var callArgs = frame.take(instr.get(0));
|
||||||
var funcObj = frame.pop();
|
var funcObj = frame.pop();
|
||||||
|
|
||||||
if (Values.isFunction(funcObj) && Values.function(funcObj).special) {
|
frame.push(ctx, Values.callNew(ctx, funcObj, callArgs));
|
||||||
frame.push(ctx, call(ctx, funcObj, null, callArgs));
|
|
||||||
}
|
// if (Values.isFunction(funcObj) && Values.function(funcObj).special) {
|
||||||
else {
|
// frame.push(ctx, call(ctx, funcObj, null, callArgs));
|
||||||
var proto = Values.getMember(ctx, funcObj, "prototype");
|
// }
|
||||||
var obj = new ObjectValue();
|
// else {
|
||||||
obj.setPrototype(ctx, proto);
|
// var proto = Values.getMember(ctx, funcObj, "prototype");
|
||||||
call(ctx, funcObj, obj, callArgs);
|
// var obj = new ObjectValue();
|
||||||
frame.push(ctx, obj);
|
// obj.setPrototype(ctx, proto);
|
||||||
}
|
// call(ctx, funcObj, obj, callArgs);
|
||||||
|
// frame.push(ctx, obj);
|
||||||
|
// }
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
@ -65,7 +67,7 @@ public class Runners {
|
|||||||
|
|
||||||
public static Object execMakeVar(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
|
public static Object execMakeVar(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
|
||||||
var name = (String)instr.get(0);
|
var name = (String)instr.get(0);
|
||||||
ctx.function.global.define(name);
|
ctx.env.global.define(name);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
@ -160,7 +162,7 @@ public class Runners {
|
|||||||
public static Object execLoadVar(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
|
public static Object execLoadVar(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
|
||||||
var i = instr.get(0);
|
var i = instr.get(0);
|
||||||
|
|
||||||
if (i instanceof String) frame.push(ctx, ctx.function.global.get(ctx, (String)i));
|
if (i instanceof String) frame.push(ctx, ctx.env.global.get(ctx, (String)i));
|
||||||
else frame.push(ctx, frame.scope.get((int)i).get(ctx));
|
else frame.push(ctx, frame.scope.get((int)i).get(ctx));
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
@ -172,7 +174,7 @@ public class Runners {
|
|||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadGlob(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadGlob(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.push(ctx, ctx.function.global.obj);
|
frame.push(ctx, ctx.env.global.obj);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
@ -198,7 +200,7 @@ public class Runners {
|
|||||||
var body = new Instruction[end - start];
|
var body = new Instruction[end - start];
|
||||||
System.arraycopy(frame.function.body, start, body, 0, end - start);
|
System.arraycopy(frame.function.body, start, body, 0, end - start);
|
||||||
|
|
||||||
var func = new CodeFunction(ctx.function, "", localsN, len, captures, body);
|
var func = new CodeFunction(ctx.env, "", localsN, len, captures, body);
|
||||||
frame.push(ctx, func);
|
frame.push(ctx, func);
|
||||||
|
|
||||||
frame.codePtr += n;
|
frame.codePtr += n;
|
||||||
@ -222,7 +224,7 @@ public class Runners {
|
|||||||
return execLoadMember(ctx, instr, frame);
|
return execLoadMember(ctx, instr, frame);
|
||||||
}
|
}
|
||||||
public static Object execLoadRegEx(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
|
public static Object execLoadRegEx(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
|
||||||
frame.push(ctx, ctx.function.regexConstructor.call(ctx, null, instr.get(0), instr.get(1)));
|
frame.push(ctx, ctx.env.regexConstructor.call(ctx, null, instr.get(0), instr.get(1)));
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
@ -246,7 +248,7 @@ public class Runners {
|
|||||||
var val = (boolean)instr.get(1) ? frame.peek() : frame.pop();
|
var val = (boolean)instr.get(1) ? frame.peek() : frame.pop();
|
||||||
var i = instr.get(0);
|
var i = instr.get(0);
|
||||||
|
|
||||||
if (i instanceof String) ctx.function.global.set(ctx, (String)i, val);
|
if (i instanceof String) ctx.env.global.set(ctx, (String)i, val);
|
||||||
else frame.scope.get((int)i).set(ctx, val);
|
else frame.scope.get((int)i).set(ctx, val);
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
@ -293,8 +295,8 @@ public class Runners {
|
|||||||
Object obj;
|
Object obj;
|
||||||
|
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
if (ctx.function.global.has(ctx, name)) {
|
if (ctx.env.global.has(ctx, name)) {
|
||||||
obj = ctx.function.global.get(ctx, name);
|
obj = ctx.env.global.get(ctx, name);
|
||||||
}
|
}
|
||||||
else obj = null;
|
else obj = null;
|
||||||
}
|
}
|
||||||
@ -305,7 +307,7 @@ public class Runners {
|
|||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execNop(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execNop(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
|
||||||
if (instr.is(0, "dbg_names")) {
|
if (instr.is(0, "dbg_names")) {
|
||||||
var names = new String[instr.params.length - 1];
|
var names = new String[instr.params.length - 1];
|
||||||
for (var i = 0; i < instr.params.length - 1; i++) {
|
for (var i = 0; i < instr.params.length - 1; i++) {
|
||||||
|
@ -9,7 +9,8 @@ public class LocalScope {
|
|||||||
public final ArrayList<ValueVariable> catchVars = new ArrayList<>();
|
public final ArrayList<ValueVariable> catchVars = new ArrayList<>();
|
||||||
|
|
||||||
public ValueVariable get(int i) {
|
public ValueVariable get(int i) {
|
||||||
if (i >= locals.length) return catchVars.get(i - locals.length);
|
if (i >= locals.length)
|
||||||
|
return catchVars.get(i - locals.length);
|
||||||
if (i >= 0) return locals[i];
|
if (i >= 0) return locals[i];
|
||||||
else return captures[~i];
|
else return captures[~i];
|
||||||
}
|
}
|
||||||
|
@ -1,90 +1,128 @@
|
|||||||
package me.topchetoeu.jscript.engine.values;
|
package me.topchetoeu.jscript.engine.values;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
|
||||||
public class ArrayValue extends ObjectValue {
|
// TODO: Make methods generic
|
||||||
private static final Object EMPTY = new Object();
|
public class ArrayValue extends ObjectValue implements Iterable<Object> {
|
||||||
private final ArrayList<Object> values = new ArrayList<>();
|
private static final Object UNDEFINED = new Object();
|
||||||
|
private Object[] values;
|
||||||
|
private int size;
|
||||||
|
|
||||||
public int size() { return values.size(); }
|
private void alloc(int index) {
|
||||||
|
if (index < values.length) return;
|
||||||
|
if (index < values.length * 2) index = values.length * 2;
|
||||||
|
|
||||||
|
var arr = new Object[index];
|
||||||
|
System.arraycopy(values, 0, arr, 0, values.length);
|
||||||
|
values = arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() { return size; }
|
||||||
public boolean setSize(int val) {
|
public boolean setSize(int val) {
|
||||||
if (val < 0) return false;
|
if (val < 0) return false;
|
||||||
while (size() > val) {
|
if (size > val) shrink(size - val);
|
||||||
values.remove(values.size() - 1);
|
else {
|
||||||
}
|
alloc(val);
|
||||||
while (size() < val) {
|
size = val;
|
||||||
values.add(EMPTY);
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object get(int i) {
|
public Object get(int i) {
|
||||||
if (i < 0 || i >= values.size()) return null;
|
if (i < 0 || i >= size) return null;
|
||||||
var res = values.get(i);
|
var res = values[i];
|
||||||
if (res == EMPTY) return null;
|
if (res == UNDEFINED) return null;
|
||||||
else return res;
|
else return res;
|
||||||
}
|
}
|
||||||
public void set(Context ctx, int i, Object val) {
|
public void set(Context ctx, int i, Object val) {
|
||||||
if (i < 0) return;
|
if (i < 0) return;
|
||||||
|
|
||||||
while (values.size() <= i) {
|
alloc(i);
|
||||||
values.add(EMPTY);
|
|
||||||
}
|
|
||||||
|
|
||||||
values.set(i, Values.normalize(ctx, val));
|
val = Values.normalize(ctx, val);
|
||||||
|
if (val == null) val = UNDEFINED;
|
||||||
|
values[i] = val;
|
||||||
|
if (i >= size) size = i + 1;
|
||||||
}
|
}
|
||||||
public boolean has(int i) {
|
public boolean has(int i) {
|
||||||
return i >= 0 && i < values.size() && values.get(i) != EMPTY;
|
return i >= 0 && i < values.length && values[i] != null;
|
||||||
}
|
}
|
||||||
public void remove(int i) {
|
public void remove(int i) {
|
||||||
if (i < 0 || i >= values.size()) return;
|
if (i < 0 || i >= values.length) return;
|
||||||
values.set(i, EMPTY);
|
values[i] = null;
|
||||||
}
|
}
|
||||||
public void shrink(int n) {
|
public void shrink(int n) {
|
||||||
if (n > values.size()) values.clear();
|
if (n >= values.length) {
|
||||||
|
values = new Object[16];
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
for (int i = 0; i < n && values.size() > 0; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
values.remove(values.size() - 1);
|
values[--size] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object[] toArray() {
|
||||||
|
Object[] res = new Object[size];
|
||||||
|
copyTo(res, 0, 0, size);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
public void copyTo(Object[] arr, int sourceStart, int destStart, int count) {
|
||||||
|
for (var i = 0; i < count; i++) {
|
||||||
|
if (i + sourceStart < 0 || i + sourceStart >= size) arr[i + destStart] = null;
|
||||||
|
if (values[i + sourceStart] == UNDEFINED) arr[i + destStart] = null;
|
||||||
|
else arr[i + sourceStart] = values[i + destStart];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void copyTo(Context ctx, ArrayValue arr, int sourceStart, int destStart, int count) {
|
||||||
|
// Iterate in reverse to reallocate at most once
|
||||||
|
for (var i = count - 1; i >= 0; i--) {
|
||||||
|
if (i + sourceStart < 0 || i + sourceStart >= size) arr.set(ctx, i + destStart, null);
|
||||||
|
if (values[i + sourceStart] == UNDEFINED) arr.set(ctx, i + destStart, null);
|
||||||
|
else arr.set(ctx, i + destStart, values[i + sourceStart]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copyFrom(Context ctx, Object[] arr, int sourceStart, int destStart, int count) {
|
||||||
|
for (var i = 0; i < count; i++) {
|
||||||
|
set(ctx, i + destStart, arr[i + sourceStart]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void move(int srcI, int dstI, int n) {
|
||||||
|
alloc(dstI + n);
|
||||||
|
|
||||||
|
System.arraycopy(values, srcI, values, dstI, n);
|
||||||
|
|
||||||
|
if (dstI + n >= size) size = dstI + n;
|
||||||
|
}
|
||||||
|
|
||||||
public void sort(Comparator<Object> comparator) {
|
public void sort(Comparator<Object> comparator) {
|
||||||
values.sort((a, b) -> {
|
Arrays.sort(values, 0, size, (a, b) -> {
|
||||||
var _a = 0;
|
var _a = 0;
|
||||||
var _b = 0;
|
var _b = 0;
|
||||||
|
|
||||||
if (a == null) _a = 1;
|
if (a == UNDEFINED) _a = 1;
|
||||||
if (a == EMPTY) _a = 2;
|
if (a == null) _a = 2;
|
||||||
|
|
||||||
if (b == null) _b = 1;
|
if (b == UNDEFINED) _b = 1;
|
||||||
if (b == EMPTY) _b = 2;
|
if (b == null) _b = 2;
|
||||||
|
|
||||||
if (Integer.compare(_a, _b) != 0) return Integer.compare(_a, _b);
|
if (_a != 0 || _b != 0) return Integer.compare(_a, _b);
|
||||||
|
|
||||||
return comparator.compare(a, b);
|
return comparator.compare(a, b);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object[] toArray() {
|
|
||||||
Object[] res = new Object[values.size()];
|
|
||||||
|
|
||||||
for (var i = 0; i < values.size(); i++) {
|
|
||||||
if (values.get(i) == EMPTY) res[i] = null;
|
|
||||||
else res[i] = values.get(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object getField(Context ctx, Object key) throws InterruptedException {
|
protected Object getField(Context ctx, Object key) throws InterruptedException {
|
||||||
if (key.equals("length")) return values.size();
|
|
||||||
if (key instanceof Number) {
|
if (key instanceof Number) {
|
||||||
var i = ((Number)key).doubleValue();
|
var i = ((Number)key).doubleValue();
|
||||||
if (i >= 0 && i - Math.floor(i) == 0) {
|
if (i >= 0 && i - Math.floor(i) == 0) {
|
||||||
@ -96,9 +134,6 @@ public class ArrayValue extends ObjectValue {
|
|||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected boolean setField(Context ctx, Object key, Object val) throws InterruptedException {
|
protected boolean setField(Context ctx, Object key, Object val) throws InterruptedException {
|
||||||
if (key.equals("length")) {
|
|
||||||
return setSize((int)Values.toNumber(ctx, val));
|
|
||||||
}
|
|
||||||
if (key instanceof Number) {
|
if (key instanceof Number) {
|
||||||
var i = Values.number(key);
|
var i = Values.number(key);
|
||||||
if (i >= 0 && i - Math.floor(i) == 0) {
|
if (i >= 0 && i - Math.floor(i) == 0) {
|
||||||
@ -111,7 +146,6 @@ public class ArrayValue extends ObjectValue {
|
|||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected boolean hasField(Context ctx, Object key) throws InterruptedException {
|
protected boolean hasField(Context ctx, Object key) throws InterruptedException {
|
||||||
if (key.equals("length")) return true;
|
|
||||||
if (key instanceof Number) {
|
if (key instanceof Number) {
|
||||||
var i = Values.number(key);
|
var i = Values.number(key);
|
||||||
if (i >= 0 && i - Math.floor(i) == 0) {
|
if (i >= 0 && i - Math.floor(i) == 0) {
|
||||||
@ -140,18 +174,42 @@ public class ArrayValue extends ObjectValue {
|
|||||||
for (var i = 0; i < size(); i++) {
|
for (var i = 0; i < size(); i++) {
|
||||||
if (has(i)) res.add(i);
|
if (has(i)) res.add(i);
|
||||||
}
|
}
|
||||||
if (includeNonEnumerable) res.add("length");
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Object> iterator() {
|
||||||
|
return new Iterator<Object>() {
|
||||||
|
private int i = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return i < size();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Object next() {
|
||||||
|
if (!hasNext()) return null;
|
||||||
|
return get(i++);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public ArrayValue() {
|
public ArrayValue() {
|
||||||
super(PlaceholderProto.ARRAY);
|
super(PlaceholderProto.ARRAY);
|
||||||
nonEnumerableSet.add("length");
|
values = new Object[16];
|
||||||
nonConfigurableSet.add("length");
|
size = 0;
|
||||||
|
}
|
||||||
|
public ArrayValue(int cap) {
|
||||||
|
super(PlaceholderProto.ARRAY);
|
||||||
|
values = new Object[cap];
|
||||||
|
size = 0;
|
||||||
}
|
}
|
||||||
public ArrayValue(Context ctx, Object ...values) {
|
public ArrayValue(Context ctx, Object ...values) {
|
||||||
this();
|
this();
|
||||||
for (var i = 0; i < values.length; i++) this.values.add(Values.normalize(ctx, values[i]));
|
this.values = new Object[values.length];
|
||||||
|
size = values.length;
|
||||||
|
|
||||||
|
for (var i = 0; i < size; i++) this.values[i] = Values.normalize(ctx, values[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ArrayValue of(Context ctx, Collection<Object> values) {
|
public static ArrayValue of(Context ctx, Collection<Object> values) {
|
||||||
|
@ -3,7 +3,7 @@ package me.topchetoeu.jscript.engine.values;
|
|||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.FunctionContext;
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ public class CodeFunction extends FunctionValue {
|
|||||||
public final int length;
|
public final int length;
|
||||||
public final Instruction[] body;
|
public final Instruction[] body;
|
||||||
public final ValueVariable[] captures;
|
public final ValueVariable[] captures;
|
||||||
public FunctionContext environment;
|
public Environment environment;
|
||||||
|
|
||||||
public Location loc() {
|
public Location loc() {
|
||||||
for (var instr : body) {
|
for (var instr : body) {
|
||||||
@ -29,10 +29,10 @@ public class CodeFunction extends FunctionValue {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
|
public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
|
||||||
return new CodeFrame(ctx, thisArg, args, this).run(new Context(environment, ctx.message));
|
return new CodeFrame(ctx, thisArg, args, this).run(ctx.setEnv(environment));
|
||||||
}
|
}
|
||||||
|
|
||||||
public CodeFunction(FunctionContext environment, String name, int localsN, int length, ValueVariable[] captures, Instruction[] body) {
|
public CodeFunction(Environment environment, String name, int localsN, int length, ValueVariable[] captures, Instruction[] body) {
|
||||||
super(name, length);
|
super(name, length);
|
||||||
this.captures = captures;
|
this.captures = captures;
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
|
@ -8,7 +8,8 @@ public class NativeWrapper extends ObjectValue {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ObjectValue getPrototype(Context ctx) throws InterruptedException {
|
public ObjectValue getPrototype(Context ctx) throws InterruptedException {
|
||||||
if (prototype == NATIVE_PROTO) return ctx.function.wrappersProvider.getProto(wrapped.getClass());
|
if (prototype == NATIVE_PROTO)
|
||||||
|
return ctx.env.wrappersProvider.getProto(wrapped.getClass());
|
||||||
else return super.getPrototype(ctx);
|
else return super.getPrototype(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ public class ObjectValue {
|
|||||||
|
|
||||||
public final boolean memberWritable(Object key) {
|
public final boolean memberWritable(Object key) {
|
||||||
if (state == State.FROZEN) return false;
|
if (state == State.FROZEN) return false;
|
||||||
return !nonWritableSet.contains(key);
|
return !values.containsKey(key) || !nonWritableSet.contains(key);
|
||||||
}
|
}
|
||||||
public final boolean memberConfigurable(Object key) {
|
public final boolean memberConfigurable(Object key) {
|
||||||
if (state == State.SEALED || state == State.FROZEN) return false;
|
if (state == State.SEALED || state == State.FROZEN) return false;
|
||||||
@ -147,13 +147,13 @@ public class ObjectValue {
|
|||||||
|
|
||||||
public ObjectValue getPrototype(Context ctx) throws InterruptedException {
|
public ObjectValue getPrototype(Context ctx) throws InterruptedException {
|
||||||
try {
|
try {
|
||||||
if (prototype == OBJ_PROTO) return ctx.function.proto("object");
|
if (prototype == OBJ_PROTO) return ctx.env.proto("object");
|
||||||
if (prototype == ARR_PROTO) return ctx.function.proto("array");
|
if (prototype == ARR_PROTO) return ctx.env.proto("array");
|
||||||
if (prototype == FUNC_PROTO) return ctx.function.proto("function");
|
if (prototype == FUNC_PROTO) return ctx.env.proto("function");
|
||||||
if (prototype == ERR_PROTO) return ctx.function.proto("error");
|
if (prototype == ERR_PROTO) return ctx.env.proto("error");
|
||||||
if (prototype == RANGE_ERR_PROTO) return ctx.function.proto("rangeErr");
|
if (prototype == RANGE_ERR_PROTO) return ctx.env.proto("rangeErr");
|
||||||
if (prototype == SYNTAX_ERR_PROTO) return ctx.function.proto("syntaxErr");
|
if (prototype == SYNTAX_ERR_PROTO) return ctx.env.proto("syntaxErr");
|
||||||
if (prototype == TYPE_ERR_PROTO) return ctx.function.proto("typeErr");
|
if (prototype == TYPE_ERR_PROTO) return ctx.env.proto("typeErr");
|
||||||
}
|
}
|
||||||
catch (NullPointerException e) {
|
catch (NullPointerException e) {
|
||||||
return null;
|
return null;
|
||||||
@ -172,14 +172,14 @@ public class ObjectValue {
|
|||||||
else if (Values.isObject(val)) {
|
else if (Values.isObject(val)) {
|
||||||
var obj = Values.object(val);
|
var obj = Values.object(val);
|
||||||
|
|
||||||
if (ctx != null && ctx.function != null) {
|
if (ctx != null && ctx.env != null) {
|
||||||
if (obj == ctx.function.proto("object")) prototype = OBJ_PROTO;
|
if (obj == ctx.env.proto("object")) prototype = OBJ_PROTO;
|
||||||
else if (obj == ctx.function.proto("array")) prototype = ARR_PROTO;
|
else if (obj == ctx.env.proto("array")) prototype = ARR_PROTO;
|
||||||
else if (obj == ctx.function.proto("function")) prototype = FUNC_PROTO;
|
else if (obj == ctx.env.proto("function")) prototype = FUNC_PROTO;
|
||||||
else if (obj == ctx.function.proto("error")) prototype = ERR_PROTO;
|
else if (obj == ctx.env.proto("error")) prototype = ERR_PROTO;
|
||||||
else if (obj == ctx.function.proto("syntaxErr")) prototype = SYNTAX_ERR_PROTO;
|
else if (obj == ctx.env.proto("syntaxErr")) prototype = SYNTAX_ERR_PROTO;
|
||||||
else if (obj == ctx.function.proto("typeErr")) prototype = TYPE_ERR_PROTO;
|
else if (obj == ctx.env.proto("typeErr")) prototype = TYPE_ERR_PROTO;
|
||||||
else if (obj == ctx.function.proto("rangeErr")) prototype = RANGE_ERR_PROTO;
|
else if (obj == ctx.env.proto("rangeErr")) prototype = RANGE_ERR_PROTO;
|
||||||
else prototype = obj;
|
else prototype = obj;
|
||||||
}
|
}
|
||||||
else prototype = obj;
|
else prototype = obj;
|
||||||
@ -233,7 +233,7 @@ public class ObjectValue {
|
|||||||
public final Object getMember(Context ctx, Object key, Object thisArg) throws InterruptedException {
|
public final Object getMember(Context ctx, Object key, Object thisArg) throws InterruptedException {
|
||||||
key = Values.normalize(ctx, key);
|
key = Values.normalize(ctx, key);
|
||||||
|
|
||||||
if (key.equals("__proto__")) {
|
if ("__proto__".equals(key)) {
|
||||||
var res = getPrototype(ctx);
|
var res = getPrototype(ctx);
|
||||||
return res == null ? Values.NULL : res;
|
return res == null ? Values.NULL : res;
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,6 @@ public final class Symbol {
|
|||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (value == null) return "Symbol";
|
if (value == null) return "Symbol";
|
||||||
else return "Symbol(" + value + ")";
|
else return "@@" + value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,9 @@ import java.util.Map;
|
|||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.frame.ConvertHint;
|
import me.topchetoeu.jscript.engine.frame.ConvertHint;
|
||||||
|
import me.topchetoeu.jscript.exceptions.ConvertException;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||||
|
|
||||||
public class Values {
|
public class Values {
|
||||||
public static final Object NULL = new Object();
|
public static final Object NULL = new Object();
|
||||||
@ -115,8 +117,8 @@ public class Values {
|
|||||||
public static double toNumber(Context ctx, Object obj) throws InterruptedException {
|
public static double toNumber(Context ctx, Object obj) throws InterruptedException {
|
||||||
var val = toPrimitive(ctx, obj, ConvertHint.VALUEOF);
|
var val = toPrimitive(ctx, obj, ConvertHint.VALUEOF);
|
||||||
|
|
||||||
if (val instanceof Number) return number(obj);
|
if (val instanceof Number) return number(val);
|
||||||
if (val instanceof Boolean) return ((Boolean)obj) ? 1 : 0;
|
if (val instanceof Boolean) return ((Boolean)val) ? 1 : 0;
|
||||||
if (val instanceof String) {
|
if (val instanceof String) {
|
||||||
try {
|
try {
|
||||||
return Double.parseDouble((String)val);
|
return Double.parseDouble((String)val);
|
||||||
@ -132,7 +134,7 @@ public class Values {
|
|||||||
if (val == NULL) return "null";
|
if (val == NULL) return "null";
|
||||||
|
|
||||||
if (val instanceof Number) {
|
if (val instanceof Number) {
|
||||||
var d = number(obj);
|
var d = number(val);
|
||||||
if (d == Double.NEGATIVE_INFINITY) return "-Infinity";
|
if (d == Double.NEGATIVE_INFINITY) return "-Infinity";
|
||||||
if (d == Double.POSITIVE_INFINITY) return "Infinity";
|
if (d == Double.POSITIVE_INFINITY) return "Infinity";
|
||||||
if (Double.isNaN(d)) return "NaN";
|
if (Double.isNaN(d)) return "NaN";
|
||||||
@ -321,10 +323,10 @@ public class Values {
|
|||||||
if (isObject(obj)) return object(obj).getPrototype(ctx);
|
if (isObject(obj)) return object(obj).getPrototype(ctx);
|
||||||
if (ctx == null) return null;
|
if (ctx == null) return null;
|
||||||
|
|
||||||
if (obj instanceof String) return ctx.function.proto("string");
|
if (obj instanceof String) return ctx.env.proto("string");
|
||||||
else if (obj instanceof Number) return ctx.function.proto("number");
|
else if (obj instanceof Number) return ctx.env.proto("number");
|
||||||
else if (obj instanceof Boolean) return ctx.function.proto("bool");
|
else if (obj instanceof Boolean) return ctx.env.proto("bool");
|
||||||
else if (obj instanceof Symbol) return ctx.function.proto("symbol");
|
else if (obj instanceof Symbol) return ctx.env.proto("symbol");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -352,12 +354,38 @@ public class Values {
|
|||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
public static ObjectValue getMemberDescriptor(Context ctx, Object obj, Object key) throws InterruptedException {
|
||||||
|
if (obj instanceof ObjectValue) return ((ObjectValue)obj).getMemberDescriptor(ctx, key);
|
||||||
|
else if (obj instanceof String && key instanceof Number) {
|
||||||
|
var i = ((Number)key).intValue();
|
||||||
|
var _i = ((Number)key).doubleValue();
|
||||||
|
if (i - _i != 0) return null;
|
||||||
|
if (i < 0 || i >= ((String)obj).length()) return null;
|
||||||
|
|
||||||
|
return new ObjectValue(ctx, Map.of(
|
||||||
|
"value", ((String)obj).charAt(i) + "",
|
||||||
|
"writable", false,
|
||||||
|
"enumerable", true,
|
||||||
|
"configurable", false
|
||||||
|
));
|
||||||
|
}
|
||||||
|
else return null;
|
||||||
|
}
|
||||||
|
|
||||||
public static Object call(Context ctx, Object func, Object thisArg, Object ...args) throws InterruptedException {
|
public static Object call(Context ctx, Object func, Object thisArg, Object ...args) throws InterruptedException {
|
||||||
if (!isFunction(func))
|
if (!isFunction(func)) throw EngineException.ofType("Tried to call a non-function value.");
|
||||||
throw EngineException.ofType("Tried to call a non-function value.");
|
|
||||||
return function(func).call(ctx, thisArg, args);
|
return function(func).call(ctx, thisArg, args);
|
||||||
}
|
}
|
||||||
|
public static Object callNew(Context ctx, Object func, Object ...args) throws InterruptedException {
|
||||||
|
var res = new ObjectValue();
|
||||||
|
var proto = Values.getMember(ctx, func, "prototype");
|
||||||
|
res.setPrototype(ctx, proto);
|
||||||
|
|
||||||
|
var ret = call(ctx, func, res, args);
|
||||||
|
|
||||||
|
if (ret != null && func instanceof FunctionValue && ((FunctionValue)func).special) return ret;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean strictEquals(Context ctx, Object a, Object b) {
|
public static boolean strictEquals(Context ctx, Object a, Object b) {
|
||||||
a = normalize(ctx, a); b = normalize(ctx, b);
|
a = normalize(ctx, a); b = normalize(ctx, b);
|
||||||
@ -420,7 +448,7 @@ public class Values {
|
|||||||
|
|
||||||
if (val instanceof Class) {
|
if (val instanceof Class) {
|
||||||
if (ctx == null) return null;
|
if (ctx == null) return null;
|
||||||
else return ctx.function.wrappersProvider.getConstr((Class<?>)val);
|
else return ctx.env.wrappersProvider.getConstr((Class<?>)val);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new NativeWrapper(val);
|
return new NativeWrapper(val);
|
||||||
@ -429,17 +457,15 @@ public class Values {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static <T> T convert(Context ctx, Object obj, Class<T> clazz) throws InterruptedException {
|
public static <T> T convert(Context ctx, Object obj, Class<T> clazz) throws InterruptedException {
|
||||||
if (clazz == Void.class) return null;
|
if (clazz == Void.class) return null;
|
||||||
if (clazz == null || clazz == Object.class) return (T)obj;
|
|
||||||
|
|
||||||
var err = new IllegalArgumentException(String.format("Cannot convert '%s' to '%s'.", type(obj), clazz.getName()));
|
|
||||||
|
|
||||||
if (obj instanceof NativeWrapper) {
|
if (obj instanceof NativeWrapper) {
|
||||||
var res = ((NativeWrapper)obj).wrapped;
|
var res = ((NativeWrapper)obj).wrapped;
|
||||||
if (clazz.isInstance(res)) return (T)res;
|
if (clazz.isInstance(res)) return (T)res;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj instanceof ArrayValue) {
|
if (clazz == null || clazz == Object.class) return (T)obj;
|
||||||
|
|
||||||
|
if (obj instanceof ArrayValue) {
|
||||||
if (clazz.isAssignableFrom(ArrayList.class)) {
|
if (clazz.isAssignableFrom(ArrayList.class)) {
|
||||||
var raw = array(obj).toArray();
|
var raw = array(obj).toArray();
|
||||||
var res = new ArrayList<>();
|
var res = new ArrayList<>();
|
||||||
@ -480,11 +506,9 @@ public class Values {
|
|||||||
|
|
||||||
if (clazz == Character.class || clazz == char.class) {
|
if (clazz == Character.class || clazz == char.class) {
|
||||||
if (obj instanceof Number) return (T)(Character)(char)number(obj);
|
if (obj instanceof Number) return (T)(Character)(char)number(obj);
|
||||||
else if (obj == NULL) throw new IllegalArgumentException("Cannot convert null to character.");
|
|
||||||
else if (obj == null) throw new IllegalArgumentException("Cannot convert undefined to character.");
|
|
||||||
else {
|
else {
|
||||||
var res = toString(ctx, obj);
|
var res = toString(ctx, obj);
|
||||||
if (res.length() == 0) throw new IllegalArgumentException("Cannot convert empty string to character.");
|
if (res.length() == 0) throw new ConvertException("\"\"", "Character");
|
||||||
else return (T)(Character)res.charAt(0);
|
else return (T)(Character)res.charAt(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -492,30 +516,32 @@ public class Values {
|
|||||||
if (obj == null) return null;
|
if (obj == null) return null;
|
||||||
if (clazz.isInstance(obj)) return (T)obj;
|
if (clazz.isInstance(obj)) return (T)obj;
|
||||||
|
|
||||||
throw err;
|
throw new ConvertException(type(obj), clazz.getSimpleName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Iterable<Object> toJavaIterable(Context ctx, Object obj) throws InterruptedException {
|
public static Iterable<Object> toJavaIterable(Context ctx, Object obj) {
|
||||||
return () -> {
|
return () -> {
|
||||||
try {
|
try {
|
||||||
var constr = getMember(ctx, ctx.function.proto("symbol"), "constructor");
|
var symbol = ctx.env.symbol("Symbol.iterator");
|
||||||
var symbol = getMember(ctx, constr, "iterator");
|
|
||||||
|
|
||||||
var iteratorFunc = getMember(ctx, obj, symbol);
|
var iteratorFunc = getMember(ctx, obj, symbol);
|
||||||
if (!isFunction(iteratorFunc)) return Collections.emptyIterator();
|
if (!isFunction(iteratorFunc)) return Collections.emptyIterator();
|
||||||
var iterator = getMember(ctx, call(ctx, iteratorFunc, obj), "next");
|
var iterator = iteratorFunc instanceof FunctionValue ?
|
||||||
if (!isFunction(iterator)) return Collections.emptyIterator();
|
((FunctionValue)iteratorFunc).call(ctx, obj, obj) :
|
||||||
var iterable = obj;
|
iteratorFunc;
|
||||||
|
var nextFunc = getMember(ctx, call(ctx, iteratorFunc, obj), "next");
|
||||||
|
|
||||||
|
if (!isFunction(nextFunc)) return Collections.emptyIterator();
|
||||||
|
|
||||||
return new Iterator<Object>() {
|
return new Iterator<Object>() {
|
||||||
private Object value = null;
|
private Object value = null;
|
||||||
public boolean consumed = true;
|
public boolean consumed = true;
|
||||||
private FunctionValue next = function(iterator);
|
private FunctionValue next = (FunctionValue)nextFunc;
|
||||||
|
|
||||||
private void loadNext() throws InterruptedException {
|
private void loadNext() throws InterruptedException {
|
||||||
if (next == null) value = null;
|
if (next == null) value = null;
|
||||||
else if (consumed) {
|
else if (consumed) {
|
||||||
var curr = object(next.call(ctx, iterable));
|
var curr = object(next.call(ctx, iterator));
|
||||||
if (curr == null) { next = null; value = null; }
|
if (curr == null) { next = null; value = null; }
|
||||||
if (toBoolean(curr.getMember(ctx, "done"))) { next = null; value = null; }
|
if (toBoolean(curr.getMember(ctx, "done"))) { next = null; value = null; }
|
||||||
else {
|
else {
|
||||||
@ -562,12 +588,11 @@ public class Values {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ObjectValue fromJavaIterable(Context ctx, Iterable<?> iterable) throws InterruptedException {
|
public static ObjectValue fromJavaIterator(Context ctx, Iterator<?> it) throws InterruptedException {
|
||||||
var res = new ObjectValue();
|
var res = new ObjectValue();
|
||||||
var it = iterable.iterator();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var key = getMember(ctx, getMember(ctx, ctx.function.proto("symbol"), "constructor"), "iterator");
|
var key = getMember(ctx, getMember(ctx, ctx.env.proto("symbol"), "constructor"), "iterator");
|
||||||
res.defineProperty(ctx, key, new NativeFunction("", (_ctx, thisArg, args) -> thisArg));
|
res.defineProperty(ctx, key, new NativeFunction("", (_ctx, thisArg, args) -> thisArg));
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException | NullPointerException e) { }
|
catch (IllegalArgumentException | NullPointerException e) { }
|
||||||
@ -580,6 +605,10 @@ public class Values {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ObjectValue fromJavaIterable(Context ctx, Iterable<?> it) throws InterruptedException {
|
||||||
|
return fromJavaIterator(ctx, it.iterator());
|
||||||
|
}
|
||||||
|
|
||||||
private static void printValue(Context ctx, Object val, HashSet<Object> passed, int tab) throws InterruptedException {
|
private static void printValue(Context ctx, Object val, HashSet<Object> passed, int tab) throws InterruptedException {
|
||||||
if (passed.contains(val)) {
|
if (passed.contains(val)) {
|
||||||
System.out.print("[circular]");
|
System.out.print("[circular]");
|
||||||
@ -604,7 +633,7 @@ public class Values {
|
|||||||
if (i != 0) System.out.print(", ");
|
if (i != 0) System.out.print(", ");
|
||||||
else System.out.print(" ");
|
else System.out.print(" ");
|
||||||
if (obj.has(i)) printValue(ctx, obj.get(i), passed, tab);
|
if (obj.has(i)) printValue(ctx, obj.get(i), passed, tab);
|
||||||
else System.out.print(", ");
|
else System.out.print("<empty>");
|
||||||
}
|
}
|
||||||
System.out.print(" ] ");
|
System.out.print(" ] ");
|
||||||
}
|
}
|
||||||
@ -655,4 +684,25 @@ public class Values {
|
|||||||
public static void printValue(Context ctx, Object val) throws InterruptedException {
|
public static void printValue(Context ctx, Object val) throws InterruptedException {
|
||||||
printValue(ctx, val, new HashSet<>(), 0);
|
printValue(ctx, val, new HashSet<>(), 0);
|
||||||
}
|
}
|
||||||
|
public static void printError(RuntimeException err, String prefix) throws InterruptedException {
|
||||||
|
prefix = prefix == null ? "Uncaught" : "Uncaught " + prefix;
|
||||||
|
try {
|
||||||
|
if (err instanceof EngineException) {
|
||||||
|
System.out.println(prefix + " " + ((EngineException)err).toString(((EngineException)err).ctx));
|
||||||
|
}
|
||||||
|
else if (err instanceof SyntaxException) {
|
||||||
|
System.out.println("Syntax error:" + ((SyntaxException)err).msg);
|
||||||
|
}
|
||||||
|
else if (err.getCause() instanceof InterruptedException) return;
|
||||||
|
else {
|
||||||
|
System.out.println("Internal error ocurred:");
|
||||||
|
err.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (EngineException ex) {
|
||||||
|
System.out.println("Uncaught ");
|
||||||
|
Values.printValue(null, ((EngineException)err).value);
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
11
src/me/topchetoeu/jscript/exceptions/ConvertException.java
Normal file
11
src/me/topchetoeu/jscript/exceptions/ConvertException.java
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package me.topchetoeu.jscript.exceptions;
|
||||||
|
|
||||||
|
public class ConvertException extends RuntimeException {
|
||||||
|
public final String source, target;
|
||||||
|
|
||||||
|
public ConvertException(String source, String target) {
|
||||||
|
super(String.format("Cannot convert '%s' to '%s'.", source, target));
|
||||||
|
this.source = source;
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,7 @@ import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto;
|
|||||||
public class EngineException extends RuntimeException {
|
public class EngineException extends RuntimeException {
|
||||||
public final Object value;
|
public final Object value;
|
||||||
public EngineException cause;
|
public EngineException cause;
|
||||||
|
public Context ctx = null;
|
||||||
public final List<String> stackTrace = new ArrayList<>();
|
public final List<String> stackTrace = new ArrayList<>();
|
||||||
|
|
||||||
public EngineException add(String name, Location location) {
|
public EngineException add(String name, Location location) {
|
||||||
@ -27,6 +28,10 @@ public class EngineException extends RuntimeException {
|
|||||||
this.cause = cause;
|
this.cause = cause;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
public EngineException setContext(Context ctx) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public String toString(Context ctx) throws InterruptedException {
|
public String toString(Context ctx) throws InterruptedException {
|
||||||
var ss = new StringBuilder();
|
var ss = new StringBuilder();
|
||||||
@ -36,16 +41,17 @@ public class EngineException extends RuntimeException {
|
|||||||
catch (EngineException e) {
|
catch (EngineException e) {
|
||||||
ss.append("[Error while stringifying]\n");
|
ss.append("[Error while stringifying]\n");
|
||||||
}
|
}
|
||||||
for (var line : stackTrace) {
|
// for (var line : stackTrace) {
|
||||||
ss.append(" ").append(line).append('\n');
|
// ss.append(" ").append(line).append('\n');
|
||||||
}
|
// }
|
||||||
if (cause != null) ss.append("Caused by ").append(cause.toString(ctx)).append('\n');
|
// if (cause != null) ss.append("Caused by ").append(cause.toString(ctx)).append('\n');
|
||||||
ss.deleteCharAt(ss.length() - 1);
|
ss.deleteCharAt(ss.length() - 1);
|
||||||
return ss.toString();
|
return ss.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object err(String msg, PlaceholderProto proto) {
|
private static Object err(String name, String msg, PlaceholderProto proto) {
|
||||||
var res = new ObjectValue(proto);
|
var res = new ObjectValue(proto);
|
||||||
|
if (name != null) res.defineProperty(null, "name", name);
|
||||||
res.defineProperty(null, "message", msg);
|
res.defineProperty(null, "message", msg);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -57,19 +63,22 @@ public class EngineException extends RuntimeException {
|
|||||||
this.cause = null;
|
this.cause = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static EngineException ofError(String name, String msg) {
|
||||||
|
return new EngineException(err(name, msg, PlaceholderProto.ERROR));
|
||||||
|
}
|
||||||
public static EngineException ofError(String msg) {
|
public static EngineException ofError(String msg) {
|
||||||
return new EngineException(err(msg, PlaceholderProto.ERROR));
|
return new EngineException(err(null, msg, PlaceholderProto.ERROR));
|
||||||
}
|
}
|
||||||
public static EngineException ofSyntax(SyntaxException e) {
|
public static EngineException ofSyntax(SyntaxException e) {
|
||||||
return new EngineException(err(e.msg, PlaceholderProto.SYNTAX_ERROR)).add(null, e.loc);
|
return new EngineException(err(null, e.msg, PlaceholderProto.SYNTAX_ERROR)).add(null, e.loc);
|
||||||
}
|
}
|
||||||
public static EngineException ofSyntax(String msg) {
|
public static EngineException ofSyntax(String msg) {
|
||||||
return new EngineException(err(msg, PlaceholderProto.SYNTAX_ERROR));
|
return new EngineException(err(null, msg, PlaceholderProto.SYNTAX_ERROR));
|
||||||
}
|
}
|
||||||
public static EngineException ofType(String msg) {
|
public static EngineException ofType(String msg) {
|
||||||
return new EngineException(err(msg, PlaceholderProto.TYPE_ERROR));
|
return new EngineException(err(null, msg, PlaceholderProto.TYPE_ERROR));
|
||||||
}
|
}
|
||||||
public static EngineException ofRange(String msg) {
|
public static EngineException ofRange(String msg) {
|
||||||
return new EngineException(err(msg, PlaceholderProto.RANGE_ERROR));
|
return new EngineException(err(null, msg, PlaceholderProto.RANGE_ERROR));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
7
src/me/topchetoeu/jscript/filesystem/EntryType.java
Normal file
7
src/me/topchetoeu/jscript/filesystem/EntryType.java
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package me.topchetoeu.jscript.filesystem;
|
||||||
|
|
||||||
|
public enum EntryType {
|
||||||
|
NONE,
|
||||||
|
FILE,
|
||||||
|
FOLDER,
|
||||||
|
}
|
27
src/me/topchetoeu/jscript/filesystem/File.java
Normal file
27
src/me/topchetoeu/jscript/filesystem/File.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package me.topchetoeu.jscript.filesystem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface File {
|
||||||
|
int read() throws IOException, InterruptedException;
|
||||||
|
boolean write(byte val) throws IOException, InterruptedException;
|
||||||
|
long tell() throws IOException, InterruptedException;
|
||||||
|
void seek(long offset, int pos) throws IOException, InterruptedException;
|
||||||
|
void close() throws IOException, InterruptedException;
|
||||||
|
Permissions perms();
|
||||||
|
|
||||||
|
default String readToString() throws IOException, InterruptedException {
|
||||||
|
seek(0, 2);
|
||||||
|
long len = tell();
|
||||||
|
if (len < 0) return null;
|
||||||
|
|
||||||
|
seek(0, 0);
|
||||||
|
byte[] res = new byte[(int)len];
|
||||||
|
|
||||||
|
for (var i = 0; i < len; i++) {
|
||||||
|
res[i] = (byte)read();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new String(res);
|
||||||
|
}
|
||||||
|
}
|
10
src/me/topchetoeu/jscript/filesystem/Filesystem.java
Normal file
10
src/me/topchetoeu/jscript/filesystem/Filesystem.java
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package me.topchetoeu.jscript.filesystem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface Filesystem {
|
||||||
|
File open(String path) throws IOException, InterruptedException;
|
||||||
|
boolean mkdir(String path) throws IOException, InterruptedException;
|
||||||
|
EntryType type(String path) throws IOException, InterruptedException;
|
||||||
|
boolean rm(String path) throws IOException, InterruptedException;
|
||||||
|
}
|
35
src/me/topchetoeu/jscript/filesystem/InaccessibleFile.java
Normal file
35
src/me/topchetoeu/jscript/filesystem/InaccessibleFile.java
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package me.topchetoeu.jscript.filesystem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class InaccessibleFile implements File {
|
||||||
|
public static final InaccessibleFile INSTANCE = new InaccessibleFile();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException, InterruptedException {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean write(byte val) throws IOException, InterruptedException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long tell() throws IOException, InterruptedException {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void seek(long offset, int pos) throws IOException, InterruptedException { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException, InterruptedException { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Permissions perms() {
|
||||||
|
return Permissions.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private InaccessibleFile() { }
|
||||||
|
}
|
53
src/me/topchetoeu/jscript/filesystem/MemoryFile.java
Normal file
53
src/me/topchetoeu/jscript/filesystem/MemoryFile.java
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package me.topchetoeu.jscript.filesystem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class MemoryFile implements File {
|
||||||
|
private int ptr;
|
||||||
|
private Permissions mode;
|
||||||
|
public final byte[] data;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException, InterruptedException {
|
||||||
|
if (data == null || !mode.readable || ptr >= data.length) return -1;
|
||||||
|
return data[ptr++];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean write(byte val) throws IOException, InterruptedException {
|
||||||
|
if (data == null || !mode.writable || ptr >= data.length) return false;
|
||||||
|
data[ptr++] = val;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long tell() throws IOException, InterruptedException {
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void seek(long offset, int pos) throws IOException, InterruptedException {
|
||||||
|
if (data == null) return;
|
||||||
|
|
||||||
|
if (pos == 0) ptr = (int)offset;
|
||||||
|
else if (pos == 1) ptr += (int)offset;
|
||||||
|
else if (pos == 2) ptr = data.length - (int)offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException, InterruptedException {
|
||||||
|
mode = null;
|
||||||
|
ptr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Permissions perms() {
|
||||||
|
if (data == null) return Permissions.NONE;
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MemoryFile(byte[] buff, Permissions mode) {
|
||||||
|
this.data = buff;
|
||||||
|
this.mode = mode;
|
||||||
|
}
|
||||||
|
}
|
17
src/me/topchetoeu/jscript/filesystem/Permissions.java
Normal file
17
src/me/topchetoeu/jscript/filesystem/Permissions.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package me.topchetoeu.jscript.filesystem;
|
||||||
|
|
||||||
|
public enum Permissions {
|
||||||
|
NONE("", false, false),
|
||||||
|
READ("r", true, false),
|
||||||
|
READ_WRITE("rw", true, true);
|
||||||
|
|
||||||
|
public final String readMode;
|
||||||
|
public final boolean readable;
|
||||||
|
public final boolean writable;
|
||||||
|
|
||||||
|
private Permissions(String mode, boolean r, boolean w) {
|
||||||
|
this.readMode = mode;
|
||||||
|
this.readable = r;
|
||||||
|
this.writable = w;
|
||||||
|
}
|
||||||
|
}
|
51
src/me/topchetoeu/jscript/filesystem/PhysicalFile.java
Normal file
51
src/me/topchetoeu/jscript/filesystem/PhysicalFile.java
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package me.topchetoeu.jscript.filesystem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
|
||||||
|
public class PhysicalFile implements File {
|
||||||
|
private RandomAccessFile file;
|
||||||
|
private Permissions perms;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException, InterruptedException {
|
||||||
|
if (file == null || !perms.readable) return -1;
|
||||||
|
else return file.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean write(byte val) throws IOException, InterruptedException {
|
||||||
|
if (file == null || !perms.writable) return false;
|
||||||
|
file.write(val);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long tell() throws IOException, InterruptedException {
|
||||||
|
if (file == null) return 0;
|
||||||
|
return file.getFilePointer();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void seek(long offset, int pos) throws IOException, InterruptedException {
|
||||||
|
if (file == null) return;
|
||||||
|
if (pos == 0) file.seek(pos);
|
||||||
|
else if (pos == 1) file.seek(file.getFilePointer() + pos);
|
||||||
|
else if (pos == 2) file.seek(file.length() + pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException, InterruptedException {
|
||||||
|
if (file == null) return;
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Permissions perms() { return perms; }
|
||||||
|
|
||||||
|
public PhysicalFile(String path, Permissions mode) throws IOException {
|
||||||
|
if (mode == Permissions.NONE) file = null;
|
||||||
|
else file = new RandomAccessFile(path, mode.readMode);
|
||||||
|
|
||||||
|
perms = mode;
|
||||||
|
}
|
||||||
|
}
|
74
src/me/topchetoeu/jscript/filesystem/PhysicalFilesystem.java
Normal file
74
src/me/topchetoeu/jscript/filesystem/PhysicalFilesystem.java
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package me.topchetoeu.jscript.filesystem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public class PhysicalFilesystem implements Filesystem {
|
||||||
|
public final Path root;
|
||||||
|
|
||||||
|
private Permissions getPerms(Path path) {
|
||||||
|
var file = path.toFile();
|
||||||
|
if (!path.startsWith(root)) return Permissions.NONE;
|
||||||
|
if (file.canRead() && file.canWrite()) return Permissions.READ_WRITE;
|
||||||
|
if (file.canRead()) return Permissions.READ;
|
||||||
|
|
||||||
|
return Permissions.NONE;
|
||||||
|
}
|
||||||
|
private Path getPath(String name) {
|
||||||
|
return root.resolve(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public File open(String path) throws IOException, InterruptedException {
|
||||||
|
var _path = root.resolve(path);
|
||||||
|
|
||||||
|
var perms = getPerms(_path);
|
||||||
|
if (perms == Permissions.NONE) return InaccessibleFile.INSTANCE;
|
||||||
|
|
||||||
|
var f = _path.toFile();
|
||||||
|
|
||||||
|
if (f.isDirectory()) {
|
||||||
|
var res = new StringBuilder();
|
||||||
|
|
||||||
|
for (var child : f.listFiles()) res.append(child.toString()).append('\n');
|
||||||
|
|
||||||
|
return new MemoryFile(res.toString().getBytes(), Permissions.READ);
|
||||||
|
}
|
||||||
|
else return new PhysicalFile(path, perms);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mkdir(String path) throws IOException, InterruptedException {
|
||||||
|
var _path = getPath(path);
|
||||||
|
var perms = getPerms(_path);
|
||||||
|
var f = _path.toFile();
|
||||||
|
|
||||||
|
if (!perms.writable) return false;
|
||||||
|
else return f.mkdir();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntryType type(String path) throws IOException, InterruptedException {
|
||||||
|
var _path = getPath(path);
|
||||||
|
var perms = getPerms(_path);
|
||||||
|
var f = _path.toFile();
|
||||||
|
|
||||||
|
if (perms == Permissions.NONE) return EntryType.NONE;
|
||||||
|
else if (f.isFile()) return EntryType.FILE;
|
||||||
|
else return EntryType.FOLDER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean rm(String path) throws IOException, InterruptedException {
|
||||||
|
var _path = getPath(path);
|
||||||
|
var perms = getPerms(_path);
|
||||||
|
var f = _path.toFile();
|
||||||
|
|
||||||
|
if (!perms.writable) return false;
|
||||||
|
else return f.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PhysicalFilesystem(Path root) {
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
}
|
7
src/me/topchetoeu/jscript/interop/InitType.java
Normal file
7
src/me/topchetoeu/jscript/interop/InitType.java
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package me.topchetoeu.jscript.interop;
|
||||||
|
|
||||||
|
public enum InitType {
|
||||||
|
CONSTRUCTOR,
|
||||||
|
PROTOTYPE,
|
||||||
|
NAMESPACE,
|
||||||
|
}
|
@ -9,4 +9,5 @@ import java.lang.annotation.Target;
|
|||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface Native {
|
public @interface Native {
|
||||||
public String value() default "";
|
public String value() default "";
|
||||||
|
public boolean thisArg() default false;
|
||||||
}
|
}
|
||||||
|
13
src/me/topchetoeu/jscript/interop/NativeConstructor.java
Normal file
13
src/me/topchetoeu/jscript/interop/NativeConstructor.java
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package me.topchetoeu.jscript.interop;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target({ ElementType.METHOD })
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface NativeConstructor {
|
||||||
|
public boolean thisArg() default false;
|
||||||
|
}
|
||||||
|
|
@ -8,5 +8,6 @@ import java.lang.annotation.Target;
|
|||||||
@Target({ ElementType.METHOD })
|
@Target({ ElementType.METHOD })
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface NativeGetter {
|
public @interface NativeGetter {
|
||||||
public String value();
|
public String value() default "";
|
||||||
|
public boolean thisArg() default false;
|
||||||
}
|
}
|
||||||
|
12
src/me/topchetoeu/jscript/interop/NativeInit.java
Normal file
12
src/me/topchetoeu/jscript/interop/NativeInit.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package me.topchetoeu.jscript.interop;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target({ ElementType.METHOD })
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface NativeInit {
|
||||||
|
InitType value();
|
||||||
|
}
|
@ -8,5 +8,6 @@ import java.lang.annotation.Target;
|
|||||||
@Target({ ElementType.METHOD })
|
@Target({ ElementType.METHOD })
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface NativeSetter {
|
public @interface NativeSetter {
|
||||||
public String value();
|
public String value() default "";
|
||||||
|
public boolean thisArg() default false;
|
||||||
}
|
}
|
||||||
|
@ -3,36 +3,46 @@ package me.topchetoeu.jscript.interop;
|
|||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
import me.topchetoeu.jscript.engine.WrappersProvider;
|
import me.topchetoeu.jscript.engine.WrappersProvider;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
|
||||||
public class NativeTypeRegister implements WrappersProvider {
|
public class NativeWrapperProvider implements WrappersProvider {
|
||||||
private final HashMap<Class<?>, FunctionValue> constructors = new HashMap<>();
|
private final HashMap<Class<?>, FunctionValue> constructors = new HashMap<>();
|
||||||
private final HashMap<Class<?>, ObjectValue> prototypes = new HashMap<>();
|
private final HashMap<Class<?>, ObjectValue> prototypes = new HashMap<>();
|
||||||
|
private final Environment env;
|
||||||
|
|
||||||
private static void applyMethods(boolean member, ObjectValue target, Class<?> clazz) {
|
private static void applyMethods(Environment env, boolean member, ObjectValue target, Class<?> clazz) {
|
||||||
for (var method : clazz.getDeclaredMethods()) {
|
for (var method : clazz.getDeclaredMethods()) {
|
||||||
if (!Modifier.isStatic(method.getModifiers()) != member) continue;
|
|
||||||
|
|
||||||
var nat = method.getAnnotation(Native.class);
|
var nat = method.getAnnotation(Native.class);
|
||||||
var get = method.getAnnotation(NativeGetter.class);
|
var get = method.getAnnotation(NativeGetter.class);
|
||||||
var set = method.getAnnotation(NativeSetter.class);
|
var set = method.getAnnotation(NativeSetter.class);
|
||||||
|
var memberMatch = !Modifier.isStatic(method.getModifiers()) == member;
|
||||||
|
|
||||||
if (nat != null) {
|
if (nat != null) {
|
||||||
var name = nat.value();
|
if (nat.thisArg() && !member || !nat.thisArg() && !memberMatch) continue;
|
||||||
|
|
||||||
|
Object name = nat.value();
|
||||||
|
if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2));
|
||||||
|
else if (name.equals("")) name = method.getName();
|
||||||
|
|
||||||
var val = target.values.get(name);
|
var val = target.values.get(name);
|
||||||
|
|
||||||
if (name.equals("")) name = method.getName();
|
if (!(val instanceof OverloadFunction)) target.defineProperty(null, name, val = new OverloadFunction(name.toString()));
|
||||||
if (!(val instanceof OverloadFunction)) target.defineProperty(null, name, val = new OverloadFunction(name));
|
|
||||||
|
|
||||||
((OverloadFunction)val).overloads.add(Overload.fromMethod(method));
|
((OverloadFunction)val).add(Overload.fromMethod(method, nat.thisArg()));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (get != null) {
|
if (get != null) {
|
||||||
var name = get.value();
|
if (get.thisArg() && !member || !get.thisArg() && !memberMatch) continue;
|
||||||
|
|
||||||
|
Object name = get.value();
|
||||||
|
if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2));
|
||||||
|
else if (name.equals("")) name = method.getName();
|
||||||
|
|
||||||
var prop = target.properties.get(name);
|
var prop = target.properties.get(name);
|
||||||
OverloadFunction getter = null;
|
OverloadFunction getter = null;
|
||||||
var setter = prop == null ? null : prop.setter;
|
var setter = prop == null ? null : prop.setter;
|
||||||
@ -40,11 +50,16 @@ public class NativeTypeRegister implements WrappersProvider {
|
|||||||
if (prop != null && prop.getter instanceof OverloadFunction) getter = (OverloadFunction)prop.getter;
|
if (prop != null && prop.getter instanceof OverloadFunction) getter = (OverloadFunction)prop.getter;
|
||||||
else getter = new OverloadFunction("get " + name);
|
else getter = new OverloadFunction("get " + name);
|
||||||
|
|
||||||
getter.overloads.add(Overload.fromMethod(method));
|
getter.add(Overload.fromMethod(method, get.thisArg()));
|
||||||
target.defineProperty(null, name, getter, setter, true, true);
|
target.defineProperty(null, name, getter, setter, true, true);
|
||||||
}
|
}
|
||||||
if (set != null) {
|
if (set != null) {
|
||||||
var name = set.value();
|
if (set.thisArg() && !member || !set.thisArg() && !memberMatch) continue;
|
||||||
|
|
||||||
|
Object name = set.value();
|
||||||
|
if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2));
|
||||||
|
else if (name.equals("")) name = method.getName();
|
||||||
|
|
||||||
var prop = target.properties.get(name);
|
var prop = target.properties.get(name);
|
||||||
var getter = prop == null ? null : prop.getter;
|
var getter = prop == null ? null : prop.getter;
|
||||||
OverloadFunction setter = null;
|
OverloadFunction setter = null;
|
||||||
@ -52,34 +67,37 @@ public class NativeTypeRegister implements WrappersProvider {
|
|||||||
if (prop != null && prop.setter instanceof OverloadFunction) setter = (OverloadFunction)prop.setter;
|
if (prop != null && prop.setter instanceof OverloadFunction) setter = (OverloadFunction)prop.setter;
|
||||||
else setter = new OverloadFunction("set " + name);
|
else setter = new OverloadFunction("set " + name);
|
||||||
|
|
||||||
setter.overloads.add(Overload.fromMethod(method));
|
setter.add(Overload.fromMethod(method, set.thisArg()));
|
||||||
target.defineProperty(null, name, getter, setter, true, true);
|
target.defineProperty(null, name, getter, setter, true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private static void applyFields(boolean member, ObjectValue target, Class<?> clazz) {
|
private static void applyFields(Environment env, boolean member, ObjectValue target, Class<?> clazz) {
|
||||||
for (var field : clazz.getDeclaredFields()) {
|
for (var field : clazz.getDeclaredFields()) {
|
||||||
if (!Modifier.isStatic(field.getModifiers()) != member) continue;
|
if (!Modifier.isStatic(field.getModifiers()) != member) continue;
|
||||||
var nat = field.getAnnotation(Native.class);
|
var nat = field.getAnnotation(Native.class);
|
||||||
|
|
||||||
if (nat != null) {
|
if (nat != null) {
|
||||||
var name = nat.value();
|
Object name = nat.value();
|
||||||
if (name.equals("")) name = field.getName();
|
if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2));
|
||||||
var getter = new OverloadFunction("get " + name).add(Overload.getterFromField(field));
|
else if (name.equals("")) name = field.getName();
|
||||||
var setter = new OverloadFunction("set " + name).add(Overload.setterFromField(field));
|
|
||||||
|
var getter = OverloadFunction.of("get " + name, Overload.getterFromField(field));
|
||||||
|
var setter = OverloadFunction.of("set " + name, Overload.setterFromField(field));
|
||||||
target.defineProperty(null, name, getter, setter, true, false);
|
target.defineProperty(null, name, getter, setter, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private static void applyClasses(boolean member, ObjectValue target, Class<?> clazz) {
|
private static void applyClasses(Environment env, boolean member, ObjectValue target, Class<?> clazz) {
|
||||||
for (var cl : clazz.getDeclaredClasses()) {
|
for (var cl : clazz.getDeclaredClasses()) {
|
||||||
if (!Modifier.isStatic(cl.getModifiers()) != member) continue;
|
if (!Modifier.isStatic(cl.getModifiers()) != member) continue;
|
||||||
var nat = cl.getAnnotation(Native.class);
|
var nat = cl.getAnnotation(Native.class);
|
||||||
|
|
||||||
if (nat != null) {
|
if (nat != null) {
|
||||||
var name = nat.value();
|
Object name = nat.value();
|
||||||
if (name.equals("")) name = cl.getSimpleName();
|
if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2));
|
||||||
|
else if (name.equals("")) name = cl.getName();
|
||||||
|
|
||||||
var getter = new NativeFunction("get " + name, (ctx, thisArg, args) -> cl);
|
var getter = new NativeFunction("get " + name, (ctx, thisArg, args) -> cl);
|
||||||
|
|
||||||
@ -94,12 +112,19 @@ public class NativeTypeRegister implements WrappersProvider {
|
|||||||
* All accessors and methods will expect the this argument to be a native wrapper of the given class type.
|
* All accessors and methods will expect the this argument to be a native wrapper of the given class type.
|
||||||
* @param clazz The class for which a prototype should be generated
|
* @param clazz The class for which a prototype should be generated
|
||||||
*/
|
*/
|
||||||
public static ObjectValue makeProto(Class<?> clazz) {
|
public static ObjectValue makeProto(Environment ctx, Class<?> clazz) {
|
||||||
var res = new ObjectValue();
|
var res = new ObjectValue();
|
||||||
|
|
||||||
applyMethods(true, res, clazz);
|
for (var overload : clazz.getDeclaredMethods()) {
|
||||||
applyFields(true, res, clazz);
|
var init = overload.getAnnotation(NativeInit.class);
|
||||||
applyClasses(true, res, clazz);
|
if (init == null || init.value() != InitType.PROTOTYPE) continue;
|
||||||
|
try { overload.invoke(null, ctx, res); }
|
||||||
|
catch (ReflectiveOperationException e) { e.printStackTrace(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
applyMethods(ctx, true, res, clazz);
|
||||||
|
applyFields(ctx, true, res, clazz);
|
||||||
|
applyClasses(ctx, true, res, clazz);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -109,21 +134,33 @@ public class NativeTypeRegister implements WrappersProvider {
|
|||||||
* When the function gets called, the underlying constructor will get called, unless the constructor is inaccessible.
|
* When the function gets called, the underlying constructor will get called, unless the constructor is inaccessible.
|
||||||
* @param clazz The class for which a constructor should be generated
|
* @param clazz The class for which a constructor should be generated
|
||||||
*/
|
*/
|
||||||
public static FunctionValue makeConstructor(Class<?> clazz) {
|
public static FunctionValue makeConstructor(Environment ctx, Class<?> clazz) {
|
||||||
FunctionValue func = new OverloadFunction(clazz.getName());
|
FunctionValue func = new OverloadFunction(clazz.getName());
|
||||||
|
|
||||||
for (var overload : clazz.getConstructors()) {
|
for (var overload : clazz.getDeclaredConstructors()) {
|
||||||
if (overload.getAnnotation(Native.class) == null) continue;
|
var nat = overload.getAnnotation(Native.class);
|
||||||
((OverloadFunction)func).add(Overload.fromConstructor(overload));
|
if (nat == null) continue;
|
||||||
|
((OverloadFunction)func).add(Overload.fromConstructor(overload, nat.thisArg()));
|
||||||
|
}
|
||||||
|
for (var overload : clazz.getDeclaredMethods()) {
|
||||||
|
var constr = overload.getAnnotation(NativeConstructor.class);
|
||||||
|
if (constr == null) continue;
|
||||||
|
((OverloadFunction)func).add(Overload.fromMethod(overload, constr.thisArg()));
|
||||||
|
}
|
||||||
|
for (var overload : clazz.getDeclaredMethods()) {
|
||||||
|
var init = overload.getAnnotation(NativeInit.class);
|
||||||
|
if (init == null || init.value() != InitType.CONSTRUCTOR) continue;
|
||||||
|
try { overload.invoke(null, ctx, func); }
|
||||||
|
catch (ReflectiveOperationException e) { e.printStackTrace(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (((OverloadFunction)func).overloads.size() == 0) {
|
if (((OverloadFunction)func).overloads.size() == 0) {
|
||||||
func = new NativeFunction(clazz.getName(), (a, b, c) -> { throw EngineException.ofError("This constructor is not invokable."); });
|
func = new NativeFunction(clazz.getName(), (a, b, c) -> { throw EngineException.ofError("This constructor is not invokable."); });
|
||||||
}
|
}
|
||||||
|
|
||||||
applyMethods(false, func, clazz);
|
applyMethods(ctx, false, func, clazz);
|
||||||
applyFields(false, func, clazz);
|
applyFields(ctx, false, func, clazz);
|
||||||
applyClasses(false, func, clazz);
|
applyClasses(ctx, false, func, clazz);
|
||||||
|
|
||||||
func.special = true;
|
func.special = true;
|
||||||
|
|
||||||
@ -132,15 +169,22 @@ public class NativeTypeRegister implements WrappersProvider {
|
|||||||
/**
|
/**
|
||||||
* Generates a namespace for the given class.
|
* Generates a namespace for the given class.
|
||||||
* The returned function will have appropriate wrappers for all static members.
|
* The returned function will have appropriate wrappers for all static members.
|
||||||
* This method behaves almost like {@link NativeTypeRegister#makeConstructor}, but will return an object instead.
|
* This method behaves almost like {@link NativeWrapperProvider#makeConstructor}, but will return an object instead.
|
||||||
* @param clazz The class for which a constructor should be generated
|
* @param clazz The class for which a constructor should be generated
|
||||||
*/
|
*/
|
||||||
public static ObjectValue makeNamespace(Class<?> clazz) {
|
public static ObjectValue makeNamespace(Environment ctx, Class<?> clazz) {
|
||||||
ObjectValue res = new ObjectValue();
|
ObjectValue res = new ObjectValue();
|
||||||
|
|
||||||
applyMethods(false, res, clazz);
|
for (var overload : clazz.getDeclaredMethods()) {
|
||||||
applyFields(false, res, clazz);
|
var init = overload.getAnnotation(NativeInit.class);
|
||||||
applyClasses(false, res, clazz);
|
if (init == null || init.value() != InitType.NAMESPACE) continue;
|
||||||
|
try { overload.invoke(null, ctx, res); }
|
||||||
|
catch (ReflectiveOperationException e) { e.printStackTrace(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
applyMethods(ctx, false, res, clazz);
|
||||||
|
applyFields(ctx, false, res, clazz);
|
||||||
|
applyClasses(ctx, false, res, clazz);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -162,8 +206,8 @@ public class NativeTypeRegister implements WrappersProvider {
|
|||||||
clazz.isSynthetic()
|
clazz.isSynthetic()
|
||||||
) return;
|
) return;
|
||||||
|
|
||||||
if (constr == null) constr = makeConstructor(clazz);
|
if (constr == null) constr = makeConstructor(env, clazz);
|
||||||
if (proto == null) proto = makeProto(clazz);
|
if (proto == null) proto = makeProto(env, clazz);
|
||||||
|
|
||||||
proto.values.put("constructor", constr);
|
proto.values.put("constructor", constr);
|
||||||
constr.values.put("prototype", proto);
|
constr.values.put("prototype", proto);
|
||||||
@ -196,4 +240,8 @@ public class NativeTypeRegister implements WrappersProvider {
|
|||||||
public void setConstr(Class<?> clazz, FunctionValue value) {
|
public void setConstr(Class<?> clazz, FunctionValue value) {
|
||||||
constructors.put(clazz, value);
|
constructors.put(clazz, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NativeWrapperProvider(Environment env) {
|
||||||
|
this.env = env;
|
||||||
|
}
|
||||||
}
|
}
|
@ -17,28 +17,29 @@ public class Overload {
|
|||||||
|
|
||||||
public final OverloadRunner runner;
|
public final OverloadRunner runner;
|
||||||
public final boolean variadic;
|
public final boolean variadic;
|
||||||
|
public final boolean passThis;
|
||||||
public final Class<?> thisArg;
|
public final Class<?> thisArg;
|
||||||
public final Class<?>[] params;
|
public final Class<?>[] params;
|
||||||
|
|
||||||
public static Overload fromMethod(Method method) {
|
public static Overload fromMethod(Method method, boolean passThis) {
|
||||||
return new Overload(
|
return new Overload(
|
||||||
(ctx, th, args) -> method.invoke(th, args),
|
(ctx, th, args) -> method.invoke(th, args),
|
||||||
method.isVarArgs(),
|
method.isVarArgs(), passThis,
|
||||||
Modifier.isStatic(method.getModifiers()) ? null : method.getDeclaringClass(),
|
Modifier.isStatic(method.getModifiers()) ? null : method.getDeclaringClass(),
|
||||||
method.getParameterTypes()
|
method.getParameterTypes()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
public static Overload fromConstructor(Constructor<?> method) {
|
public static Overload fromConstructor(Constructor<?> method, boolean passThis) {
|
||||||
return new Overload(
|
return new Overload(
|
||||||
(ctx, th, args) -> method.newInstance(args),
|
(ctx, th, args) -> method.newInstance(args),
|
||||||
method.isVarArgs(),
|
method.isVarArgs(), passThis,
|
||||||
Modifier.isStatic(method.getModifiers()) ? null : method.getDeclaringClass(),
|
null,
|
||||||
method.getParameterTypes()
|
method.getParameterTypes()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
public static Overload getterFromField(Field field) {
|
public static Overload getterFromField(Field field) {
|
||||||
return new Overload(
|
return new Overload(
|
||||||
(ctx, th, args) -> field.get(th), false,
|
(ctx, th, args) -> field.get(th), false, false,
|
||||||
Modifier.isStatic(field.getModifiers()) ? null : field.getDeclaringClass(),
|
Modifier.isStatic(field.getModifiers()) ? null : field.getDeclaringClass(),
|
||||||
new Class[0]
|
new Class[0]
|
||||||
);
|
);
|
||||||
@ -46,23 +47,24 @@ public class Overload {
|
|||||||
public static Overload setterFromField(Field field) {
|
public static Overload setterFromField(Field field) {
|
||||||
if (Modifier.isFinal(field.getModifiers())) return null;
|
if (Modifier.isFinal(field.getModifiers())) return null;
|
||||||
return new Overload(
|
return new Overload(
|
||||||
(ctx, th, args) -> { field.set(th, args[0]); return null; }, false,
|
(ctx, th, args) -> { field.set(th, args[0]); return null; }, false, false,
|
||||||
Modifier.isStatic(field.getModifiers()) ? null : field.getDeclaringClass(),
|
Modifier.isStatic(field.getModifiers()) ? null : field.getDeclaringClass(),
|
||||||
new Class[0]
|
new Class[0]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Overload getter(Class<?> thisArg, OverloadRunner runner) {
|
public static Overload getter(Class<?> thisArg, OverloadRunner runner, boolean passThis) {
|
||||||
return new Overload(
|
return new Overload(
|
||||||
(ctx, th, args) -> runner.run(ctx, th, args), false,
|
(ctx, th, args) -> runner.run(ctx, th, args), false, passThis,
|
||||||
thisArg,
|
thisArg,
|
||||||
new Class[0]
|
new Class[0]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Overload(OverloadRunner runner, boolean variadic, Class<?> thisArg, Class<?> args[]) {
|
public Overload(OverloadRunner runner, boolean variadic, boolean passThis, Class<?> thisArg, Class<?> args[]) {
|
||||||
this.runner = runner;
|
this.runner = runner;
|
||||||
this.variadic = variadic;
|
this.variadic = variadic;
|
||||||
|
this.passThis = passThis;
|
||||||
this.thisArg = thisArg;
|
this.thisArg = thisArg;
|
||||||
this.params = args;
|
this.params = args;
|
||||||
}
|
}
|
||||||
|
@ -5,30 +5,38 @@ import java.lang.reflect.InvocationTargetException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
|
import me.topchetoeu.jscript.exceptions.ConvertException;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
|
||||||
public class OverloadFunction extends FunctionValue {
|
public class OverloadFunction extends FunctionValue {
|
||||||
public final List<Overload> overloads = new ArrayList<>();
|
public final List<Overload> overloads = new ArrayList<>();
|
||||||
|
|
||||||
public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
|
public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
|
||||||
for (var overload : overloads) {
|
loop: for (var overload : overloads) {
|
||||||
boolean consumesEngine = overload.params.length > 0 && overload.params[0] == Context.class;
|
|
||||||
int start = consumesEngine ? 1 : 0;
|
|
||||||
int end = overload.params.length - (overload.variadic ? 1 : 0);
|
|
||||||
|
|
||||||
Object[] newArgs = new Object[overload.params.length];
|
Object[] newArgs = new Object[overload.params.length];
|
||||||
|
|
||||||
|
boolean consumesEngine = overload.params.length > 0 && overload.params[0] == Context.class;
|
||||||
|
int start = (consumesEngine ? 1 : 0) + (overload.passThis ? 1 : 0);
|
||||||
|
int end = overload.params.length - (overload.variadic ? 1 : 0);
|
||||||
|
|
||||||
for (var i = start; i < end; i++) {
|
for (var i = start; i < end; i++) {
|
||||||
Object val;
|
Object val;
|
||||||
|
|
||||||
if (i - start >= args.length) val = null;
|
if (i - start >= args.length) val = null;
|
||||||
else val = args[i - start];
|
else val = args[i - start];
|
||||||
|
|
||||||
|
try {
|
||||||
newArgs[i] = Values.convert(ctx, val, overload.params[i]);
|
newArgs[i] = Values.convert(ctx, val, overload.params[i]);
|
||||||
}
|
}
|
||||||
|
catch (ConvertException e) {
|
||||||
|
if (overloads.size() > 1) continue loop;
|
||||||
|
else throw EngineException.ofType(String.format("Argument %d can't be converted from %s to %s", i - start, e.source, e.target));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (overload.variadic) {
|
if (overload.variadic) {
|
||||||
var type = overload.params[overload.params.length - 1].getComponentType();
|
var type = overload.params[overload.params.length - 1].getComponentType();
|
||||||
@ -36,15 +44,34 @@ public class OverloadFunction extends FunctionValue {
|
|||||||
Object varArg = Array.newInstance(type, n);
|
Object varArg = Array.newInstance(type, n);
|
||||||
|
|
||||||
for (var i = 0; i < n; i++) {
|
for (var i = 0; i < n; i++) {
|
||||||
|
try {
|
||||||
Array.set(varArg, i, Values.convert(ctx, args[i + end - start], type));
|
Array.set(varArg, i, Values.convert(ctx, args[i + end - start], type));
|
||||||
}
|
}
|
||||||
|
catch (ConvertException e) {
|
||||||
|
if (overloads.size() > 1) continue loop;
|
||||||
|
else throw EngineException.ofType(String.format("Element in variadic argument can't be converted from %s to %s", e.source, e.target));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
newArgs[newArgs.length - 1] = varArg;
|
newArgs[newArgs.length - 1] = varArg;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (consumesEngine) newArgs[0] = ctx;
|
var thisArgType = overload.passThis ? overload.params[consumesEngine ? 1 : 0] : overload.thisArg;
|
||||||
|
Object _this;
|
||||||
|
|
||||||
Object _this = overload.thisArg == null ? null : Values.convert(ctx, thisArg, overload.thisArg);
|
try {
|
||||||
|
_this = thisArgType == null ? null : Values.convert(ctx, thisArg, thisArgType);
|
||||||
|
}
|
||||||
|
catch (ConvertException e) {
|
||||||
|
if (overloads.size() > 1) continue loop;
|
||||||
|
else throw EngineException.ofType(String.format("This argument can't be converted from %s to %s", e.source, e.target));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (consumesEngine) newArgs[0] = ctx;
|
||||||
|
if (overload.passThis) {
|
||||||
|
newArgs[consumesEngine ? 1 : 0] = _this;
|
||||||
|
_this = null;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return Values.normalize(ctx, overload.runner.run(ctx, _this, newArgs));
|
return Values.normalize(ctx, overload.runner.run(ctx, _this, newArgs));
|
||||||
@ -56,15 +83,19 @@ public class OverloadFunction extends FunctionValue {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
catch (InvocationTargetException e) {
|
catch (InvocationTargetException e) {
|
||||||
|
var loc = new Location(0, 0, "<internal>");
|
||||||
if (e.getTargetException() instanceof EngineException) {
|
if (e.getTargetException() instanceof EngineException) {
|
||||||
throw ((EngineException)e.getTargetException());
|
throw ((EngineException)e.getTargetException()).add(name, loc);
|
||||||
|
}
|
||||||
|
else if (e.getTargetException() instanceof NullPointerException) {
|
||||||
|
throw EngineException.ofType("Unexpected value of 'undefined'.").add(name, loc);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw EngineException.ofError(e.getTargetException().getMessage());
|
throw EngineException.ofError(e.getTargetException().getMessage()).add(name, loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ReflectiveOperationException e) {
|
catch (ReflectiveOperationException e) {
|
||||||
throw EngineException.ofError(e.getMessage());
|
throw EngineException.ofError(e.getMessage()).add(name, new Location(0, 0, "<internal>"));
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
throw e;
|
throw e;
|
||||||
@ -82,4 +113,9 @@ public class OverloadFunction extends FunctionValue {
|
|||||||
public OverloadFunction(String name) {
|
public OverloadFunction(String name) {
|
||||||
super(name, 0);
|
super(name, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static OverloadFunction of(String name, Overload overload) {
|
||||||
|
if (overload == null) return null;
|
||||||
|
else return new OverloadFunction(name).add(overload);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,6 +244,7 @@ interface ArrayConstructor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Boolean {
|
interface Boolean {
|
||||||
|
toString(): string;
|
||||||
valueOf(): boolean;
|
valueOf(): boolean;
|
||||||
}
|
}
|
||||||
interface BooleanConstructor {
|
interface BooleanConstructor {
|
||||||
@ -349,7 +350,7 @@ interface Object {
|
|||||||
toString(): string;
|
toString(): string;
|
||||||
hasOwnProperty(key: any): boolean;
|
hasOwnProperty(key: any): boolean;
|
||||||
}
|
}
|
||||||
interface ObjectConstructor extends Function {
|
interface ObjectConstructor {
|
||||||
(arg: string): String;
|
(arg: string): String;
|
||||||
(arg: number): Number;
|
(arg: number): Number;
|
||||||
(arg: boolean): Boolean;
|
(arg: boolean): Boolean;
|
@ -13,7 +13,7 @@ import me.topchetoeu.jscript.compilation.VariableDeclareStatement.Pair;
|
|||||||
import me.topchetoeu.jscript.compilation.control.*;
|
import me.topchetoeu.jscript.compilation.control.*;
|
||||||
import me.topchetoeu.jscript.compilation.control.SwitchStatement.SwitchCase;
|
import me.topchetoeu.jscript.compilation.control.SwitchStatement.SwitchCase;
|
||||||
import me.topchetoeu.jscript.compilation.values.*;
|
import me.topchetoeu.jscript.compilation.values.*;
|
||||||
import me.topchetoeu.jscript.engine.FunctionContext;
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
||||||
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
||||||
@ -516,12 +516,59 @@ public class Parsing {
|
|||||||
|
|
||||||
for (int i = 2; i < literal.length(); i++) {
|
for (int i = 2; i < literal.length(); i++) {
|
||||||
res *= 16;
|
res *= 16;
|
||||||
res += fromHex(literal.charAt(i));
|
int dig = fromHex(literal.charAt(i));
|
||||||
|
res += dig;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Double parseNumber(boolean octals, String value) {
|
||||||
|
if (value.startsWith("0x") || value.startsWith("0X")) {
|
||||||
|
if (value.length() == 2) return null;
|
||||||
|
return parseHex(value);
|
||||||
|
}
|
||||||
|
if (value.endsWith("e") || value.endsWith("E") || value.endsWith("-")) return null;
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
double res = 0, dotDivisor = 1;
|
||||||
|
boolean e = false, dot = false;
|
||||||
|
int exponent = 0;
|
||||||
|
|
||||||
|
for (; i < value.length(); i++) {
|
||||||
|
char c = value.charAt(i);
|
||||||
|
if (c == '.') { dot = true; break; }
|
||||||
|
if (c == 'e') { e = true; break; }
|
||||||
|
if (!isDigit(c)) break;
|
||||||
|
|
||||||
|
res = res * 10 + c - '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dot) for (i++; i < value.length(); i++) {
|
||||||
|
char c = value.charAt(i);
|
||||||
|
if (c == 'e') { e = true; break; }
|
||||||
|
if (!isDigit(c)) break;
|
||||||
|
|
||||||
|
res += (c - '0') / (dotDivisor *= 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e) for (i++; i < value.length(); i++) {
|
||||||
|
char c = value.charAt(i);
|
||||||
|
if (!isDigit(c)) break;
|
||||||
|
exponent = 10 * exponent + c - '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exponent < 0) for (int j = 0; j < -exponent; j++) res /= 10;
|
||||||
|
else for (int j = 0; j < exponent; j++) res *= 10;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
private static double parseNumber(Location loc, String value) {
|
||||||
|
var res = parseNumber(false, value);
|
||||||
|
if (res == null) throw new SyntaxException(loc, "Invalid number format.");
|
||||||
|
else return res;
|
||||||
|
}
|
||||||
|
|
||||||
private static List<Token> parseTokens(String filename, Collection<RawToken> tokens) {
|
private static List<Token> parseTokens(String filename, Collection<RawToken> tokens) {
|
||||||
var res = new ArrayList<Token>();
|
var res = new ArrayList<Token>();
|
||||||
|
|
||||||
@ -529,28 +576,14 @@ public class Parsing {
|
|||||||
var loc = new Location(el.line, el.start, filename);
|
var loc = new Location(el.line, el.start, filename);
|
||||||
switch (el.type) {
|
switch (el.type) {
|
||||||
case LITERAL: res.add(Token.identifier(el.line, el.start, el.value)); break;
|
case LITERAL: res.add(Token.identifier(el.line, el.start, el.value)); break;
|
||||||
case NUMBER:
|
case NUMBER: res.add(Token.number(el.line, el.start, parseNumber(loc, el.value))); break;
|
||||||
if (el.value.startsWith("0x") || el.value.startsWith("0X")) {
|
case STRING: res.add(Token.string(el.line, el.start, parseString(loc, el.value))); break;
|
||||||
if (el.value.endsWith("x") || el.value.endsWith("X")) {
|
case REGEX: res.add(Token.regex(el.line, el.start, parseRegex(loc, el.value))); break;
|
||||||
throw new SyntaxException(loc, "Invalid number format.");
|
|
||||||
}
|
|
||||||
res.add(Token.number(el.line, el.start, parseHex(el.value))); break;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
el.value.endsWith("e") || el.value.endsWith("E") || el.value.endsWith("-")
|
|
||||||
) throw new SyntaxException(loc, "Invalid number format.");
|
|
||||||
else res.add(Token.number(el.line, el.start, Double.parseDouble(el.value))); break;
|
|
||||||
case OPERATOR:
|
case OPERATOR:
|
||||||
Operator op = Operator.parse(el.value);
|
Operator op = Operator.parse(el.value);
|
||||||
if (op == null) throw new SyntaxException(loc, String.format("Unrecognized operator '%s'.", el.value));
|
if (op == null) throw new SyntaxException(loc, String.format("Unrecognized operator '%s'.", el.value));
|
||||||
res.add(Token.operator(el.line, el.start, op));
|
res.add(Token.operator(el.line, el.start, op));
|
||||||
break;
|
break;
|
||||||
case STRING:
|
|
||||||
res.add(Token.string(el.line, el.start, parseString(loc, el.value)));
|
|
||||||
break;
|
|
||||||
case REGEX:
|
|
||||||
res.add(Token.regex(el.line, el.start, parseRegex(loc, el.value)));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -652,7 +685,7 @@ public class Parsing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
public static ParseRes<NewStatement> parseRegex(String filename, List<Token> tokens, int i) {
|
public static ParseRes<RegexStatement> parseRegex(String filename, List<Token> tokens, int i) {
|
||||||
var loc = getLoc(filename, tokens, i);
|
var loc = getLoc(filename, tokens, i);
|
||||||
try {
|
try {
|
||||||
if (tokens.get(i).isRegex()) {
|
if (tokens.get(i).isRegex()) {
|
||||||
@ -660,11 +693,7 @@ public class Parsing {
|
|||||||
var index = val.lastIndexOf('/');
|
var index = val.lastIndexOf('/');
|
||||||
var first = val.substring(1, index);
|
var first = val.substring(1, index);
|
||||||
var second = val.substring(index + 1);
|
var second = val.substring(index + 1);
|
||||||
return ParseRes.res(new NewStatement(loc,
|
return ParseRes.res(new RegexStatement(loc, first, second), 1);
|
||||||
new VariableStatement(null, "RegExp"),
|
|
||||||
new ConstantStatement(loc, first),
|
|
||||||
new ConstantStatement(loc, second)
|
|
||||||
), 1);
|
|
||||||
}
|
}
|
||||||
else return ParseRes.failed();
|
else return ParseRes.failed();
|
||||||
}
|
}
|
||||||
@ -1842,7 +1871,7 @@ public class Parsing {
|
|||||||
return list.toArray(Statement[]::new);
|
return list.toArray(Statement[]::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CodeFunction compile(FunctionContext environment, Statement ...statements) {
|
public static CodeFunction compile(Environment environment, Statement ...statements) {
|
||||||
var target = environment.global.globalChild();
|
var target = environment.global.globalChild();
|
||||||
var subscope = target.child();
|
var subscope = target.child();
|
||||||
var res = new ArrayList<Instruction>();
|
var res = new ArrayList<Instruction>();
|
||||||
@ -1872,7 +1901,7 @@ public class Parsing {
|
|||||||
|
|
||||||
return new CodeFunction(environment, "", subscope.localsCount(), 0, new ValueVariable[0], res.toArray(Instruction[]::new));
|
return new CodeFunction(environment, "", subscope.localsCount(), 0, new ValueVariable[0], res.toArray(Instruction[]::new));
|
||||||
}
|
}
|
||||||
public static CodeFunction compile(FunctionContext environment, String filename, String raw) {
|
public static CodeFunction compile(Environment environment, String filename, String raw) {
|
||||||
try {
|
try {
|
||||||
return compile(environment, parse(filename, raw));
|
return compile(environment, parse(filename, raw));
|
||||||
}
|
}
|
||||||
|
357
src/me/topchetoeu/jscript/polyfills/ArrayPolyfill.java
Normal file
357
src/me/topchetoeu/jscript/polyfills/ArrayPolyfill.java
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
|
import me.topchetoeu.jscript.interop.InitType;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeSetter;
|
||||||
|
|
||||||
|
public class ArrayPolyfill {
|
||||||
|
@NativeGetter(thisArg = true) public static int length(Context ctx, ArrayValue thisArg) throws InterruptedException {
|
||||||
|
return thisArg.size();
|
||||||
|
}
|
||||||
|
@NativeSetter(thisArg = true) public static void length(Context ctx, ArrayValue thisArg, int len) throws InterruptedException {
|
||||||
|
thisArg.setSize(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static ObjectValue values(Context ctx, ArrayValue thisArg) throws InterruptedException {
|
||||||
|
return Values.fromJavaIterable(ctx, thisArg);
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static ObjectValue keys(Context ctx, ArrayValue thisArg) throws InterruptedException {
|
||||||
|
return Values.fromJavaIterable(ctx, () -> new Iterator<Object>() {
|
||||||
|
private int i = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return i < thisArg.size();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Object next() {
|
||||||
|
if (!hasNext()) return null;
|
||||||
|
return i++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static ObjectValue entries(Context ctx, ArrayValue thisArg) throws InterruptedException {
|
||||||
|
return Values.fromJavaIterable(ctx, () -> new Iterator<Object>() {
|
||||||
|
private int i = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return i < thisArg.size();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Object next() {
|
||||||
|
if (!hasNext()) return null;
|
||||||
|
return new ArrayValue(ctx, i, thisArg.get(i++));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(value = "@@Symbol.iterator", thisArg = true)
|
||||||
|
public static ObjectValue iterator(Context ctx, ArrayValue thisArg) throws InterruptedException {
|
||||||
|
return values(ctx, thisArg);
|
||||||
|
}
|
||||||
|
@Native(value = "@@Symbol.asyncIterator", thisArg = true)
|
||||||
|
public static ObjectValue asyncIterator(Context ctx, ArrayValue thisArg) throws InterruptedException {
|
||||||
|
return values(ctx, thisArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static ArrayValue concat(Context ctx, ArrayValue thisArg, Object ...others) throws InterruptedException {
|
||||||
|
// TODO: Fully implement with non-array spreadable objects
|
||||||
|
var size = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < others.length; i++) {
|
||||||
|
if (others[i] instanceof ArrayValue) size += ((ArrayValue)others[i]).size();
|
||||||
|
else i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
var res = new ArrayValue(size);
|
||||||
|
|
||||||
|
for (int i = 0, j = 0; i < others.length; i++) {
|
||||||
|
if (others[i] instanceof ArrayValue) {
|
||||||
|
int n = ((ArrayValue)others[i]).size();
|
||||||
|
((ArrayValue)others[i]).copyTo(ctx, res, 0, j, n);
|
||||||
|
j += n;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.set(ctx, j++, others[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static void sort(Context ctx, ArrayValue arr, FunctionValue cmp) throws InterruptedException {
|
||||||
|
try {
|
||||||
|
arr.sort((a, b) -> {
|
||||||
|
try {
|
||||||
|
var res = Values.toNumber(ctx, cmp.call(ctx, null, a, b));
|
||||||
|
if (res < 0) return -1;
|
||||||
|
if (res > 0) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (RuntimeException e) {
|
||||||
|
if (e.getCause() instanceof InterruptedException) throw (InterruptedException)e.getCause();
|
||||||
|
else throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int normalizeI(int len, int i, boolean clamp) {
|
||||||
|
if (i < 0) i += len;
|
||||||
|
if (clamp) {
|
||||||
|
if (i < 0) i = 0;
|
||||||
|
if (i >= len) i = len;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static ArrayValue fill(Context ctx, ArrayValue arr, Object val, int start, int end) throws InterruptedException {
|
||||||
|
start = normalizeI(arr.size(), start, true);
|
||||||
|
end = normalizeI(arr.size(), end, true);
|
||||||
|
|
||||||
|
for (; start < end; start++) {
|
||||||
|
arr.set(ctx, start, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static ArrayValue fill(Context ctx, ArrayValue arr, Object val, int start) throws InterruptedException {
|
||||||
|
return fill(ctx, arr, val, start, arr.size());
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static ArrayValue fill(Context ctx, ArrayValue arr, Object val) throws InterruptedException {
|
||||||
|
return fill(ctx, arr, val, 0, arr.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static boolean every(Context ctx, ArrayValue arr, FunctionValue func, Object thisArg) throws InterruptedException {
|
||||||
|
for (var i = 0; i < arr.size(); i++) {
|
||||||
|
if (!Values.toBoolean(func.call(ctx, thisArg, arr.get(i), i, arr))) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static boolean some(Context ctx, ArrayValue arr, FunctionValue func, Object thisArg) throws InterruptedException {
|
||||||
|
for (var i = 0; i < arr.size(); i++) {
|
||||||
|
if (Values.toBoolean(func.call(ctx, thisArg, arr.get(i), i, arr))) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static ArrayValue filter(Context ctx, ArrayValue arr, FunctionValue func, Object thisArg) throws InterruptedException {
|
||||||
|
var res = new ArrayValue(arr.size());
|
||||||
|
|
||||||
|
for (int i = 0, j = 0; i < arr.size(); i++) {
|
||||||
|
if (arr.has(i) && Values.toBoolean(func.call(ctx, thisArg, arr.get(i), i, arr))) res.set(ctx, j++, arr.get(i));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static ArrayValue map(Context ctx, ArrayValue arr, FunctionValue func, Object thisArg) throws InterruptedException {
|
||||||
|
var res = new ArrayValue(arr.size());
|
||||||
|
for (int i = 0, j = 0; i < arr.size(); i++) {
|
||||||
|
if (arr.has(i)) res.set(ctx, j++, func.call(ctx, thisArg, arr.get(i), i, arr));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static void forEach(Context ctx, ArrayValue arr, FunctionValue func, Object thisArg) throws InterruptedException {
|
||||||
|
for (int i = 0; i < arr.size(); i++) {
|
||||||
|
if (arr.has(i)) func.call(ctx, thisArg, arr.get(i), i, arr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static ArrayValue flat(Context ctx, ArrayValue arr, int depth) throws InterruptedException {
|
||||||
|
var res = new ArrayValue(arr.size());
|
||||||
|
var stack = new Stack<Object>();
|
||||||
|
var depths = new Stack<Integer>();
|
||||||
|
|
||||||
|
stack.push(arr);
|
||||||
|
depths.push(-1);
|
||||||
|
|
||||||
|
while (!stack.empty()) {
|
||||||
|
var el = stack.pop();
|
||||||
|
int d = depths.pop();
|
||||||
|
|
||||||
|
if (d <= depth && el instanceof ArrayValue) {
|
||||||
|
for (int i = ((ArrayValue)el).size() - 1; i >= 0; i--) {
|
||||||
|
stack.push(((ArrayValue)el).get(i));
|
||||||
|
depths.push(d + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else res.set(ctx, depth, arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static ArrayValue flatMap(Context ctx, ArrayValue arr, FunctionValue cmp, Object thisArg) throws InterruptedException {
|
||||||
|
return flat(ctx, map(ctx, arr, cmp, thisArg), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static Object find(Context ctx, ArrayValue arr, FunctionValue cmp, Object thisArg) throws InterruptedException {
|
||||||
|
for (int i = 0; i < arr.size(); i++) {
|
||||||
|
if (arr.has(i) && Values.toBoolean(cmp.call(ctx, thisArg, arr.get(i), i, arr))) return arr.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static Object findLast(Context ctx, ArrayValue arr, FunctionValue cmp, Object thisArg) throws InterruptedException {
|
||||||
|
for (var i = arr.size() - 1; i >= 0; i--) {
|
||||||
|
if (arr.has(i) && Values.toBoolean(cmp.call(ctx, thisArg, arr.get(i), i, arr))) return arr.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static int findIndex(Context ctx, ArrayValue arr, FunctionValue cmp, Object thisArg) throws InterruptedException {
|
||||||
|
for (int i = 0; i < arr.size(); i++) {
|
||||||
|
if (arr.has(i) && Values.toBoolean(cmp.call(ctx, thisArg, arr.get(i), i, arr))) return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static int findLastIndex(Context ctx, ArrayValue arr, FunctionValue cmp, Object thisArg) throws InterruptedException {
|
||||||
|
for (var i = arr.size() - 1; i >= 0; i--) {
|
||||||
|
if (arr.has(i) && Values.toBoolean(cmp.call(ctx, thisArg, arr.get(i), i, arr))) return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static int indexOf(Context ctx, ArrayValue arr, Object val, int start) throws InterruptedException {
|
||||||
|
start = normalizeI(arr.size(), start, true);
|
||||||
|
|
||||||
|
for (int i = 0; i < arr.size() && i < start; i++) {
|
||||||
|
if (Values.strictEquals(ctx, arr.get(i), val)) return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static int lastIndexOf(Context ctx, ArrayValue arr, Object val, int start) throws InterruptedException {
|
||||||
|
start = normalizeI(arr.size(), start, true);
|
||||||
|
|
||||||
|
for (int i = arr.size(); i >= start; i--) {
|
||||||
|
if (Values.strictEquals(ctx, arr.get(i), val)) return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static boolean includes(Context ctx, ArrayValue arr, Object el, int start) throws InterruptedException {
|
||||||
|
return indexOf(ctx, arr, el, start) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static Object pop(Context ctx, ArrayValue arr) throws InterruptedException {
|
||||||
|
if (arr.size() == 0) return null;
|
||||||
|
var val = arr.get(arr.size() - 1);
|
||||||
|
arr.shrink(1);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static int push(Context ctx, ArrayValue arr, Object ...values) throws InterruptedException {
|
||||||
|
arr.copyFrom(ctx, values, 0, arr.size(), values.length);
|
||||||
|
return arr.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static Object shift(Context ctx, ArrayValue arr) throws InterruptedException {
|
||||||
|
if (arr.size() == 0) return null;
|
||||||
|
var val = arr.get(0);
|
||||||
|
arr.move(1, 0, arr.size());
|
||||||
|
arr.shrink(1);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static int unshift(Context ctx, ArrayValue arr, Object ...values) throws InterruptedException {
|
||||||
|
arr.move(0, values.length, arr.size());
|
||||||
|
arr.copyFrom(ctx, values, 0, 0, values.length);
|
||||||
|
return arr.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static ArrayValue slice(Context ctx, ArrayValue arr, int start, int end) {
|
||||||
|
start = normalizeI(arr.size(), start, true);
|
||||||
|
end = normalizeI(arr.size(), end, true);
|
||||||
|
|
||||||
|
var res = new ArrayValue(end - start);
|
||||||
|
arr.copyTo(ctx, res, start, 0, end - start);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static ArrayValue slice(Context ctx, ArrayValue arr, int start) {
|
||||||
|
return slice(ctx, arr, start, arr.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static ArrayValue splice(Context ctx, ArrayValue arr, int start, int deleteCount, Object ...items) throws InterruptedException {
|
||||||
|
start = normalizeI(arr.size(), start, true);
|
||||||
|
deleteCount = normalizeI(arr.size(), deleteCount, true);
|
||||||
|
if (start + deleteCount >= arr.size()) deleteCount = arr.size() - start;
|
||||||
|
|
||||||
|
var size = arr.size() - deleteCount + items.length;
|
||||||
|
var res = new ArrayValue(deleteCount);
|
||||||
|
arr.copyTo(ctx, res, start, 0, deleteCount);
|
||||||
|
arr.move(start + deleteCount, start + items.length, arr.size() - start - deleteCount);
|
||||||
|
arr.copyFrom(ctx, items, 0, start, items.length);
|
||||||
|
arr.setSize(size);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static ArrayValue splice(Context ctx, ArrayValue arr, int start) throws InterruptedException {
|
||||||
|
return splice(ctx, arr, start, arr.size() - start);
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static String toString(Context ctx, ArrayValue arr) throws InterruptedException {
|
||||||
|
return join(ctx, arr, ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static String join(Context ctx, ArrayValue arr, String sep) throws InterruptedException {
|
||||||
|
var res = new StringBuilder();
|
||||||
|
var comma = true;
|
||||||
|
|
||||||
|
for (int i = 0; i < arr.size(); i++) {
|
||||||
|
if (!arr.has(i)) continue;
|
||||||
|
if (comma) res.append(sep);
|
||||||
|
comma = false;
|
||||||
|
var el = arr.get(i);
|
||||||
|
if (el == null || el == Values.NULL) continue;
|
||||||
|
|
||||||
|
res.append(Values.toString(ctx, el));
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public static boolean isArray(Context ctx, Object val) { return val instanceof ArrayValue; }
|
||||||
|
@Native public static ArrayValue of(Context ctx, Object... args) {
|
||||||
|
var res = new ArrayValue(args.length);
|
||||||
|
res.copyFrom(ctx, args, 0, 0, args.length);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeConstructor public static ArrayValue constructor(Context ctx, Object... args) {
|
||||||
|
ArrayValue res;
|
||||||
|
|
||||||
|
if (args.length == 1 && args[0] instanceof Number) {
|
||||||
|
int len = ((Number)args[0]).intValue();
|
||||||
|
res = new ArrayValue(len);
|
||||||
|
res.setSize(len);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res = new ArrayValue(args.length);
|
||||||
|
res.copyFrom(ctx, args, 0, 0, args.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeInit(InitType.PROTOTYPE) public static void init(Environment env, ObjectValue target) {
|
||||||
|
target.defineProperty(null, env.symbol("Symbol.typeName"), "Array");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
|
import me.topchetoeu.jscript.engine.frame.Runners;
|
||||||
|
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
||||||
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
|
||||||
|
public class AsyncFunctionPolyfill extends FunctionValue {
|
||||||
|
public final FunctionValue factory;
|
||||||
|
|
||||||
|
public static class AsyncHelper {
|
||||||
|
public PromisePolyfill promise = new PromisePolyfill();
|
||||||
|
public CodeFrame frame;
|
||||||
|
|
||||||
|
private boolean awaiting = false;
|
||||||
|
|
||||||
|
private void next(Context ctx, Object inducedValue, Object inducedError) throws InterruptedException {
|
||||||
|
Object res = null;
|
||||||
|
ctx.message.pushFrame(ctx, frame);
|
||||||
|
|
||||||
|
awaiting = false;
|
||||||
|
while (!awaiting) {
|
||||||
|
try {
|
||||||
|
res = frame.next(ctx, inducedValue, Runners.NO_RETURN, inducedError == Runners.NO_RETURN ? null : new EngineException(inducedError));
|
||||||
|
inducedValue = inducedError = Runners.NO_RETURN;
|
||||||
|
if (res != Runners.NO_RETURN) {
|
||||||
|
promise.fulfill(ctx, res);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (EngineException e) {
|
||||||
|
promise.reject(ctx, e.value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.message.popFrame(frame);
|
||||||
|
|
||||||
|
if (awaiting) {
|
||||||
|
PromisePolyfill.then(ctx, frame.pop(), new NativeFunction(this::fulfill), new NativeFunction(this::reject));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object fulfill(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
|
||||||
|
next(ctx, args.length > 0 ? args[0] : null, Runners.NO_RETURN);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public Object reject(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
|
||||||
|
next(ctx, Runners.NO_RETURN, args.length > 0 ? args[0] : null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object await(Context ctx, Object thisArg, Object[] args) {
|
||||||
|
this.awaiting = true;
|
||||||
|
return args.length > 0 ? args[0] : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
|
||||||
|
var handler = new AsyncHelper();
|
||||||
|
var func = factory.call(ctx, thisArg, new NativeFunction("await", handler::await));
|
||||||
|
if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function.");
|
||||||
|
handler.frame = new CodeFrame(ctx, thisArg, args, (CodeFunction)func);
|
||||||
|
handler.next(ctx, Runners.NO_RETURN, Runners.NO_RETURN);
|
||||||
|
return handler.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncFunctionPolyfill(FunctionValue factory) {
|
||||||
|
super(factory.name, factory.length);
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
}
|
134
src/me/topchetoeu/jscript/polyfills/AsyncGeneratorPolyfill.java
Normal file
134
src/me/topchetoeu/jscript/polyfills/AsyncGeneratorPolyfill.java
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
|
import me.topchetoeu.jscript.engine.frame.Runners;
|
||||||
|
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class AsyncGeneratorPolyfill extends FunctionValue {
|
||||||
|
public final FunctionValue factory;
|
||||||
|
|
||||||
|
public static class AsyncGenerator {
|
||||||
|
@Native("@@Symbol.typeName") public final String name = "AsyncGenerator";
|
||||||
|
private int state = 0;
|
||||||
|
private boolean done = false;
|
||||||
|
private PromisePolyfill currPromise;
|
||||||
|
public CodeFrame frame;
|
||||||
|
|
||||||
|
private void next(Context ctx, Object inducedValue, Object inducedReturn, Object inducedError) throws InterruptedException {
|
||||||
|
if (done) {
|
||||||
|
if (inducedError != Runners.NO_RETURN)
|
||||||
|
throw new EngineException(inducedError);
|
||||||
|
currPromise.fulfill(ctx, new ObjectValue(ctx, Map.of(
|
||||||
|
"done", true,
|
||||||
|
"value", inducedReturn == Runners.NO_RETURN ? null : inducedReturn
|
||||||
|
)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object res = null;
|
||||||
|
ctx.message.pushFrame(ctx, frame);
|
||||||
|
state = 0;
|
||||||
|
|
||||||
|
while (state == 0) {
|
||||||
|
try {
|
||||||
|
res = frame.next(ctx, inducedValue, inducedReturn, inducedError == Runners.NO_RETURN ? null : new EngineException(inducedError));
|
||||||
|
inducedValue = inducedReturn = inducedError = Runners.NO_RETURN;
|
||||||
|
if (res != Runners.NO_RETURN) {
|
||||||
|
var obj = new ObjectValue();
|
||||||
|
obj.defineProperty(ctx, "done", true);
|
||||||
|
obj.defineProperty(ctx, "value", res);
|
||||||
|
currPromise.fulfill(ctx, obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (EngineException e) {
|
||||||
|
currPromise.reject(ctx, e.value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.message.popFrame(frame);
|
||||||
|
|
||||||
|
if (state == 1) {
|
||||||
|
PromisePolyfill.then(ctx, frame.pop(), new NativeFunction(this::fulfill), new NativeFunction(this::reject));
|
||||||
|
}
|
||||||
|
else if (state == 2) {
|
||||||
|
var obj = new ObjectValue();
|
||||||
|
obj.defineProperty(ctx, "done", false);
|
||||||
|
obj.defineProperty(ctx, "value", frame.pop());
|
||||||
|
currPromise.fulfill(ctx, obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (done) return "Generator [closed]";
|
||||||
|
if (state != 0) return "Generator [suspended]";
|
||||||
|
return "Generator [running]";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object fulfill(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
|
||||||
|
next(ctx, args.length > 0 ? args[0] : null, Runners.NO_RETURN, Runners.NO_RETURN);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public Object reject(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
|
||||||
|
next(ctx, Runners.NO_RETURN, args.length > 0 ? args[0] : null, Runners.NO_RETURN);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native
|
||||||
|
public PromisePolyfill next(Context ctx, Object ...args) throws InterruptedException {
|
||||||
|
this.currPromise = new PromisePolyfill();
|
||||||
|
if (args.length == 0) next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, Runners.NO_RETURN);
|
||||||
|
else next(ctx, args[0], Runners.NO_RETURN, Runners.NO_RETURN);
|
||||||
|
return this.currPromise;
|
||||||
|
}
|
||||||
|
@Native("throw")
|
||||||
|
public PromisePolyfill _throw(Context ctx, Object error) throws InterruptedException {
|
||||||
|
this.currPromise = new PromisePolyfill();
|
||||||
|
next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, error);
|
||||||
|
return this.currPromise;
|
||||||
|
}
|
||||||
|
@Native("return")
|
||||||
|
public PromisePolyfill _return(Context ctx, Object value) throws InterruptedException {
|
||||||
|
this.currPromise = new PromisePolyfill();
|
||||||
|
next(ctx, Runners.NO_RETURN, value, Runners.NO_RETURN);
|
||||||
|
return this.currPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Object await(Context ctx, Object thisArg, Object[] args) {
|
||||||
|
this.state = 1;
|
||||||
|
return args.length > 0 ? args[0] : null;
|
||||||
|
}
|
||||||
|
public Object yield(Context ctx, Object thisArg, Object[] args) {
|
||||||
|
this.state = 2;
|
||||||
|
return args.length > 0 ? args[0] : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
|
||||||
|
var handler = new AsyncGenerator();
|
||||||
|
var func = factory.call(ctx, thisArg,
|
||||||
|
new NativeFunction("await", handler::await),
|
||||||
|
new NativeFunction("yield", handler::yield)
|
||||||
|
);
|
||||||
|
if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function.");
|
||||||
|
handler.frame = new CodeFrame(ctx, thisArg, args, (CodeFunction)func);
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncGeneratorPolyfill(FunctionValue factory) {
|
||||||
|
super(factory.name, factory.length);
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
}
|
36
src/me/topchetoeu/jscript/polyfills/BooleanPolyfill.java
Normal file
36
src/me/topchetoeu/jscript/polyfills/BooleanPolyfill.java
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
|
import me.topchetoeu.jscript.interop.InitType;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
|
public class BooleanPolyfill {
|
||||||
|
public static final BooleanPolyfill TRUE = new BooleanPolyfill(true);
|
||||||
|
public static final BooleanPolyfill FALSE = new BooleanPolyfill(false);
|
||||||
|
|
||||||
|
public final boolean value;
|
||||||
|
|
||||||
|
@NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object val) {
|
||||||
|
val = Values.toBoolean(val);
|
||||||
|
if (thisArg instanceof ObjectValue) return (boolean)val ? TRUE : FALSE;
|
||||||
|
else return val;
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static String toString(Context ctx, Object thisArg) {
|
||||||
|
return Values.toBoolean(thisArg) ? "true" : "false";
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static boolean valueOf(Context ctx, Object thisArg) {
|
||||||
|
return Values.toBoolean(thisArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanPolyfill(boolean val) {
|
||||||
|
this.value = val;
|
||||||
|
}
|
||||||
|
@NativeInit(InitType.PROTOTYPE) public static void init(Environment env, ObjectValue target) {
|
||||||
|
target.defineProperty(null, env.symbol("Symbol.typeName"), "Boolean");
|
||||||
|
}
|
||||||
|
}
|
67
src/me/topchetoeu/jscript/polyfills/ErrorPolyfill.java
Normal file
67
src/me/topchetoeu/jscript/polyfills/ErrorPolyfill.java
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
|
import me.topchetoeu.jscript.interop.InitType;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
|
public class ErrorPolyfill {
|
||||||
|
private static String toString(Context ctx, Object cause, Object name, Object message, ArrayValue stack) throws InterruptedException {
|
||||||
|
if (name == null) name = "";
|
||||||
|
else name = Values.toString(ctx, name).trim();
|
||||||
|
if (message == null) message = "";
|
||||||
|
else message = Values.toString(ctx, message).trim();
|
||||||
|
StringBuilder res = new StringBuilder();
|
||||||
|
|
||||||
|
if (!name.equals("")) res.append(name);
|
||||||
|
if (!message.equals("") && !name.equals("")) res.append(": ");
|
||||||
|
if (!message.equals("")) res.append(message);
|
||||||
|
|
||||||
|
if (stack != null) {
|
||||||
|
for (var el : stack) {
|
||||||
|
var str = Values.toString(ctx, el).trim();
|
||||||
|
if (!str.equals("")) res.append("\n ").append(el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cause instanceof ObjectValue) res.append(toString(ctx, cause));
|
||||||
|
|
||||||
|
return res.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static String toString(Context ctx, Object thisArg) throws InterruptedException {
|
||||||
|
if (thisArg instanceof ObjectValue) {
|
||||||
|
var stack = Values.getMember(ctx, thisArg, "stack");
|
||||||
|
if (!(stack instanceof ArrayValue)) stack = null;
|
||||||
|
return toString(ctx,
|
||||||
|
Values.getMember(ctx, thisArg, "cause"),
|
||||||
|
Values.getMember(ctx, thisArg, "name"),
|
||||||
|
Values.getMember(ctx, thisArg, "message"),
|
||||||
|
(ArrayValue)stack
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else return "[Invalid error]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) throws InterruptedException {
|
||||||
|
var target = new ObjectValue();
|
||||||
|
if (thisArg instanceof ObjectValue) target = (ObjectValue)thisArg;
|
||||||
|
|
||||||
|
target.defineProperty(ctx, "stack", new ArrayValue(ctx, ctx.message.stackTrace().toArray()));
|
||||||
|
target.defineProperty(ctx, "name", "Error");
|
||||||
|
if (message == null) target.defineProperty(ctx, "message", "");
|
||||||
|
else target.defineProperty(ctx, "message", Values.toString(ctx, message));
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeInit(InitType.PROTOTYPE) public static void init(Environment env, ObjectValue target) {
|
||||||
|
target.defineProperty(null, env.symbol("Symbol.typeName"), "Error");
|
||||||
|
target.defineProperty(null, "name", "Error");
|
||||||
|
}
|
||||||
|
}
|
56
src/me/topchetoeu/jscript/polyfills/FunctionPolyfill.java
Normal file
56
src/me/topchetoeu/jscript/polyfills/FunctionPolyfill.java
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
import me.topchetoeu.jscript.interop.InitType;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
|
public class FunctionPolyfill {
|
||||||
|
@Native(thisArg = true) public static Object apply(Context ctx, FunctionValue func, Object thisArg, ArrayValue args) throws InterruptedException {
|
||||||
|
return func.call(ctx, thisArg, args.toArray());
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static Object call(Context ctx, FunctionValue func, Object thisArg, Object... args) throws InterruptedException {
|
||||||
|
if (!(func instanceof FunctionValue)) throw EngineException.ofError("Expected this to be a function.");
|
||||||
|
|
||||||
|
return func.call(ctx, thisArg, args);
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static FunctionValue bind(FunctionValue func, Object thisArg, Object... args) {
|
||||||
|
if (!(func instanceof FunctionValue)) throw EngineException.ofError("Expected this to be a function.");
|
||||||
|
|
||||||
|
return new NativeFunction(func.name + " (bound)", (callCtx, _0, callArgs) -> {
|
||||||
|
Object[] resArgs;
|
||||||
|
|
||||||
|
if (args.length == 0) resArgs = callArgs;
|
||||||
|
else {
|
||||||
|
resArgs = new Object[args.length + callArgs.length];
|
||||||
|
System.arraycopy(args, 0, resArgs, 0, args.length);
|
||||||
|
System.arraycopy(callArgs, 0, resArgs, args.length, callArgs.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return func.call(callCtx, thisArg, resArgs);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static String toString(Context ctx, Object func) {
|
||||||
|
return "function (...) { ... }";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public static FunctionValue async(FunctionValue func) {
|
||||||
|
return new AsyncFunctionPolyfill(func);
|
||||||
|
}
|
||||||
|
@Native public static FunctionValue asyncGenerator(FunctionValue func) {
|
||||||
|
return new AsyncGeneratorPolyfill(func);
|
||||||
|
}
|
||||||
|
@Native public static FunctionValue generator(FunctionValue func) {
|
||||||
|
return new GeneratorPolyfill(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeInit(InitType.PROTOTYPE) public static void init(Environment env, ObjectValue target) {
|
||||||
|
target.defineProperty(null, env.symbol("Symbol.typeName"), "Function");
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ import me.topchetoeu.jscript.engine.values.ObjectValue;
|
|||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
|
||||||
public class GeneratorFunction extends FunctionValue {
|
public class GeneratorPolyfill extends FunctionValue {
|
||||||
public final FunctionValue factory;
|
public final FunctionValue factory;
|
||||||
|
|
||||||
public static class Generator {
|
public static class Generator {
|
||||||
@ -18,6 +18,8 @@ public class GeneratorFunction extends FunctionValue {
|
|||||||
private boolean done = false;
|
private boolean done = false;
|
||||||
public CodeFrame frame;
|
public CodeFrame frame;
|
||||||
|
|
||||||
|
@Native("@@Symbol.typeName") public final String name = "Generator";
|
||||||
|
|
||||||
private ObjectValue next(Context ctx, Object inducedValue, Object inducedReturn, Object inducedError) throws InterruptedException {
|
private ObjectValue next(Context ctx, Object inducedValue, Object inducedReturn, Object inducedError) throws InterruptedException {
|
||||||
if (done) {
|
if (done) {
|
||||||
if (inducedError != Runners.NO_RETURN) throw new EngineException(inducedError);
|
if (inducedError != Runners.NO_RETURN) throw new EngineException(inducedError);
|
||||||
@ -28,12 +30,12 @@ public class GeneratorFunction extends FunctionValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Object res = null;
|
Object res = null;
|
||||||
if (inducedValue != Runners.NO_RETURN) frame.push(ctx, inducedValue);
|
ctx.message.pushFrame(ctx, frame);
|
||||||
ctx.message.pushFrame(frame);
|
|
||||||
yielding = false;
|
yielding = false;
|
||||||
|
|
||||||
while (!yielding) {
|
while (!yielding) {
|
||||||
try {
|
try {
|
||||||
res = frame.next(ctx, inducedReturn, inducedError);
|
res = frame.next(ctx, inducedValue, inducedReturn, inducedError == Runners.NO_RETURN ? null : new EngineException(inducedError));
|
||||||
inducedReturn = inducedError = Runners.NO_RETURN;
|
inducedReturn = inducedError = Runners.NO_RETURN;
|
||||||
if (res != Runners.NO_RETURN) {
|
if (res != Runners.NO_RETURN) {
|
||||||
done = true;
|
done = true;
|
||||||
@ -74,7 +76,7 @@ public class GeneratorFunction extends FunctionValue {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
if (done) return "Generator [closed]";
|
if (done) return "Generator [closed]";
|
||||||
if (yielding) return "Generator [suspended]";
|
if (yielding) return "Generator [suspended]";
|
||||||
return "Generator " + (done ? "[closed]" : "[suspended]");
|
return "Generator [running]";
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object yield(Context ctx, Object thisArg, Object[] args) {
|
public Object yield(Context ctx, Object thisArg, Object[] args) {
|
||||||
@ -92,7 +94,7 @@ public class GeneratorFunction extends FunctionValue {
|
|||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GeneratorFunction(FunctionValue factory) {
|
public GeneratorPolyfill(FunctionValue factory) {
|
||||||
super(factory.name, factory.length);
|
super(factory.name, factory.length);
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
}
|
}
|
@ -1,35 +1,32 @@
|
|||||||
package me.topchetoeu.jscript.polyfills;
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.FunctionContext;
|
import me.topchetoeu.jscript.engine.DataKey;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
import me.topchetoeu.jscript.engine.scope.GlobalScope;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
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.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeWrapperProvider;
|
||||||
|
|
||||||
public class Internals {
|
public class Internals {
|
||||||
@Native public void markSpecial(FunctionValue ...funcs) {
|
private static final DataKey<HashMap<Integer, Thread>> THREADS = new DataKey<>();
|
||||||
for (var func : funcs) {
|
private static final DataKey<Integer> I = new DataKey<>();
|
||||||
func.special = true;
|
|
||||||
|
@Native public static FunctionValue bind(FunctionValue func, Object thisArg) throws InterruptedException {
|
||||||
|
return FunctionPolyfill.bind(func, thisArg);
|
||||||
}
|
}
|
||||||
|
@Native public static void log(Context ctx, Object ...args) throws InterruptedException {
|
||||||
|
for (var arg : args) {
|
||||||
|
Values.printValue(ctx, arg);
|
||||||
}
|
}
|
||||||
@Native public FunctionContext getEnv(Object func) {
|
System.out.println();
|
||||||
if (func instanceof CodeFunction) return ((CodeFunction)func).environment;
|
|
||||||
else return null;
|
|
||||||
}
|
}
|
||||||
@Native public Object setEnv(Object func, FunctionContext env) {
|
|
||||||
if (func instanceof CodeFunction) ((CodeFunction)func).environment = env;
|
@Native public static int setTimeout(Context ctx, FunctionValue func, int delay, Object ...args) {
|
||||||
return func;
|
var thread = new Thread(() -> {
|
||||||
}
|
|
||||||
@Native public Object apply(Context ctx, FunctionValue func, Object thisArg, ArrayValue args) throws InterruptedException {
|
|
||||||
return func.call(ctx, thisArg, args.toArray());
|
|
||||||
}
|
|
||||||
@Native public FunctionValue delay(Context ctx, double delay, FunctionValue callback) throws InterruptedException {
|
|
||||||
var thread = new Thread((Runnable)() -> {
|
|
||||||
var ms = (long)delay;
|
var ms = (long)delay;
|
||||||
var ns = (int)((delay - ms) * 10000000);
|
var ns = (int)((delay - ms) * 10000000);
|
||||||
|
|
||||||
@ -38,111 +35,93 @@ public class Internals {
|
|||||||
}
|
}
|
||||||
catch (InterruptedException e) { return; }
|
catch (InterruptedException e) { return; }
|
||||||
|
|
||||||
ctx.message.engine.pushMsg(false, ctx.message, callback, null);
|
ctx.message.engine.pushMsg(false, ctx.message, func, null, args);
|
||||||
});
|
});
|
||||||
thread.start();
|
thread.start();
|
||||||
|
|
||||||
return new NativeFunction((_ctx, thisArg, args) -> {
|
int i = ctx.env.data.increase(I, 1, 0);
|
||||||
thread.interrupt();
|
var threads = ctx.env.data.add(THREADS, new HashMap<>());
|
||||||
return null;
|
threads.put(++i, thread);
|
||||||
});
|
return i;
|
||||||
}
|
|
||||||
@Native public void pushMessage(Context ctx, boolean micro, FunctionValue func, Object thisArg, Object[] args) {
|
|
||||||
ctx.message.engine.pushMsg(micro, ctx.message, func, thisArg, args);
|
|
||||||
}
|
}
|
||||||
|
@Native public static int setInterval(Context ctx, FunctionValue func, int delay, Object ...args) {
|
||||||
|
var thread = new Thread(() -> {
|
||||||
|
var ms = (long)delay;
|
||||||
|
var ns = (int)((delay - ms) * 10000000);
|
||||||
|
|
||||||
@Native public int strlen(String str) {
|
while (true) {
|
||||||
return str.length();
|
|
||||||
}
|
|
||||||
@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 static void log(Context ctx, Object ...args) throws InterruptedException {
|
|
||||||
for (var arg : args) {
|
|
||||||
Values.printValue(ctx, arg);
|
|
||||||
}
|
|
||||||
System.out.println();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Native public boolean isArray(Object obj) {
|
|
||||||
return obj instanceof ArrayValue;
|
|
||||||
}
|
|
||||||
@Native public GeneratorFunction generator(FunctionValue obj) {
|
|
||||||
return new GeneratorFunction(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Native public boolean defineField(Context ctx, ObjectValue obj, Object key, Object val, boolean writable, boolean enumerable, boolean configurable) {
|
|
||||||
return obj.defineProperty(ctx, key, val, writable, configurable, enumerable);
|
|
||||||
}
|
|
||||||
@Native public boolean defineProp(Context ctx, ObjectValue obj, Object key, FunctionValue getter, FunctionValue setter, boolean enumerable, boolean configurable) {
|
|
||||||
return obj.defineProperty(ctx, key, getter, setter, configurable, enumerable);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Native public ArrayValue keys(Context 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(Context 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(Context 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(Context ctx, ArrayValue arr, FunctionValue cmp) {
|
|
||||||
arr.sort((a, b) -> {
|
|
||||||
try {
|
try {
|
||||||
var res = Values.toNumber(ctx, cmp.call(ctx, null, a, b));
|
Thread.sleep(ms, ns);
|
||||||
if (res < 0) return -1;
|
|
||||||
if (res > 0) return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
catch (InterruptedException e) {
|
catch (InterruptedException e) { return; }
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
return 0;
|
ctx.message.engine.pushMsg(false, ctx.message, func, null, args);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
thread.start();
|
||||||
|
|
||||||
|
int i = ctx.env.data.increase(I, 1, 0);
|
||||||
|
var threads = ctx.env.data.add(THREADS, new HashMap<>());
|
||||||
|
threads.put(++i, thread);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public static void clearTimeout(Context ctx, int i) {
|
||||||
|
var threads = ctx.env.data.add(THREADS, new HashMap<>());
|
||||||
|
|
||||||
|
var thread = threads.remove(i);
|
||||||
|
if (thread != null) thread.interrupt();
|
||||||
|
}
|
||||||
|
@Native public static void clearInterval(Context ctx, int i) {
|
||||||
|
clearTimeout(ctx, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public static double parseInt(Context ctx, String val) throws InterruptedException {
|
||||||
|
return NumberPolyfill.parseInt(ctx, val);
|
||||||
|
}
|
||||||
|
@Native public static double parseFloat(Context ctx, String val) throws InterruptedException {
|
||||||
|
return NumberPolyfill.parseFloat(ctx, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void apply(Environment env) {
|
||||||
|
var wp = env.wrappersProvider;
|
||||||
|
var glob = env.global = new GlobalScope(NativeWrapperProvider.makeNamespace(env, Internals.class));
|
||||||
|
|
||||||
|
glob.define(null, "Object", false, wp.getConstr(ObjectPolyfill.class));
|
||||||
|
glob.define(null, "Function", false, wp.getConstr(FunctionPolyfill.class));
|
||||||
|
glob.define(null, "Array", false, wp.getConstr(ArrayPolyfill.class));
|
||||||
|
|
||||||
|
glob.define(null, "Boolean", false, wp.getConstr(BooleanPolyfill.class));
|
||||||
|
glob.define(null, "Number", false, wp.getConstr(NumberPolyfill.class));
|
||||||
|
glob.define(null, "String", false, wp.getConstr(StringPolyfill.class));
|
||||||
|
glob.define(null, "Symbol", false, wp.getConstr(SymbolPolyfill.class));
|
||||||
|
|
||||||
|
glob.define(null, "Promise", false, wp.getConstr(PromisePolyfill.class));
|
||||||
|
glob.define(null, "RegExp", false, wp.getConstr(RegExpPolyfill.class));
|
||||||
|
glob.define(null, "Map", false, wp.getConstr(MapPolyfill.class));
|
||||||
|
glob.define(null, "Set", false, wp.getConstr(SetPolyfill.class));
|
||||||
|
|
||||||
|
glob.define(null, "Error", false, wp.getConstr(ErrorPolyfill.class));
|
||||||
|
glob.define(null, "SyntaxError", false, wp.getConstr(SyntaxErrorPolyfill.class));
|
||||||
|
glob.define(null, "TypeError", false, wp.getConstr(TypeErrorPolyfill.class));
|
||||||
|
glob.define(null, "RangeError", false, wp.getConstr(RangeErrorPolyfill.class));
|
||||||
|
|
||||||
|
env.setProto("object", wp.getProto(ObjectPolyfill.class));
|
||||||
|
env.setProto("function", wp.getProto(FunctionPolyfill.class));
|
||||||
|
env.setProto("array", wp.getProto(ArrayPolyfill.class));
|
||||||
|
|
||||||
|
env.setProto("bool", wp.getProto(BooleanPolyfill.class));
|
||||||
|
env.setProto("number", wp.getProto(NumberPolyfill.class));
|
||||||
|
env.setProto("string", wp.getProto(StringPolyfill.class));
|
||||||
|
env.setProto("symbol", wp.getProto(SymbolPolyfill.class));
|
||||||
|
|
||||||
|
env.setProto("error", wp.getProto(ErrorPolyfill.class));
|
||||||
|
env.setProto("syntaxErr", wp.getProto(SyntaxErrorPolyfill.class));
|
||||||
|
env.setProto("typeErr", wp.getProto(TypeErrorPolyfill.class));
|
||||||
|
env.setProto("rangeErr", wp.getProto(RangeErrorPolyfill.class));
|
||||||
|
|
||||||
|
wp.getProto(ObjectPolyfill.class).setPrototype(null, null);
|
||||||
|
|
||||||
|
System.out.println("Loaded polyfills!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,12 +14,12 @@ import me.topchetoeu.jscript.json.JSONElement;
|
|||||||
import me.topchetoeu.jscript.json.JSONList;
|
import me.topchetoeu.jscript.json.JSONList;
|
||||||
import me.topchetoeu.jscript.json.JSONMap;
|
import me.topchetoeu.jscript.json.JSONMap;
|
||||||
|
|
||||||
public class JSON {
|
public class JSONPolyfill {
|
||||||
private static Object toJS(JSONElement val) {
|
private static Object toJS(JSONElement val) {
|
||||||
if (val.isBoolean()) return val.bool();
|
if (val.isBoolean()) return val.bool();
|
||||||
if (val.isString()) return val.string();
|
if (val.isString()) return val.string();
|
||||||
if (val.isNumber()) return val.number();
|
if (val.isNumber()) return val.number();
|
||||||
if (val.isList()) return ArrayValue.of(null, val.list().stream().map(JSON::toJS).collect(Collectors.toList()));
|
if (val.isList()) return ArrayValue.of(null, val.list().stream().map(JSONPolyfill::toJS).collect(Collectors.toList()));
|
||||||
if (val.isMap()) {
|
if (val.isMap()) {
|
||||||
var res = new ObjectValue();
|
var res = new ObjectValue();
|
||||||
for (var el : val.map().entrySet()) {
|
for (var el : val.map().entrySet()) {
|
78
src/me/topchetoeu/jscript/polyfills/MapPolyfill.java
Normal file
78
src/me/topchetoeu/jscript/polyfills/MapPolyfill.java
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
|
|
||||||
|
public class MapPolyfill {
|
||||||
|
private LinkedHashMap<Object, Object> map = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
@Native("@@Symbol.typeName") public final String name = "Map";
|
||||||
|
@Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) throws InterruptedException {
|
||||||
|
return this.entries(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public void clear() {
|
||||||
|
map.clear();
|
||||||
|
}
|
||||||
|
@Native public boolean delete(Object key) {
|
||||||
|
if (map.containsKey(key)) {
|
||||||
|
map.remove(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public ObjectValue entries(Context ctx) throws InterruptedException {
|
||||||
|
var res = map.entrySet().stream().map(v -> {
|
||||||
|
return new ArrayValue(ctx, v.getKey(), v.getValue());
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
return Values.fromJavaIterator(ctx, res.iterator());
|
||||||
|
}
|
||||||
|
@Native public ObjectValue keys(Context ctx) throws InterruptedException {
|
||||||
|
var res = new ArrayList<>(map.keySet());
|
||||||
|
return Values.fromJavaIterator(ctx, res.iterator());
|
||||||
|
}
|
||||||
|
@Native public ObjectValue values(Context ctx) throws InterruptedException {
|
||||||
|
var res = new ArrayList<>(map.values());
|
||||||
|
return Values.fromJavaIterator(ctx, res.iterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public Object get(Object key) {
|
||||||
|
return map.get(key);
|
||||||
|
}
|
||||||
|
@Native public MapPolyfill set(Object key, Object val) {
|
||||||
|
map.put(key, val);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
@Native public boolean has(Object key) {
|
||||||
|
return map.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeGetter public int size() {
|
||||||
|
return map.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeGetter public void forEach(Context ctx, FunctionValue func, Object thisArg) throws InterruptedException {
|
||||||
|
var keys = new ArrayList<>(map.keySet());
|
||||||
|
|
||||||
|
for (var el : keys) func.call(ctx, thisArg, el, map.get(el), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public MapPolyfill(Context ctx, Object iterable) throws InterruptedException {
|
||||||
|
for (var el : Values.toJavaIterable(ctx, iterable)) {
|
||||||
|
try {
|
||||||
|
set(Values.getMember(ctx, el, 0), Values.getMember(ctx, el, 1));
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
package me.topchetoeu.jscript.polyfills;
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.MessageContext;
|
import me.topchetoeu.jscript.engine.Message;
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
|
||||||
public class Math {
|
public class Math {
|
||||||
@ -22,19 +22,19 @@ public class Math {
|
|||||||
public static final double LOG10E = java.lang.Math.log10(java.lang.Math.E);
|
public static final double LOG10E = java.lang.Math.log10(java.lang.Math.E);
|
||||||
|
|
||||||
@Native
|
@Native
|
||||||
public static double asin(MessageContext ctx, double x) throws InterruptedException {
|
public static double asin(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.asin(x);
|
return java.lang.Math.asin(x);
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static double acos(MessageContext ctx, double x) throws InterruptedException {
|
public static double acos(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.acos(x);
|
return java.lang.Math.acos(x);
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static double atan(MessageContext ctx, double x) throws InterruptedException {
|
public static double atan(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.atan(x);
|
return java.lang.Math.atan(x);
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static double atan2(MessageContext ctx, double y, double x) throws InterruptedException {
|
public static double atan2(Message ctx, double y, double x) throws InterruptedException {
|
||||||
double _y = y;
|
double _y = y;
|
||||||
double _x = x;
|
double _x = x;
|
||||||
if (_x == 0) {
|
if (_x == 0) {
|
||||||
@ -51,59 +51,59 @@ public class Math {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Native
|
@Native
|
||||||
public static double asinh(MessageContext ctx, double x) throws InterruptedException {
|
public static double asinh(Message ctx, double x) throws InterruptedException {
|
||||||
double _x = x;
|
double _x = x;
|
||||||
return java.lang.Math.log(_x + java.lang.Math.sqrt(_x * _x + 1));
|
return java.lang.Math.log(_x + java.lang.Math.sqrt(_x * _x + 1));
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static double acosh(MessageContext ctx, double x) throws InterruptedException {
|
public static double acosh(Message ctx, double x) throws InterruptedException {
|
||||||
double _x = x;
|
double _x = x;
|
||||||
return java.lang.Math.log(_x + java.lang.Math.sqrt(_x * _x - 1));
|
return java.lang.Math.log(_x + java.lang.Math.sqrt(_x * _x - 1));
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static double atanh(MessageContext ctx, double x) throws InterruptedException {
|
public static double atanh(Message ctx, double x) throws InterruptedException {
|
||||||
double _x = x;
|
double _x = x;
|
||||||
if (_x <= -1 || _x >= 1) return Double.NaN;
|
if (_x <= -1 || _x >= 1) return Double.NaN;
|
||||||
return .5 * java.lang.Math.log((1 + _x) / (1 - _x));
|
return .5 * java.lang.Math.log((1 + _x) / (1 - _x));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native
|
@Native
|
||||||
public static double sin(MessageContext ctx, double x) throws InterruptedException {
|
public static double sin(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.sin(x);
|
return java.lang.Math.sin(x);
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static double cos(MessageContext ctx, double x) throws InterruptedException {
|
public static double cos(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.cos(x);
|
return java.lang.Math.cos(x);
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static double tan(MessageContext ctx, double x) throws InterruptedException {
|
public static double tan(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.tan(x);
|
return java.lang.Math.tan(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native
|
@Native
|
||||||
public static double sinh(MessageContext ctx, double x) throws InterruptedException {
|
public static double sinh(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.sinh(x);
|
return java.lang.Math.sinh(x);
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static double cosh(MessageContext ctx, double x) throws InterruptedException {
|
public static double cosh(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.cosh(x);
|
return java.lang.Math.cosh(x);
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static double tanh(MessageContext ctx, double x) throws InterruptedException {
|
public static double tanh(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.tanh(x);
|
return java.lang.Math.tanh(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native
|
@Native
|
||||||
public static double sqrt(MessageContext ctx, double x) throws InterruptedException {
|
public static double sqrt(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.sqrt(x);
|
return java.lang.Math.sqrt(x);
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static double cbrt(MessageContext ctx, double x) throws InterruptedException {
|
public static double cbrt(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.cbrt(x);
|
return java.lang.Math.cbrt(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native
|
@Native
|
||||||
public static double hypot(MessageContext ctx, double ...vals) throws InterruptedException {
|
public static double hypot(Message ctx, double ...vals) throws InterruptedException {
|
||||||
var res = 0.;
|
var res = 0.;
|
||||||
for (var el : vals) {
|
for (var el : vals) {
|
||||||
var val = el;
|
var val = el;
|
||||||
@ -112,68 +112,68 @@ public class Math {
|
|||||||
return java.lang.Math.sqrt(res);
|
return java.lang.Math.sqrt(res);
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static int imul(MessageContext ctx, double a, double b) throws InterruptedException {
|
public static int imul(Message ctx, double a, double b) throws InterruptedException {
|
||||||
return (int)a * (int)b;
|
return (int)a * (int)b;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native
|
@Native
|
||||||
public static double exp(MessageContext ctx, double x) throws InterruptedException {
|
public static double exp(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.exp(x);
|
return java.lang.Math.exp(x);
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static double expm1(MessageContext ctx, double x) throws InterruptedException {
|
public static double expm1(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.expm1(x);
|
return java.lang.Math.expm1(x);
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static double pow(MessageContext ctx, double x, double y) throws InterruptedException {
|
public static double pow(Message ctx, double x, double y) throws InterruptedException {
|
||||||
return java.lang.Math.pow(x, y);
|
return java.lang.Math.pow(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native
|
@Native
|
||||||
public static double log(MessageContext ctx, double x) throws InterruptedException {
|
public static double log(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.log(x);
|
return java.lang.Math.log(x);
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static double log10(MessageContext ctx, double x) throws InterruptedException {
|
public static double log10(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.log10(x);
|
return java.lang.Math.log10(x);
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static double log1p(MessageContext ctx, double x) throws InterruptedException {
|
public static double log1p(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.log1p(x);
|
return java.lang.Math.log1p(x);
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static double log2(MessageContext ctx, double x) throws InterruptedException {
|
public static double log2(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.log(x) / LN2;
|
return java.lang.Math.log(x) / LN2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native
|
@Native
|
||||||
public static double ceil(MessageContext ctx, double x) throws InterruptedException {
|
public static double ceil(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.ceil(x);
|
return java.lang.Math.ceil(x);
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static double floor(MessageContext ctx, double x) throws InterruptedException {
|
public static double floor(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.floor(x);
|
return java.lang.Math.floor(x);
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static double round(MessageContext ctx, double x) throws InterruptedException {
|
public static double round(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.round(x);
|
return java.lang.Math.round(x);
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static float fround(MessageContext ctx, double x) throws InterruptedException {
|
public static float fround(Message ctx, double x) throws InterruptedException {
|
||||||
return (float)x;
|
return (float)x;
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static double trunc(MessageContext ctx, double x) throws InterruptedException {
|
public static double trunc(Message ctx, double x) throws InterruptedException {
|
||||||
var _x = x;
|
var _x = x;
|
||||||
return java.lang.Math.floor(java.lang.Math.abs(_x)) * java.lang.Math.signum(_x);
|
return java.lang.Math.floor(java.lang.Math.abs(_x)) * java.lang.Math.signum(_x);
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static double abs(MessageContext ctx, double x) throws InterruptedException {
|
public static double abs(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.abs(x);
|
return java.lang.Math.abs(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native
|
@Native
|
||||||
public static double max(MessageContext ctx, double ...vals) throws InterruptedException {
|
public static double max(Message ctx, double ...vals) throws InterruptedException {
|
||||||
var res = Double.NEGATIVE_INFINITY;
|
var res = Double.NEGATIVE_INFINITY;
|
||||||
|
|
||||||
for (var el : vals) {
|
for (var el : vals) {
|
||||||
@ -184,7 +184,7 @@ public class Math {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static double min(MessageContext ctx, double ...vals) throws InterruptedException {
|
public static double min(Message ctx, double ...vals) throws InterruptedException {
|
||||||
var res = Double.POSITIVE_INFINITY;
|
var res = Double.POSITIVE_INFINITY;
|
||||||
|
|
||||||
for (var el : vals) {
|
for (var el : vals) {
|
||||||
@ -196,7 +196,7 @@ public class Math {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Native
|
@Native
|
||||||
public static double sign(MessageContext ctx, double x) throws InterruptedException {
|
public static double sign(Message ctx, double x) throws InterruptedException {
|
||||||
return java.lang.Math.signum(x);
|
return java.lang.Math.signum(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,7 +205,7 @@ public class Math {
|
|||||||
return java.lang.Math.random();
|
return java.lang.Math.random();
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static int clz32(MessageContext ctx, double x) throws InterruptedException {
|
public static int clz32(Message ctx, double x) throws InterruptedException {
|
||||||
return Integer.numberOfLeadingZeros((int)x);
|
return Integer.numberOfLeadingZeros((int)x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
59
src/me/topchetoeu/jscript/polyfills/NumberPolyfill.java
Normal file
59
src/me/topchetoeu/jscript/polyfills/NumberPolyfill.java
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
|
import me.topchetoeu.jscript.interop.InitType;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
|
public class NumberPolyfill {
|
||||||
|
@Native public static final double EPSILON = java.lang.Math.ulp(1.0);
|
||||||
|
@Native public static final double MAX_SAFE_INTEGER = 9007199254740991.;
|
||||||
|
@Native public static final double MIN_SAFE_INTEGER = -MAX_SAFE_INTEGER;
|
||||||
|
// lmao big number go brrr
|
||||||
|
@Native public static final double MAX_VALUE = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.;
|
||||||
|
@Native public static final double MIN_VALUE = -MAX_VALUE;
|
||||||
|
@Native public static final double NaN = 0. / 0;
|
||||||
|
@Native public static final double NEGATIVE_INFINITY = -1. / 0;
|
||||||
|
@Native public static final double POSITIVE_INFINITY = 1. / 0;
|
||||||
|
|
||||||
|
public final double value;
|
||||||
|
|
||||||
|
@Native public static boolean isFinite(Context ctx, double val) { return Double.isFinite(val); }
|
||||||
|
@Native public static boolean isInfinite(Context ctx, double val) { return Double.isInfinite(val); }
|
||||||
|
@Native public static boolean isNaN(Context ctx, double val) { return Double.isNaN(val); }
|
||||||
|
@Native public static boolean isSafeInteger(Context ctx, double val) {
|
||||||
|
return val > MIN_SAFE_INTEGER && val < MAX_SAFE_INTEGER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public static double parseFloat(Context ctx, String val) throws InterruptedException {
|
||||||
|
return Values.toNumber(ctx, val);
|
||||||
|
}
|
||||||
|
@Native public static double parseInt(Context ctx, String val) throws InterruptedException {
|
||||||
|
return (long)Values.toNumber(ctx, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object val) throws InterruptedException {
|
||||||
|
val = Values.toNumber(ctx, val);
|
||||||
|
if (thisArg instanceof ObjectValue) return new NumberPolyfill((double)val);
|
||||||
|
else return val;
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static String toString(Context ctx, Object thisArg) throws InterruptedException {
|
||||||
|
return Values.toString(ctx, Values.toNumber(ctx, thisArg));
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static double valueOf(Context ctx, Object thisArg) throws InterruptedException {
|
||||||
|
if (thisArg instanceof NumberPolyfill) return ((NumberPolyfill)thisArg).value;
|
||||||
|
else return Values.toNumber(ctx, thisArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NumberPolyfill(double val) {
|
||||||
|
this.value = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeInit(InitType.PROTOTYPE) public static void init(Environment env, ObjectValue target) {
|
||||||
|
target.defineProperty(null, env.symbol("Symbol.typeName"), "Number");
|
||||||
|
}
|
||||||
|
}
|
219
src/me/topchetoeu/jscript/polyfills/ObjectPolyfill.java
Normal file
219
src/me/topchetoeu/jscript/polyfills/ObjectPolyfill.java
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.Symbol;
|
||||||
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
import me.topchetoeu.jscript.interop.InitType;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
|
public class ObjectPolyfill {
|
||||||
|
@Native public static ObjectValue assign(Context ctx, ObjectValue dst, Object... src) throws InterruptedException {
|
||||||
|
for (var obj : src) {
|
||||||
|
for (var key : Values.getMembers(ctx, obj, true, true)) {
|
||||||
|
Values.setMember(ctx, dst, key, Values.getMember(ctx, obj, key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
@Native public static ObjectValue create(Context ctx, ObjectValue proto, ObjectValue props) throws InterruptedException {
|
||||||
|
var obj = new ObjectValue();
|
||||||
|
obj.setPrototype(ctx, proto);
|
||||||
|
return defineProperties(ctx, obj, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public static ObjectValue defineProperty(Context ctx, ObjectValue obj, Object key, ObjectValue attrib) throws InterruptedException {
|
||||||
|
var hasVal = attrib.hasMember(ctx, "value", false);
|
||||||
|
var hasGet = attrib.hasMember(ctx, "get", false);
|
||||||
|
var hasSet = attrib.hasMember(ctx, "set", false);
|
||||||
|
|
||||||
|
if (hasVal) {
|
||||||
|
if (hasGet || hasSet) throw EngineException.ofType("Cannot specify a value and accessors for a property.");
|
||||||
|
if (!obj.defineProperty(
|
||||||
|
ctx, key,
|
||||||
|
attrib.getMember(ctx, "value"),
|
||||||
|
Values.toBoolean(attrib.getMember(ctx, "writable")),
|
||||||
|
Values.toBoolean(attrib.getMember(ctx, "configurable")),
|
||||||
|
Values.toBoolean(attrib.getMember(ctx, "enumerable"))
|
||||||
|
)) throw EngineException.ofType("Can't define property '" + key + "'.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var get = attrib.getMember(ctx, "get");
|
||||||
|
var set = attrib.getMember(ctx, "set");
|
||||||
|
if (get != null && !(get instanceof FunctionValue)) throw EngineException.ofType("Get accessor must be a function.");
|
||||||
|
if (set != null && !(set instanceof FunctionValue)) throw EngineException.ofType("Set accessor must be a function.");
|
||||||
|
|
||||||
|
if (!obj.defineProperty(
|
||||||
|
ctx, key,
|
||||||
|
(FunctionValue)get, (FunctionValue)set,
|
||||||
|
Values.toBoolean(attrib.getMember(ctx, "configurable")),
|
||||||
|
Values.toBoolean(attrib.getMember(ctx, "enumerable"))
|
||||||
|
)) throw EngineException.ofType("Can't define property '" + key + "'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
@Native public static ObjectValue defineProperties(Context ctx, ObjectValue obj, ObjectValue attrib) throws InterruptedException {
|
||||||
|
for (var key : Values.getMembers(null, obj, false, false)) {
|
||||||
|
obj.defineProperty(ctx, key, attrib.getMember(ctx, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public static ArrayValue keys(Context ctx, Object obj, Object all) throws InterruptedException {
|
||||||
|
var res = new ArrayValue();
|
||||||
|
var _all = Values.toBoolean(all);
|
||||||
|
|
||||||
|
for (var key : Values.getMembers(ctx, obj, true, false)) {
|
||||||
|
if (_all || !(key instanceof Symbol)) res.set(ctx, res.size(), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@Native public static ArrayValue entries(Context ctx, Object obj, Object all) throws InterruptedException {
|
||||||
|
var res = new ArrayValue();
|
||||||
|
var _all = Values.toBoolean(all);
|
||||||
|
|
||||||
|
for (var key : Values.getMembers(ctx, obj, true, false)) {
|
||||||
|
if (_all || !(key instanceof Symbol)) res.set(ctx, res.size(), new ArrayValue(ctx, key, Values.getMember(ctx, obj, key)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@Native public static ArrayValue values(Context ctx, Object obj, Object all) throws InterruptedException {
|
||||||
|
var res = new ArrayValue();
|
||||||
|
var _all = Values.toBoolean(all);
|
||||||
|
|
||||||
|
for (var key : Values.getMembers(ctx, obj, true, false)) {
|
||||||
|
if (_all || key instanceof String) res.set(ctx, res.size(), Values.getMember(ctx, obj, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public static ObjectValue getOwnPropertyDescriptor(Context ctx, Object obj, Object key) throws InterruptedException {
|
||||||
|
return Values.getMemberDescriptor(ctx, obj, key);
|
||||||
|
}
|
||||||
|
@Native public static ObjectValue getOwnPropertyDescriptors(Context ctx, Object obj) throws InterruptedException {
|
||||||
|
var res = new ObjectValue();
|
||||||
|
for (var key : Values.getMembers(ctx, obj, true, true)) {
|
||||||
|
res.defineProperty(ctx, key, getOwnPropertyDescriptor(ctx, obj, key));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public static ArrayValue getOwnPropertyNames(Context ctx, Object obj, Object all) throws InterruptedException {
|
||||||
|
var res = new ArrayValue();
|
||||||
|
var _all = Values.toBoolean(all);
|
||||||
|
|
||||||
|
for (var key : Values.getMembers(ctx, obj, true, true)) {
|
||||||
|
if (_all || !(key instanceof Symbol)) res.set(ctx, res.size(), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@Native public static ArrayValue getOwnPropertySymbols(Context ctx, Object obj) throws InterruptedException {
|
||||||
|
var res = new ArrayValue();
|
||||||
|
|
||||||
|
for (var key : Values.getMembers(ctx, obj, true, true)) {
|
||||||
|
if (key instanceof Symbol) res.set(ctx, res.size(), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@Native public static boolean hasOwn(Context ctx, Object obj, Object key) throws InterruptedException {
|
||||||
|
return Values.hasMember(ctx, obj, key, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public static ObjectValue getPrototypeOf(Context ctx, Object obj) throws InterruptedException {
|
||||||
|
return Values.getPrototype(ctx, obj);
|
||||||
|
}
|
||||||
|
@Native public static Object setPrototypeOf(Context ctx, Object obj, Object proto) throws InterruptedException {
|
||||||
|
Values.setPrototype(ctx, obj, proto);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public static ObjectValue fromEntries(Context ctx, Object iterable) throws InterruptedException {
|
||||||
|
var res = new ObjectValue();
|
||||||
|
|
||||||
|
for (var el : Values.toJavaIterable(ctx, iterable)) {
|
||||||
|
if (el instanceof ArrayValue) {
|
||||||
|
res.defineProperty(ctx, ((ArrayValue)el).get(0), ((ArrayValue)el).get(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public static Object preventExtensions(Context ctx, Object obj) throws InterruptedException {
|
||||||
|
if (obj instanceof ObjectValue) ((ObjectValue)obj).preventExtensions();
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
@Native public static Object seal(Context ctx, Object obj) throws InterruptedException {
|
||||||
|
if (obj instanceof ObjectValue) ((ObjectValue)obj).seal();
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
@Native public static Object freeze(Context ctx, Object obj) throws InterruptedException {
|
||||||
|
if (obj instanceof ObjectValue) ((ObjectValue)obj).freeze();
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public static boolean isExtensible(Context ctx, Object obj) throws InterruptedException {
|
||||||
|
return obj instanceof ObjectValue && ((ObjectValue)obj).extensible();
|
||||||
|
}
|
||||||
|
@Native public static boolean isSealed(Context ctx, Object obj) throws InterruptedException {
|
||||||
|
if (obj instanceof ObjectValue && ((ObjectValue)obj).extensible()) {
|
||||||
|
var _obj = (ObjectValue)obj;
|
||||||
|
for (var key : _obj.keys(true)) {
|
||||||
|
if (_obj.memberConfigurable(key)) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@Native public static boolean isFrozen(Context ctx, Object obj) throws InterruptedException {
|
||||||
|
if (obj instanceof ObjectValue && ((ObjectValue)obj).extensible()) {
|
||||||
|
var _obj = (ObjectValue)obj;
|
||||||
|
for (var key : _obj.keys(true)) {
|
||||||
|
if (_obj.memberConfigurable(key)) return false;
|
||||||
|
if (_obj.memberWritable(key)) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static Object valueOf(Context ctx, Object thisArg) {
|
||||||
|
return thisArg;
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static String toString(Context ctx, Object thisArg) throws InterruptedException {
|
||||||
|
var name = Values.getMember(ctx, thisArg, ctx.env.symbol("Symbol.typeName"));
|
||||||
|
if (name == null) name = "Unknown";
|
||||||
|
else name = Values.toString(ctx, name);
|
||||||
|
|
||||||
|
return "[object " + name + "]";
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static boolean hasOwnProperty(Context ctx, Object thisArg, Object key) throws InterruptedException {
|
||||||
|
return ObjectPolyfill.hasOwn(ctx, thisArg, Values.convert(ctx, key, String.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object arg) throws InterruptedException {
|
||||||
|
if (arg == null || arg == Values.NULL) return new ObjectValue();
|
||||||
|
else if (arg instanceof Boolean) return BooleanPolyfill.constructor(ctx, thisArg, arg);
|
||||||
|
else if (arg instanceof Number) return NumberPolyfill.constructor(ctx, thisArg, arg);
|
||||||
|
else if (arg instanceof String) return StringPolyfill.constructor(ctx, thisArg, arg);
|
||||||
|
// else if (arg instanceof Symbol) return SymbolPolyfill.constructor(ctx, thisArg, arg);
|
||||||
|
else return arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeInit(InitType.PROTOTYPE) public static void init(Environment env, ObjectValue target) {
|
||||||
|
target.defineProperty(null, env.symbol("Symbol.typeName"), "Object");
|
||||||
|
}
|
||||||
|
}
|
348
src/me/topchetoeu/jscript/polyfills/PromisePolyfill.java
Normal file
348
src/me/topchetoeu/jscript/polyfills/PromisePolyfill.java
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
|
import me.topchetoeu.jscript.engine.Message;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
|
import me.topchetoeu.jscript.engine.values.NativeWrapper;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
import me.topchetoeu.jscript.interop.InitType;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
|
public class PromisePolyfill {
|
||||||
|
private static class Handle {
|
||||||
|
public final Context ctx;
|
||||||
|
public final FunctionValue fulfilled;
|
||||||
|
public final FunctionValue rejected;
|
||||||
|
|
||||||
|
public Handle(Context ctx, FunctionValue fulfilled, FunctionValue rejected) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.fulfilled = fulfilled;
|
||||||
|
this.rejected = rejected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native("resolve")
|
||||||
|
public static PromisePolyfill ofResolved(Context ctx, Object val) throws InterruptedException {
|
||||||
|
var res = new PromisePolyfill();
|
||||||
|
res.fulfill(ctx, val);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@Native("reject")
|
||||||
|
public static PromisePolyfill ofRejected(Context ctx, Object val) throws InterruptedException {
|
||||||
|
var res = new PromisePolyfill();
|
||||||
|
res.reject(ctx, val);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public static PromisePolyfill any(Context ctx, Object _promises) throws InterruptedException {
|
||||||
|
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
|
||||||
|
var promises = Values.array(_promises);
|
||||||
|
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
|
||||||
|
var n = new int[] { promises.size() };
|
||||||
|
var res = new PromisePolyfill();
|
||||||
|
|
||||||
|
var errors = new ArrayValue();
|
||||||
|
|
||||||
|
for (var i = 0; i < promises.size(); i++) {
|
||||||
|
var index = i;
|
||||||
|
var val = promises.get(i);
|
||||||
|
then(ctx, val,
|
||||||
|
new NativeFunction(null, (e, th, args) -> { res.fulfill(e, args[0]); return null; }),
|
||||||
|
new NativeFunction(null, (e, th, args) -> {
|
||||||
|
errors.set(ctx, index, args[0]);
|
||||||
|
n[0]--;
|
||||||
|
if (n[0] <= 0) res.reject(e, errors);
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@Native public static PromisePolyfill race(Context ctx, Object _promises) throws InterruptedException {
|
||||||
|
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
|
||||||
|
var promises = Values.array(_promises);
|
||||||
|
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
|
||||||
|
var res = new PromisePolyfill();
|
||||||
|
|
||||||
|
for (var i = 0; i < promises.size(); i++) {
|
||||||
|
var val = promises.get(i);
|
||||||
|
then(ctx, val,
|
||||||
|
new NativeFunction(null, (e, th, args) -> { res.fulfill(e, args[0]); return null; }),
|
||||||
|
new NativeFunction(null, (e, th, args) -> { res.reject(e, args[0]); return null; })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@Native public static PromisePolyfill all(Context ctx, Object _promises) throws InterruptedException {
|
||||||
|
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
|
||||||
|
var promises = Values.array(_promises);
|
||||||
|
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
|
||||||
|
var n = new int[] { promises.size() };
|
||||||
|
var res = new PromisePolyfill();
|
||||||
|
|
||||||
|
var result = new ArrayValue();
|
||||||
|
|
||||||
|
for (var i = 0; i < promises.size(); i++) {
|
||||||
|
var index = i;
|
||||||
|
var val = promises.get(i);
|
||||||
|
then(ctx, val,
|
||||||
|
new NativeFunction(null, (e, th, args) -> {
|
||||||
|
result.set(ctx, index, args[0]);
|
||||||
|
n[0]--;
|
||||||
|
if (n[0] <= 0) res.fulfill(e, result);
|
||||||
|
return null;
|
||||||
|
}),
|
||||||
|
new NativeFunction(null, (e, th, args) -> { res.reject(e, args[0]); return null; })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n[0] <= 0) res.fulfill(ctx, result);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@Native public static PromisePolyfill allSettled(Context ctx, Object _promises) throws InterruptedException {
|
||||||
|
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
|
||||||
|
var promises = Values.array(_promises);
|
||||||
|
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
|
||||||
|
var n = new int[] { promises.size() };
|
||||||
|
var res = new PromisePolyfill();
|
||||||
|
|
||||||
|
var result = new ArrayValue();
|
||||||
|
|
||||||
|
for (var i = 0; i < promises.size(); i++) {
|
||||||
|
var index = i;
|
||||||
|
var val = promises.get(i);
|
||||||
|
then(ctx, val,
|
||||||
|
new NativeFunction(null, (e, th, args) -> {
|
||||||
|
result.set(ctx, index, new ObjectValue(ctx, Map.of(
|
||||||
|
"status", "fulfilled",
|
||||||
|
"value", args[0]
|
||||||
|
)));
|
||||||
|
n[0]--;
|
||||||
|
if (n[0] <= 0) res.fulfill(e, result);
|
||||||
|
return null;
|
||||||
|
}),
|
||||||
|
new NativeFunction(null, (e, th, args) -> {
|
||||||
|
result.set(ctx, index, new ObjectValue(ctx, Map.of(
|
||||||
|
"status", "rejected",
|
||||||
|
"reason", args[0]
|
||||||
|
)));
|
||||||
|
n[0]--;
|
||||||
|
if (n[0] <= 0) res.fulfill(e, result);
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n[0] <= 0) res.fulfill(ctx, result);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread safe - you can call this from anywhere
|
||||||
|
* HOWEVER, it's strongly recommended to use this only in javascript
|
||||||
|
*/
|
||||||
|
@Native(thisArg=true) public static Object then(Context ctx, Object thisArg, Object _onFulfill, Object _onReject) throws InterruptedException {
|
||||||
|
var onFulfill = _onFulfill instanceof FunctionValue ? ((FunctionValue)_onFulfill) : null;
|
||||||
|
var onReject = _onReject instanceof FunctionValue ? ((FunctionValue)_onReject) : null;
|
||||||
|
|
||||||
|
var res = new PromisePolyfill();
|
||||||
|
|
||||||
|
var fulfill = onFulfill == null ? new NativeFunction((_ctx, _thisArg, _args) -> _args.length > 0 ? _args[0] : null) : (FunctionValue)onFulfill;
|
||||||
|
var reject = onReject == null ? new NativeFunction((_ctx, _thisArg, _args) -> {
|
||||||
|
throw new EngineException(_args.length > 0 ? _args[0] : null);
|
||||||
|
}) : (FunctionValue)onReject;
|
||||||
|
|
||||||
|
if (thisArg instanceof NativeWrapper && ((NativeWrapper)thisArg).wrapped instanceof PromisePolyfill) {
|
||||||
|
thisArg = ((NativeWrapper)thisArg).wrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fulfillHandle = new NativeFunction(null, (_ctx, th, a) -> {
|
||||||
|
try {
|
||||||
|
res.fulfill(ctx, Values.convert(ctx, fulfill.call(ctx, null, a[0]), Object.class));
|
||||||
|
}
|
||||||
|
catch (EngineException err) { res.reject(ctx, err.value); }
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
var rejectHandle = new NativeFunction(null, (_ctx, th, a) -> {
|
||||||
|
try { res.fulfill(ctx, reject.call(ctx, null, a[0])); }
|
||||||
|
catch (EngineException err) { res.reject(ctx, err.value); }
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (thisArg instanceof PromisePolyfill) ((PromisePolyfill)thisArg).handle(ctx, fulfillHandle, rejectHandle);
|
||||||
|
else {
|
||||||
|
Object next;
|
||||||
|
try {
|
||||||
|
next = Values.getMember(ctx, thisArg, "then");
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) { next = null; }
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (next instanceof FunctionValue) ((FunctionValue)next).call(ctx, thisArg, fulfillHandle, rejectHandle);
|
||||||
|
else res.fulfill(ctx, fulfill.call(ctx, null, thisArg));
|
||||||
|
}
|
||||||
|
catch (EngineException err) {
|
||||||
|
res.reject(ctx, fulfill.call(ctx, null, err.value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Thread safe - you can call this from anywhere
|
||||||
|
* HOWEVER, it's strongly recommended to use this only in javascript
|
||||||
|
*/
|
||||||
|
@Native(value="catch", thisArg=true) public static Object _catch(Context ctx, Object thisArg, Object _onReject) throws InterruptedException {
|
||||||
|
return then(ctx, thisArg, null, _onReject);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Thread safe - you can call this from anywhere
|
||||||
|
* HOWEVER, it's strongly recommended to use this only in javascript
|
||||||
|
*/
|
||||||
|
@Native(value="finally", thisArg=true) public static Object _finally(Context ctx, Object thisArg, Object _handle) throws InterruptedException {
|
||||||
|
return then(ctx, thisArg,
|
||||||
|
new NativeFunction(null, (e, th, _args) -> {
|
||||||
|
if (_handle instanceof FunctionValue) ((FunctionValue)_handle).call(ctx);
|
||||||
|
return _args.length > 0 ? _args[0] : null;
|
||||||
|
}),
|
||||||
|
new NativeFunction(null, (e, th, _args) -> {
|
||||||
|
if (_handle instanceof FunctionValue) ((FunctionValue)_handle).call(ctx);
|
||||||
|
throw new EngineException(_args.length > 0 ? _args[0] : null);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Handle> handles = new ArrayList<>();
|
||||||
|
|
||||||
|
private static final int STATE_PENDING = 0;
|
||||||
|
private static final int STATE_FULFILLED = 1;
|
||||||
|
private static final int STATE_REJECTED = 2;
|
||||||
|
|
||||||
|
private int state = STATE_PENDING;
|
||||||
|
private boolean handled = false;
|
||||||
|
private Object val;
|
||||||
|
|
||||||
|
private void resolve(Context ctx, Object val, int state) throws InterruptedException {
|
||||||
|
if (this.state != STATE_PENDING) return;
|
||||||
|
|
||||||
|
if (val instanceof PromisePolyfill) ((PromisePolyfill)val).handle(ctx,
|
||||||
|
new NativeFunction(null, (e, th, a) -> { this.resolve(ctx, a[0], state); return null; }),
|
||||||
|
new NativeFunction(null, (e, th, a) -> { this.resolve(ctx, a[0], STATE_REJECTED); return null; })
|
||||||
|
);
|
||||||
|
else {
|
||||||
|
Object next;
|
||||||
|
try { next = Values.getMember(ctx, val, "next"); }
|
||||||
|
catch (IllegalArgumentException e) { next = null; }
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (next instanceof FunctionValue) ((FunctionValue)next).call(ctx, val,
|
||||||
|
new NativeFunction((e, _thisArg, a) -> { this.resolve(ctx, a.length > 0 ? a[0] : null, state); return null; }),
|
||||||
|
new NativeFunction((e, _thisArg, a) -> { this.resolve(ctx, a.length > 0 ? a[0] : null, STATE_REJECTED); return null; })
|
||||||
|
);
|
||||||
|
else {
|
||||||
|
this.val = val;
|
||||||
|
this.state = state;
|
||||||
|
|
||||||
|
if (state == STATE_FULFILLED) {
|
||||||
|
for (var handle : handles) handle.fulfilled.call(handle.ctx, null, val);
|
||||||
|
}
|
||||||
|
else if (state == STATE_REJECTED) {
|
||||||
|
for (var handle : handles) handle.rejected.call(handle.ctx, null, val);
|
||||||
|
if (handles.size() == 0) {
|
||||||
|
ctx.message.engine.pushMsg(true, ctx.message, new NativeFunction((_ctx, _thisArg, _args) -> {
|
||||||
|
if (!handled) {
|
||||||
|
try { Values.printError(new EngineException(val).setContext(ctx), "(in promise)"); }
|
||||||
|
catch (InterruptedException ex) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handles = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (EngineException err) {
|
||||||
|
this.reject(ctx, err.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread safe - call from any thread
|
||||||
|
*/
|
||||||
|
public void fulfill(Context ctx, Object val) throws InterruptedException {
|
||||||
|
resolve(ctx, val, STATE_FULFILLED);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Thread safe - call from any thread
|
||||||
|
*/
|
||||||
|
public void reject(Context ctx, Object val) throws InterruptedException {
|
||||||
|
resolve(ctx, val, STATE_REJECTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handle(Context ctx, FunctionValue fulfill, FunctionValue reject) {
|
||||||
|
if (state == STATE_FULFILLED) ctx.message.engine.pushMsg(true, new Message(ctx.message.engine), fulfill, null, val);
|
||||||
|
else if (state == STATE_REJECTED) {
|
||||||
|
ctx.message.engine.pushMsg(true, new Message(ctx.message.engine), reject, null, val);
|
||||||
|
handled = true;
|
||||||
|
}
|
||||||
|
else handles.add(new Handle(ctx, fulfill, reject));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override @Native public String toString() {
|
||||||
|
if (state == STATE_PENDING) return "Promise (pending)";
|
||||||
|
else if (state == STATE_FULFILLED) return "Promise (fulfilled)";
|
||||||
|
else return "Promise (rejected)";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOT THREAD SAFE - must be called from the engine executor thread
|
||||||
|
*/
|
||||||
|
@Native public PromisePolyfill(Context ctx, FunctionValue func) throws InterruptedException {
|
||||||
|
if (!(func instanceof FunctionValue)) throw EngineException.ofType("A function must be passed to the promise constructor.");
|
||||||
|
try {
|
||||||
|
func.call(
|
||||||
|
ctx, null,
|
||||||
|
new NativeFunction(null, (e, th, args) -> {
|
||||||
|
fulfill(e, args.length > 0 ? args[0] : null);
|
||||||
|
return null;
|
||||||
|
}),
|
||||||
|
new NativeFunction(null, (e, th, args) -> {
|
||||||
|
reject(e, args.length > 0 ? args[0] : null);
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (EngineException e) {
|
||||||
|
reject(ctx, e.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PromisePolyfill(int state, Object val) {
|
||||||
|
this.state = state;
|
||||||
|
this.val = val;
|
||||||
|
}
|
||||||
|
public PromisePolyfill() {
|
||||||
|
this(STATE_PENDING, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeInit(InitType.PROTOTYPE) public static void init(Environment env, ObjectValue target) {
|
||||||
|
target.defineProperty(null, env.symbol("Symbol.typeName"), "Promise");
|
||||||
|
}
|
||||||
|
}
|
20
src/me/topchetoeu/jscript/polyfills/RangeErrorPolyfill.java
Normal file
20
src/me/topchetoeu/jscript/polyfills/RangeErrorPolyfill.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
|
import me.topchetoeu.jscript.interop.InitType;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
|
public class RangeErrorPolyfill extends ErrorPolyfill {
|
||||||
|
@NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) throws InterruptedException {
|
||||||
|
var target = ErrorPolyfill.constructor(ctx, thisArg, message);
|
||||||
|
target.defineProperty(ctx, "name", "RangeError");
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
@NativeInit(InitType.PROTOTYPE) public static void init(Environment env, ObjectValue target) {
|
||||||
|
target.defineProperty(null, env.symbol("Symbol.typeName"), "RangeError");
|
||||||
|
target.defineProperty(null, "name", "RangeError");
|
||||||
|
}
|
||||||
|
}
|
@ -1,187 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.polyfills;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
|
||||||
import me.topchetoeu.jscript.engine.values.NativeWrapper;
|
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
|
||||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
|
||||||
import me.topchetoeu.jscript.interop.NativeSetter;
|
|
||||||
|
|
||||||
public class RegExp {
|
|
||||||
// I used Regex to analyze Regex
|
|
||||||
private static final Pattern NAMED_PATTERN = Pattern.compile("\\(\\?<([^=!].*?)>", Pattern.DOTALL);
|
|
||||||
private static final Pattern ESCAPE_PATTERN = Pattern.compile("[/\\-\\\\^$*+?.()|\\[\\]{}]");
|
|
||||||
|
|
||||||
private static String cleanupPattern(Context ctx, Object val) throws InterruptedException {
|
|
||||||
if (val == null) return "(?:)";
|
|
||||||
if (val instanceof RegExp) return ((RegExp)val).source;
|
|
||||||
if (val instanceof NativeWrapper && ((NativeWrapper)val).wrapped instanceof RegExp) {
|
|
||||||
return ((RegExp)((NativeWrapper)val).wrapped).source;
|
|
||||||
}
|
|
||||||
var res = Values.toString(ctx, val);
|
|
||||||
if (res.equals("")) return "(?:)";
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
private static String cleanupFlags(Context ctx, Object val) throws InterruptedException {
|
|
||||||
if (val == null) return "";
|
|
||||||
return Values.toString(ctx, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean checkEscaped(String s, int pos) {
|
|
||||||
int n = 0;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (pos <= 0) break;
|
|
||||||
if (s.charAt(pos) != '\\') break;
|
|
||||||
n++;
|
|
||||||
pos--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (n % 2) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Native
|
|
||||||
public static RegExp escape(Context ctx, Object raw, Object flags) throws InterruptedException {
|
|
||||||
return escape(Values.toString(ctx, raw), cleanupFlags(ctx, flags));
|
|
||||||
}
|
|
||||||
public static RegExp escape(String raw, String flags) {
|
|
||||||
return new RegExp(ESCAPE_PATTERN.matcher(raw).replaceAll("\\\\$0"), flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Pattern pattern;
|
|
||||||
private String[] namedGroups;
|
|
||||||
private int flags;
|
|
||||||
private int lastI = 0;
|
|
||||||
|
|
||||||
@Native
|
|
||||||
public final String source;
|
|
||||||
@Native
|
|
||||||
public final boolean hasIndices;
|
|
||||||
@Native
|
|
||||||
public final boolean global;
|
|
||||||
@Native
|
|
||||||
public final boolean sticky;
|
|
||||||
|
|
||||||
@NativeGetter("ignoreCase")
|
|
||||||
public boolean ignoreCase() { return (flags & Pattern.CASE_INSENSITIVE) != 0; }
|
|
||||||
@NativeGetter("multiline")
|
|
||||||
public boolean multiline() { return (flags & Pattern.MULTILINE) != 0; }
|
|
||||||
@NativeGetter("unicode")
|
|
||||||
public boolean unicode() { return (flags & Pattern.UNICODE_CHARACTER_CLASS) != 0; }
|
|
||||||
@NativeGetter("unicode")
|
|
||||||
public boolean dotAll() { return (flags & Pattern.DOTALL) != 0; }
|
|
||||||
|
|
||||||
@NativeGetter("lastIndex")
|
|
||||||
public int lastIndex() { return lastI; }
|
|
||||||
@NativeSetter("lastIndex")
|
|
||||||
public void setLastIndex(Context ctx, Object i) throws InterruptedException {
|
|
||||||
lastI = (int)Values.toNumber(ctx, i);
|
|
||||||
}
|
|
||||||
public void setLastIndex(int i) {
|
|
||||||
lastI = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NativeGetter("flags")
|
|
||||||
public final String flags() {
|
|
||||||
String res = "";
|
|
||||||
if (hasIndices) res += 'd';
|
|
||||||
if (global) res += 'g';
|
|
||||||
if (ignoreCase()) res += 'i';
|
|
||||||
if (multiline()) res += 'm';
|
|
||||||
if (dotAll()) res += 's';
|
|
||||||
if (unicode()) res += 'u';
|
|
||||||
if (sticky) res += 'y';
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Native
|
|
||||||
public Object exec(Context ctx, Object str) throws InterruptedException {
|
|
||||||
return exec(Values.toString(ctx, str));
|
|
||||||
}
|
|
||||||
public Object exec(String str) {
|
|
||||||
var matcher = pattern.matcher(str);
|
|
||||||
if (lastI > str.length() || !matcher.find(lastI) || sticky && matcher.start() != lastI) {
|
|
||||||
lastI = 0;
|
|
||||||
return Values.NULL;
|
|
||||||
}
|
|
||||||
if (sticky || global) {
|
|
||||||
lastI = matcher.end();
|
|
||||||
if (matcher.end() == matcher.start()) lastI++;
|
|
||||||
}
|
|
||||||
|
|
||||||
var obj = new ArrayValue();
|
|
||||||
var groups = new ObjectValue();
|
|
||||||
|
|
||||||
for (var el : namedGroups) {
|
|
||||||
try {
|
|
||||||
groups.defineProperty(null, el, matcher.group(el));
|
|
||||||
}
|
|
||||||
catch (IllegalArgumentException e) { }
|
|
||||||
}
|
|
||||||
if (groups.values.size() == 0) groups = null;
|
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < matcher.groupCount() + 1; i++) {
|
|
||||||
obj.set(null, i, matcher.group(i));
|
|
||||||
}
|
|
||||||
obj.defineProperty(null, "groups", groups);
|
|
||||||
obj.defineProperty(null, "index", matcher.start());
|
|
||||||
obj.defineProperty(null, "input", str);
|
|
||||||
|
|
||||||
if (hasIndices) {
|
|
||||||
var indices = new ArrayValue();
|
|
||||||
for (int i = 0; i < matcher.groupCount() + 1; i++) {
|
|
||||||
indices.set(null, i, new ArrayValue(null, matcher.start(i), matcher.end(i)));
|
|
||||||
}
|
|
||||||
var groupIndices = new ObjectValue();
|
|
||||||
for (var el : namedGroups) {
|
|
||||||
groupIndices.defineProperty(null, el, new ArrayValue(null, matcher.start(el), matcher.end(el)));
|
|
||||||
}
|
|
||||||
indices.defineProperty(null, "groups", groupIndices);
|
|
||||||
obj.defineProperty(null, "indices", indices);
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Native
|
|
||||||
public RegExp(Context ctx, Object pattern, Object flags) throws InterruptedException {
|
|
||||||
this(cleanupPattern(ctx, pattern), cleanupFlags(ctx, flags));
|
|
||||||
}
|
|
||||||
public RegExp(String pattern, String flags) {
|
|
||||||
if (pattern == null || pattern.equals("")) pattern = "(?:)";
|
|
||||||
if (flags == null || flags.equals("")) flags = "";
|
|
||||||
|
|
||||||
this.flags = 0;
|
|
||||||
this.hasIndices = flags.contains("d");
|
|
||||||
this.global = flags.contains("g");
|
|
||||||
this.sticky = flags.contains("y");
|
|
||||||
this.source = pattern;
|
|
||||||
|
|
||||||
if (flags.contains("i")) this.flags |= Pattern.CASE_INSENSITIVE;
|
|
||||||
if (flags.contains("m")) this.flags |= Pattern.MULTILINE;
|
|
||||||
if (flags.contains("s")) this.flags |= Pattern.DOTALL;
|
|
||||||
if (flags.contains("u")) this.flags |= Pattern.UNICODE_CHARACTER_CLASS;
|
|
||||||
|
|
||||||
this.pattern = Pattern.compile(pattern.replace("\\d", "[0-9]"), this.flags);
|
|
||||||
|
|
||||||
var matcher = NAMED_PATTERN.matcher(source);
|
|
||||||
var groups = new ArrayList<String>();
|
|
||||||
|
|
||||||
while (matcher.find()) {
|
|
||||||
if (!checkEscaped(source, matcher.start() - 1)) {
|
|
||||||
groups.add(matcher.group(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namedGroups = groups.toArray(String[]::new);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RegExp(String pattern) { this(pattern, null); }
|
|
||||||
public RegExp() { this(null, null); }
|
|
||||||
}
|
|
297
src/me/topchetoeu/jscript/polyfills/RegExpPolyfill.java
Normal file
297
src/me/topchetoeu/jscript/polyfills/RegExpPolyfill.java
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.NativeWrapper;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
|
|
||||||
|
public class RegExpPolyfill {
|
||||||
|
// I used Regex to analyze Regex
|
||||||
|
private static final Pattern NAMED_PATTERN = Pattern.compile("\\(\\?<([^=!].*?)>", Pattern.DOTALL);
|
||||||
|
private static final Pattern ESCAPE_PATTERN = Pattern.compile("[/\\-\\\\^$*+?.()|\\[\\]{}]");
|
||||||
|
|
||||||
|
private static String cleanupPattern(Context ctx, Object val) throws InterruptedException {
|
||||||
|
if (val == null) return "(?:)";
|
||||||
|
if (val instanceof RegExpPolyfill) return ((RegExpPolyfill)val).source;
|
||||||
|
if (val instanceof NativeWrapper && ((NativeWrapper)val).wrapped instanceof RegExpPolyfill) {
|
||||||
|
return ((RegExpPolyfill)((NativeWrapper)val).wrapped).source;
|
||||||
|
}
|
||||||
|
var res = Values.toString(ctx, val);
|
||||||
|
if (res.equals("")) return "(?:)";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
private static String cleanupFlags(Context ctx, Object val) throws InterruptedException {
|
||||||
|
if (val == null) return "";
|
||||||
|
return Values.toString(ctx, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean checkEscaped(String s, int pos) {
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (pos <= 0) break;
|
||||||
|
if (s.charAt(pos) != '\\') break;
|
||||||
|
n++;
|
||||||
|
pos--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (n % 2) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native
|
||||||
|
public static RegExpPolyfill escape(Context ctx, Object raw, Object flags) throws InterruptedException {
|
||||||
|
return escape(Values.toString(ctx, raw), cleanupFlags(ctx, flags));
|
||||||
|
}
|
||||||
|
public static RegExpPolyfill escape(String raw, String flags) {
|
||||||
|
return new RegExpPolyfill(ESCAPE_PATTERN.matcher(raw).replaceAll("\\\\$0"), flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pattern pattern;
|
||||||
|
private String[] namedGroups;
|
||||||
|
private int flags;
|
||||||
|
|
||||||
|
@Native public int lastI = 0;
|
||||||
|
@Native public final String source;
|
||||||
|
@Native public final boolean hasIndices;
|
||||||
|
@Native public final boolean global;
|
||||||
|
@Native public final boolean sticky;
|
||||||
|
@Native("@@Symbol.typeName") public final String name = "RegExp";
|
||||||
|
|
||||||
|
@NativeGetter public boolean ignoreCase() { return (flags & Pattern.CASE_INSENSITIVE) != 0; }
|
||||||
|
@NativeGetter public boolean multiline() { return (flags & Pattern.MULTILINE) != 0; }
|
||||||
|
@NativeGetter public boolean unicode() { return (flags & Pattern.UNICODE_CHARACTER_CLASS) != 0; }
|
||||||
|
@NativeGetter public boolean dotAll() { return (flags & Pattern.DOTALL) != 0; }
|
||||||
|
|
||||||
|
@NativeGetter("flags") public final String flags() {
|
||||||
|
String res = "";
|
||||||
|
if (hasIndices) res += 'd';
|
||||||
|
if (global) res += 'g';
|
||||||
|
if (ignoreCase()) res += 'i';
|
||||||
|
if (multiline()) res += 'm';
|
||||||
|
if (dotAll()) res += 's';
|
||||||
|
if (unicode()) res += 'u';
|
||||||
|
if (sticky) res += 'y';
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Native public Object exec(String str) {
|
||||||
|
var matcher = pattern.matcher(str);
|
||||||
|
if (lastI > str.length() || !matcher.find(lastI) || sticky && matcher.start() != lastI) {
|
||||||
|
lastI = 0;
|
||||||
|
return Values.NULL;
|
||||||
|
}
|
||||||
|
if (sticky || global) {
|
||||||
|
lastI = matcher.end();
|
||||||
|
if (matcher.end() == matcher.start()) lastI++;
|
||||||
|
}
|
||||||
|
|
||||||
|
var obj = new ArrayValue();
|
||||||
|
var groups = new ObjectValue();
|
||||||
|
|
||||||
|
for (var el : namedGroups) {
|
||||||
|
try {
|
||||||
|
groups.defineProperty(null, el, matcher.group(el));
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) { }
|
||||||
|
}
|
||||||
|
if (groups.values.size() == 0) groups = null;
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < matcher.groupCount() + 1; i++) {
|
||||||
|
obj.set(null, i, matcher.group(i));
|
||||||
|
}
|
||||||
|
obj.defineProperty(null, "groups", groups);
|
||||||
|
obj.defineProperty(null, "index", matcher.start());
|
||||||
|
obj.defineProperty(null, "input", str);
|
||||||
|
|
||||||
|
if (hasIndices) {
|
||||||
|
var indices = new ArrayValue();
|
||||||
|
for (int i = 0; i < matcher.groupCount() + 1; i++) {
|
||||||
|
indices.set(null, i, new ArrayValue(null, matcher.start(i), matcher.end(i)));
|
||||||
|
}
|
||||||
|
var groupIndices = new ObjectValue();
|
||||||
|
for (var el : namedGroups) {
|
||||||
|
groupIndices.defineProperty(null, el, new ArrayValue(null, matcher.start(el), matcher.end(el)));
|
||||||
|
}
|
||||||
|
indices.defineProperty(null, "groups", groupIndices);
|
||||||
|
obj.defineProperty(null, "indices", indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public boolean test(String str) {
|
||||||
|
return this.exec(str) != Values.NULL;
|
||||||
|
}
|
||||||
|
@Native public String toString() {
|
||||||
|
return "/" + source + "/" + flags();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native("@@Symvol.match") public Object match(Context ctx, String target) throws InterruptedException {
|
||||||
|
if (this.global) {
|
||||||
|
var res = new ArrayValue();
|
||||||
|
Object val;
|
||||||
|
while ((val = this.exec(target)) != Values.NULL) {
|
||||||
|
res.set(ctx, res.size(), Values.getMember(ctx, val, 0));
|
||||||
|
}
|
||||||
|
lastI = 0;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var res = this.exec(target);
|
||||||
|
if (!this.sticky) this.lastI = 0;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native("@@Symvol.matchAll") public Object matchAll(Context ctx, String target) throws InterruptedException {
|
||||||
|
var pattern = new RegExpPolyfill(this.source, this.flags() + "g");
|
||||||
|
|
||||||
|
return Values.fromJavaIterator(ctx, new Iterator<Object>() {
|
||||||
|
private Object val = null;
|
||||||
|
private boolean updated = false;
|
||||||
|
|
||||||
|
private void update() {
|
||||||
|
if (!updated) val = pattern.exec(target);
|
||||||
|
}
|
||||||
|
@Override public boolean hasNext() {
|
||||||
|
update();
|
||||||
|
return val != Values.NULL;
|
||||||
|
}
|
||||||
|
@Override public Object next() {
|
||||||
|
update();
|
||||||
|
updated = false;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native("@@Symvol.split") public ArrayValue split(Context ctx, String target, Object limit, boolean sensible) throws InterruptedException {
|
||||||
|
var pattern = new RegExpPolyfill(this.source, this.flags() + "g");
|
||||||
|
Object match;
|
||||||
|
int lastEnd = 0;
|
||||||
|
var res = new ArrayValue();
|
||||||
|
var lim = limit == null ? 0 : Values.toNumber(ctx, limit);
|
||||||
|
|
||||||
|
while ((match = pattern.exec(target)) != Values.NULL) {
|
||||||
|
var added = new ArrayList<String>();
|
||||||
|
var arrMatch = (ArrayValue)match;
|
||||||
|
int index = (int)Values.toNumber(ctx, Values.getMember(ctx, match, "index"));
|
||||||
|
var matchVal = (String)arrMatch.get(0);
|
||||||
|
|
||||||
|
if (index >= target.length()) break;
|
||||||
|
|
||||||
|
if (matchVal.length() == 0 || index - lastEnd > 0) {
|
||||||
|
added.add(target.substring(lastEnd, pattern.lastI));
|
||||||
|
if (pattern.lastI < target.length()) {
|
||||||
|
for (var i = 1; i < arrMatch.size(); i++) added.add((String)arrMatch.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (var i = 1; i < arrMatch.size(); i++) added.add((String)arrMatch.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sensible) {
|
||||||
|
if (limit != null && res.size() + added.size() >= lim) break;
|
||||||
|
else for (var i = 0; i < added.size(); i++) res.set(ctx, res.size(), added.get(i));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (var i = 0; i < added.size(); i++) {
|
||||||
|
if (limit != null && res.size() >= lim) return res;
|
||||||
|
else res.set(ctx, res.size(), added.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastEnd = pattern.lastI;
|
||||||
|
}
|
||||||
|
if (lastEnd < target.length()) {
|
||||||
|
res.set(ctx, res.size(), target.substring(lastEnd));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
// [Symbol.replace](target, replacement) {
|
||||||
|
// const pattern = new this.constructor(this, this.flags + "d") as RegExp;
|
||||||
|
// let match: RegExpResult | null;
|
||||||
|
// let lastEnd = 0;
|
||||||
|
// const res: string[] = [];
|
||||||
|
// // log(pattern.toString());
|
||||||
|
// while ((match = pattern.exec(target)) !== null) {
|
||||||
|
// const indices = match.indices![0];
|
||||||
|
// res.push(target.substring(lastEnd, indices[0]));
|
||||||
|
// if (replacement instanceof Function) {
|
||||||
|
// res.push(replacement(target.substring(indices[0], indices[1]), ...match.slice(1), indices[0], target));
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// res.push(replacement);
|
||||||
|
// }
|
||||||
|
// lastEnd = indices[1];
|
||||||
|
// if (!pattern.global) break;
|
||||||
|
// }
|
||||||
|
// if (lastEnd < target.length) {
|
||||||
|
// res.push(target.substring(lastEnd));
|
||||||
|
// }
|
||||||
|
// return res.join('');
|
||||||
|
// },
|
||||||
|
// [Symbol.search](target, reverse, start) {
|
||||||
|
// const pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp;
|
||||||
|
// if (!reverse) {
|
||||||
|
// pattern.lastIndex = (start as any) | 0;
|
||||||
|
// const res = pattern.exec(target);
|
||||||
|
// if (res) return res.index;
|
||||||
|
// else return -1;
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// start ??= target.length;
|
||||||
|
// start |= 0;
|
||||||
|
// let res: RegExpResult | null = null;
|
||||||
|
// while (true) {
|
||||||
|
// const tmp = pattern.exec(target);
|
||||||
|
// if (tmp === null || tmp.index > start) break;
|
||||||
|
// res = tmp;
|
||||||
|
// }
|
||||||
|
// if (res && res.index <= start) return res.index;
|
||||||
|
// else return -1;
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
@Native public RegExpPolyfill(Context ctx, Object pattern, Object flags) throws InterruptedException {
|
||||||
|
this(cleanupPattern(ctx, pattern), cleanupFlags(ctx, flags));
|
||||||
|
}
|
||||||
|
public RegExpPolyfill(String pattern, String flags) {
|
||||||
|
if (pattern == null || pattern.equals("")) pattern = "(?:)";
|
||||||
|
if (flags == null || flags.equals("")) flags = "";
|
||||||
|
|
||||||
|
this.flags = 0;
|
||||||
|
this.hasIndices = flags.contains("d");
|
||||||
|
this.global = flags.contains("g");
|
||||||
|
this.sticky = flags.contains("y");
|
||||||
|
this.source = pattern;
|
||||||
|
|
||||||
|
if (flags.contains("i")) this.flags |= Pattern.CASE_INSENSITIVE;
|
||||||
|
if (flags.contains("m")) this.flags |= Pattern.MULTILINE;
|
||||||
|
if (flags.contains("s")) this.flags |= Pattern.DOTALL;
|
||||||
|
if (flags.contains("u")) this.flags |= Pattern.UNICODE_CHARACTER_CLASS;
|
||||||
|
|
||||||
|
this.pattern = Pattern.compile(pattern.replace("\\d", "[0-9]"), this.flags);
|
||||||
|
|
||||||
|
var matcher = NAMED_PATTERN.matcher(source);
|
||||||
|
var groups = new ArrayList<String>();
|
||||||
|
|
||||||
|
while (matcher.find()) {
|
||||||
|
if (!checkEscaped(source, matcher.start() - 1)) {
|
||||||
|
groups.add(matcher.group(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namedGroups = groups.toArray(String[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RegExpPolyfill(String pattern) { this(pattern, null); }
|
||||||
|
public RegExpPolyfill() { this(null, null); }
|
||||||
|
}
|
63
src/me/topchetoeu/jscript/polyfills/SetPolyfill.java
Normal file
63
src/me/topchetoeu/jscript/polyfills/SetPolyfill.java
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
|
|
||||||
|
public class SetPolyfill {
|
||||||
|
private LinkedHashSet<Object> set = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
@Native("@@Symbol.typeName") public final String name = "Set";
|
||||||
|
@Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) throws InterruptedException {
|
||||||
|
return this.values(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public ObjectValue entries(Context ctx) throws InterruptedException {
|
||||||
|
var res = set.stream().map(v -> new ArrayValue(ctx, v, v)).collect(Collectors.toList());
|
||||||
|
return Values.fromJavaIterator(ctx, res.iterator());
|
||||||
|
}
|
||||||
|
@Native public ObjectValue keys(Context ctx) throws InterruptedException {
|
||||||
|
var res = new ArrayList<>(set);
|
||||||
|
return Values.fromJavaIterator(ctx, res.iterator());
|
||||||
|
}
|
||||||
|
@Native public ObjectValue values(Context ctx) throws InterruptedException {
|
||||||
|
var res = new ArrayList<>(set);
|
||||||
|
return Values.fromJavaIterator(ctx, res.iterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public Object add(Object key) {
|
||||||
|
return set.add(key);
|
||||||
|
}
|
||||||
|
@Native public boolean delete(Object key) {
|
||||||
|
return set.remove(key);
|
||||||
|
}
|
||||||
|
@Native public boolean has(Object key) {
|
||||||
|
return set.contains(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public void clear() {
|
||||||
|
set.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeGetter public int size() {
|
||||||
|
return set.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeGetter public void forEach(Context ctx, FunctionValue func, Object thisArg) throws InterruptedException {
|
||||||
|
var keys = new ArrayList<>(set);
|
||||||
|
|
||||||
|
for (var el : keys) func.call(ctx, thisArg, el, el, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public SetPolyfill(Context ctx, Object iterable) throws InterruptedException {
|
||||||
|
for (var el : Values.toJavaIterable(ctx, iterable)) add(el);
|
||||||
|
}
|
||||||
|
}
|
260
src/me/topchetoeu/jscript/polyfills/StringPolyfill.java
Normal file
260
src/me/topchetoeu/jscript/polyfills/StringPolyfill.java
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
import me.topchetoeu.jscript.interop.InitType;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
|
// TODO: implement index wrapping properly
|
||||||
|
public class StringPolyfill {
|
||||||
|
public final String value;
|
||||||
|
|
||||||
|
private static String passThis(Context ctx, String funcName, Object val) throws InterruptedException {
|
||||||
|
if (val instanceof StringPolyfill) return ((StringPolyfill)val).value;
|
||||||
|
else if (val instanceof String) return (String)val;
|
||||||
|
else throw EngineException.ofType(String.format("'%s' may only be called upon object and primitve strings.", funcName));
|
||||||
|
}
|
||||||
|
private static int normalizeI(int i, int len, boolean clamp) {
|
||||||
|
if (i < 0) i += len;
|
||||||
|
if (clamp) {
|
||||||
|
if (i < 0) i = 0;
|
||||||
|
if (i >= len) i = len;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeGetter(thisArg = true) public static int length(Context ctx, Object thisArg) throws InterruptedException {
|
||||||
|
return passThis(ctx, "substring", thisArg).length();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static String substring(Context ctx, Object thisArg, int start, Object _end) throws InterruptedException {
|
||||||
|
var val = passThis(ctx, "substring", thisArg);
|
||||||
|
start = normalizeI(start, val.length(), true);
|
||||||
|
int end = normalizeI(_end == null ? val.length() : (int)Values.toNumber(ctx, _end), val.length(), true);
|
||||||
|
|
||||||
|
return val.substring(start, end);
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static String substr(Context ctx, Object thisArg, int start, Object _len) throws InterruptedException {
|
||||||
|
var val = passThis(ctx, "substr", thisArg);
|
||||||
|
int len = _len == null ? val.length() - start : (int)Values.toNumber(ctx, _len);
|
||||||
|
return substring(ctx, val, start, start + len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static String toLowerCase(Context ctx, Object thisArg) throws InterruptedException {
|
||||||
|
return passThis(ctx, "toLowerCase", thisArg).toLowerCase();
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static String toUpperCase(Context ctx, Object thisArg) throws InterruptedException {
|
||||||
|
return passThis(ctx, "toUpperCase", thisArg).toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static String charAt(Context ctx, Object thisArg, int i) throws InterruptedException {
|
||||||
|
return passThis(ctx, "charAt", thisArg).charAt(i) + "";
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static int charCodeAt(Context ctx, Object thisArg, int i) throws InterruptedException {
|
||||||
|
return passThis(ctx, "charCodeAt", thisArg).charAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static boolean startsWith(Context ctx, Object thisArg, String term, int pos) throws InterruptedException {
|
||||||
|
return passThis(ctx, "startsWith", thisArg).startsWith(term, pos);
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static boolean endsWith(Context ctx, Object thisArg, String term, int pos) throws InterruptedException {
|
||||||
|
var val = passThis(ctx, "endsWith", thisArg);
|
||||||
|
return val.lastIndexOf(term, pos) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static int indexOf(Context ctx, Object thisArg, Object term, int start) throws InterruptedException {
|
||||||
|
var val = passThis(ctx, "indexOf", thisArg);
|
||||||
|
|
||||||
|
if (term != null && term != Values.NULL && !(term instanceof String)) {
|
||||||
|
var search = Values.getMember(ctx, term, ctx.env.symbol("Symbol.search"));
|
||||||
|
if (search instanceof FunctionValue) {
|
||||||
|
return (int)Values.toNumber(ctx, ((FunctionValue)search).call(ctx, term, val, false, start));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return val.indexOf(Values.toString(ctx, term), start);
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static int lastIndexOf(Context ctx, Object thisArg, Object term, int pos) throws InterruptedException {
|
||||||
|
var val = passThis(ctx, "lastIndexOf", thisArg);
|
||||||
|
|
||||||
|
if (term != null && term != Values.NULL && !(term instanceof String)) {
|
||||||
|
var search = Values.getMember(ctx, term, ctx.env.symbol("Symbol.search"));
|
||||||
|
if (search instanceof FunctionValue) {
|
||||||
|
return (int)Values.toNumber(ctx, ((FunctionValue)search).call(ctx, term, val, true, pos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return val.lastIndexOf(Values.toString(ctx, term), pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static boolean includes(Context ctx, Object thisArg, Object term, int pos) throws InterruptedException {
|
||||||
|
return lastIndexOf(ctx, passThis(ctx, "includes", thisArg), term, pos) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static String replace(Context ctx, Object thisArg, Object term, String replacement) throws InterruptedException {
|
||||||
|
var val = passThis(ctx, "replace", thisArg);
|
||||||
|
|
||||||
|
if (term != null && term != Values.NULL && !(term instanceof String)) {
|
||||||
|
var replace = Values.getMember(ctx, term, ctx.env.symbol("Symbol.replace"));
|
||||||
|
if (replace instanceof FunctionValue) {
|
||||||
|
return Values.toString(ctx, ((FunctionValue)replace).call(ctx, term, val, replacement));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return val.replaceFirst(Pattern.quote(Values.toString(ctx, term)), replacement);
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static String replaceAll(Context ctx, Object thisArg, Object term, String replacement) throws InterruptedException {
|
||||||
|
var val = passThis(ctx, "replaceAll", thisArg);
|
||||||
|
|
||||||
|
if (term != null && term != Values.NULL && !(term instanceof String)) {
|
||||||
|
var replace = Values.getMember(ctx, term, ctx.env.symbol("Symbol.replace"));
|
||||||
|
if (replace instanceof FunctionValue) {
|
||||||
|
return Values.toString(ctx, ((FunctionValue)replace).call(ctx, term, val, replacement));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return val.replaceFirst(Pattern.quote(Values.toString(ctx, term)), replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static ArrayValue match(Context ctx, Object thisArg, Object term, String replacement) throws InterruptedException {
|
||||||
|
var val = passThis(ctx, "match", thisArg);
|
||||||
|
|
||||||
|
FunctionValue match;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var _match = Values.getMember(ctx, term, ctx.env.symbol("Symbol.match"));
|
||||||
|
if (_match instanceof FunctionValue) match = (FunctionValue)_match;
|
||||||
|
else if (ctx.env.regexConstructor != null) {
|
||||||
|
var regex = Values.callNew(ctx, ctx.env.regexConstructor, Values.toString(ctx, term), "");
|
||||||
|
_match = Values.getMember(ctx, regex, ctx.env.symbol("Symbol.match"));
|
||||||
|
if (_match instanceof FunctionValue) match = (FunctionValue)_match;
|
||||||
|
else throw EngineException.ofError("Regular expressions don't support matching.");
|
||||||
|
}
|
||||||
|
else throw EngineException.ofError("Regular expressions not supported.");
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) { return new ArrayValue(ctx, ""); }
|
||||||
|
|
||||||
|
var res = match.call(ctx, term, val);
|
||||||
|
if (res instanceof ArrayValue) return (ArrayValue)res;
|
||||||
|
else return new ArrayValue(ctx, "");
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static Object matchAll(Context ctx, Object thisArg, Object term, String replacement) throws InterruptedException {
|
||||||
|
var val = passThis(ctx, "matchAll", thisArg);
|
||||||
|
|
||||||
|
FunctionValue match = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var _match = Values.getMember(ctx, term, ctx.env.symbol("Symbol.matchAll"));
|
||||||
|
if (_match instanceof FunctionValue) match = (FunctionValue)_match;
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) { }
|
||||||
|
|
||||||
|
if (match == null && ctx.env.regexConstructor != null) {
|
||||||
|
var regex = Values.callNew(ctx, ctx.env.regexConstructor, Values.toString(ctx, term), "g");
|
||||||
|
var _match = Values.getMember(ctx, regex, ctx.env.symbol("Symbol.matchAll"));
|
||||||
|
if (_match instanceof FunctionValue) match = (FunctionValue)_match;
|
||||||
|
else throw EngineException.ofError("Regular expressions don't support matching.");
|
||||||
|
}
|
||||||
|
else throw EngineException.ofError("Regular expressions not supported.");
|
||||||
|
|
||||||
|
return match.call(ctx, term, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static ArrayValue split(Context ctx, Object thisArg, Object term, Object lim, boolean sensible) throws InterruptedException {
|
||||||
|
var val = passThis(ctx, "split", thisArg);
|
||||||
|
|
||||||
|
if (lim != null) lim = Values.toNumber(ctx, lim);
|
||||||
|
|
||||||
|
if (term != null && term != Values.NULL && !(term instanceof String)) {
|
||||||
|
var replace = Values.getMember(ctx, term, ctx.env.symbol("Symbol.replace"));
|
||||||
|
if (replace instanceof FunctionValue) {
|
||||||
|
var tmp = ((FunctionValue)replace).call(ctx, term, val, lim, sensible);
|
||||||
|
|
||||||
|
if (tmp instanceof ArrayValue) {
|
||||||
|
var parts = new ArrayValue(((ArrayValue)tmp).size());
|
||||||
|
for (int i = 0; i < parts.size(); i++) parts.set(ctx, i, Values.toString(ctx, ((ArrayValue)tmp).get(i)));
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] parts;
|
||||||
|
var pattern = Pattern.quote(Values.toString(ctx, term));
|
||||||
|
|
||||||
|
if (lim == null) parts = val.split(pattern);
|
||||||
|
else if (sensible) parts = val.split(pattern, (int)(double)lim);
|
||||||
|
else {
|
||||||
|
var limit = (int)(double)lim;
|
||||||
|
parts = val.split(pattern, limit + 1);
|
||||||
|
ArrayValue res;
|
||||||
|
|
||||||
|
if (parts.length > limit) res = new ArrayValue(limit);
|
||||||
|
else res = new ArrayValue(parts.length);
|
||||||
|
|
||||||
|
for (var i = 0; i < parts.length && i < limit; i++) res.set(ctx, i, parts[i]);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
var res = new ArrayValue(parts.length);
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
for (; i < parts.length; i++) {
|
||||||
|
if (lim != null && (double)lim <= i) break;
|
||||||
|
res.set(ctx, i, parts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static String slice(Context ctx, Object thisArg, int start, Object _end) throws InterruptedException {
|
||||||
|
return substring(ctx, passThis(ctx, "slice", thisArg), start, _end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static String concat(Context ctx, Object thisArg, Object... args) throws InterruptedException {
|
||||||
|
var res = new StringBuilder(passThis(ctx, "concat", thisArg));
|
||||||
|
|
||||||
|
for (var el : args) res.append(Values.toString(ctx, el));
|
||||||
|
|
||||||
|
return res.toString();
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static String trim(Context ctx, Object thisArg) throws InterruptedException {
|
||||||
|
return passThis(ctx, "trim", thisArg).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object val) throws InterruptedException {
|
||||||
|
val = Values.toString(ctx, val);
|
||||||
|
if (thisArg instanceof ObjectValue) return new StringPolyfill((String)val);
|
||||||
|
else return val;
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static String toString(Context ctx, Object thisArg) throws InterruptedException {
|
||||||
|
return passThis(ctx, "toString", thisArg);
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static String valueOf(Context ctx, Object thisArg) throws InterruptedException {
|
||||||
|
return passThis(ctx, "valueOf", thisArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public static String fromCharCode(int ...val) {
|
||||||
|
char[] arr = new char[val.length];
|
||||||
|
for (var i = 0; i < val.length; i++) arr[i] = (char)val[i];
|
||||||
|
return new String(arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringPolyfill(String val) {
|
||||||
|
this.value = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeInit(InitType.PROTOTYPE) public static void init(Environment env, ObjectValue target) {
|
||||||
|
target.defineProperty(null, env.symbol("Symbol.typeName"), "String");
|
||||||
|
}
|
||||||
|
}
|
69
src/me/topchetoeu/jscript/polyfills/SymbolPolyfill.java
Normal file
69
src/me/topchetoeu/jscript/polyfills/SymbolPolyfill.java
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
|
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.InitType;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
|
public class SymbolPolyfill {
|
||||||
|
private static final Map<String, Symbol> symbols = new HashMap<>();
|
||||||
|
|
||||||
|
@NativeGetter public static Symbol typeName(Context ctx) { return ctx.env.symbol("Symbol.typeName"); }
|
||||||
|
@NativeGetter public static Symbol replace(Context ctx) { return ctx.env.symbol("Symbol.replace"); }
|
||||||
|
@NativeGetter public static Symbol match(Context ctx) { return ctx.env.symbol("Symbol.match"); }
|
||||||
|
@NativeGetter public static Symbol matchAll(Context ctx) { return ctx.env.symbol("Symbol.matchAll"); }
|
||||||
|
@NativeGetter public static Symbol split(Context ctx) { return ctx.env.symbol("Symbol.split"); }
|
||||||
|
@NativeGetter public static Symbol search(Context ctx) { return ctx.env.symbol("Symbol.search"); }
|
||||||
|
@NativeGetter public static Symbol iterator(Context ctx) { return ctx.env.symbol("Symbol.iterator"); }
|
||||||
|
@NativeGetter public static Symbol asyncIterator(Context ctx) { return ctx.env.symbol("Symbol.asyncIterator"); }
|
||||||
|
|
||||||
|
public final Symbol value;
|
||||||
|
|
||||||
|
private static Symbol passThis(Context ctx, String funcName, Object val) throws InterruptedException {
|
||||||
|
if (val instanceof SymbolPolyfill) return ((SymbolPolyfill)val).value;
|
||||||
|
else if (val instanceof Symbol) return (Symbol)val;
|
||||||
|
else throw EngineException.ofType(String.format("'%s' may only be called upon object and primitve symbols.", funcName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object val) throws InterruptedException {
|
||||||
|
if (thisArg instanceof ObjectValue) throw EngineException.ofType("Symbol constructor may not be called with new.");
|
||||||
|
if (val == null) return new Symbol("");
|
||||||
|
else return new Symbol(Values.toString(ctx, val));
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static String toString(Context ctx, Object thisArg) throws InterruptedException {
|
||||||
|
return passThis(ctx, "toString", thisArg).value;
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static Symbol valueOf(Context ctx, Object thisArg) throws InterruptedException {
|
||||||
|
return passThis(ctx, "valueOf", thisArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native("for") public static Symbol _for(String key) {
|
||||||
|
if (symbols.containsKey(key)) return symbols.get(key);
|
||||||
|
else {
|
||||||
|
var sym = new Symbol(key);
|
||||||
|
symbols.put(key, sym);
|
||||||
|
return sym;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Native public static String keyFor(Symbol sym) {
|
||||||
|
return sym.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SymbolPolyfill(Symbol val) {
|
||||||
|
this.value = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeInit(InitType.PROTOTYPE) public static void init(Environment env, ObjectValue target) {
|
||||||
|
target.defineProperty(null, env.symbol("Symbol.typeName"), "Symbol");
|
||||||
|
}
|
||||||
|
}
|
20
src/me/topchetoeu/jscript/polyfills/SyntaxErrorPolyfill.java
Normal file
20
src/me/topchetoeu/jscript/polyfills/SyntaxErrorPolyfill.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
|
import me.topchetoeu.jscript.interop.InitType;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
|
public class SyntaxErrorPolyfill extends ErrorPolyfill {
|
||||||
|
@NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) throws InterruptedException {
|
||||||
|
var target = ErrorPolyfill.constructor(ctx, thisArg, message);
|
||||||
|
target.defineProperty(ctx, "name", "SyntaxError");
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
@NativeInit(InitType.PROTOTYPE) public static void init(Environment env, ObjectValue target) {
|
||||||
|
target.defineProperty(null, env.symbol("Symbol.typeName"), "SyntaxError");
|
||||||
|
target.defineProperty(null, "name", "SyntaxError");
|
||||||
|
}
|
||||||
|
}
|
20
src/me/topchetoeu/jscript/polyfills/TypeErrorPolyfill.java
Normal file
20
src/me/topchetoeu/jscript/polyfills/TypeErrorPolyfill.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
|
import me.topchetoeu.jscript.interop.InitType;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeInit;
|
||||||
|
|
||||||
|
public class TypeErrorPolyfill extends ErrorPolyfill {
|
||||||
|
@NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) throws InterruptedException {
|
||||||
|
var target = ErrorPolyfill.constructor(ctx, thisArg, message);
|
||||||
|
target.defineProperty(ctx, "name", "TypeError");
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
@NativeInit(InitType.PROTOTYPE) public static void init(Environment env, ObjectValue target) {
|
||||||
|
target.defineProperty(null, env.symbol("Symbol.typeName"), "TypeError");
|
||||||
|
target.defineProperty(null, "name", "TypeError");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user