diff --git a/src/lib/libs/_entry.ts b/src/lib/libs/_entry.ts index 2357ee4..1ac7458 100644 --- a/src/lib/libs/_entry.ts +++ b/src/lib/libs/_entry.ts @@ -1,36 +1,52 @@ -import { Boolean } from "./boolean.ts"; +import { object, setGlobalPrototypes, target } from "./primordials.ts"; import { Error, RangeError, SyntaxError, TypeError } from "./errors.ts"; +import { Boolean } from "./boolean.ts"; import { Function } from "./function.ts"; import { Number } from "./number.ts"; import { Object } from "./object.ts"; -import { object, setGlobalPrototypes, target } from "./primordials.ts"; import { String } from "./string.ts"; import { Symbol } from "./symbol.ts"; import { Array } from "./array.ts"; -import { Map } from "./map.ts"; +import { Map, WeakMap } from "./map.ts"; import { RegExp } from "./regex.ts"; +import { Date } from "./date.ts"; +import { Math as _Math } from "./math.ts"; +import { Set, WeakSet } from "./set.ts"; declare global { function print(...args: any[]): void; + function measure(func: Function): void; } -object.defineField(target, "undefined", false, false, false, undefined); +function fixup(clazz: T) { + object.setPrototype(clazz, Function.prototype); + object.setPrototype(clazz.prototype, Object.prototype); + return clazz; +} -target.Symbol = Symbol; -target.Number = Number; -target.String = String; -target.Boolean = Boolean; +object.defineField(target, "undefined", { e: false, c: false, w: false, v: void 0 }); + +target.Symbol = fixup(Symbol); +target.Number = fixup(Number); +target.String = fixup(String); +target.Boolean = fixup(Boolean); target.Object = Object; -target.Function = Function; -target.Array = Array; +target.Function = fixup(Function); +target.Array = fixup(Array); -target.Error = Error; +target.Error = fixup(Error); target.RangeError = RangeError; target.SyntaxError = SyntaxError; target.TypeError = TypeError; -target.Map = Map; +target.Map = fixup(Map); +target.WeakMap = fixup(WeakMap); +target.Set = fixup(Set); +target.WeakSet = fixup(WeakSet); +target.RegExp = fixup(RegExp); +target.Date = fixup(Date); +target.Math = object.setPrototype(_Math, Object.prototype); target.parseInt = Number.parseInt; target.parseFloat = Number.parseFloat; @@ -38,8 +54,6 @@ target.NaN = Number.NaN; target.Infinity = Number.POSITIVE_INFINITY; -target.RegExp = RegExp; - setGlobalPrototypes({ string: String.prototype, number: Number.prototype, diff --git a/src/lib/libs/array.ts b/src/lib/libs/array.ts index dc64700..84bab28 100644 --- a/src/lib/libs/array.ts +++ b/src/lib/libs/array.ts @@ -1,5 +1,6 @@ -import { func, object } from "./primordials.ts"; -import { Symbol } from "./symbol.ts"; +import { func, object, string } from "./primordials.ts"; +import { String } from "./string.ts"; +import { limitI, symbols, wrapI } from "./utils.ts"; export const Array = (() => { class Array { @@ -8,15 +9,133 @@ export const Array = (() => { if (i in this) func.invoke(cb, self, [this[i], i, this]); } } + public join(this: any[], delim = ",") { + delim = String(delim); + const parts = []; + if (delim) { + for (let i = 0; i < this.length; i++) { + if (i) parts[parts.length] = delim; + parts[parts.length] = (i in this) ? String(this[i]) : ""; + } + } + else { + for (let i = 0; i < this.length; i++) { + parts[i] = (i in this) ? String(this[i]) : ""; + } + } - public [Symbol.iterator](this: any[]) { + return string.stringBuild(parts); + } + public push(this: any[]) { + const start = this.length; + for (let i = arguments.length - 1; i >= 0; i--) { + this[start + i] = arguments[i]; + } + return arguments.length; + } + public concat(this: any[]) { + const res: any[] = []; + + function add(arr: any) { + if (Array.isArray(arr) || symbols.isConcatSpreadable in arr) { + const start = res.length; + res.length += arr.length; + + for (let i = 0; i < res.length; i++) { + if (i in arr) res[start + i] = arr[i]; + } + } + else res[res.length] = arr; + } + + add(this); + + for (let i = 0; i < arguments.length; i++) { + add(arguments[i]); + } + + return res; + } + public slice(this: any[], start = 0, end = this.length) { + start = wrapI(start, this.length); + end = wrapI(end, this.length); + + if (end <= start) return []; + + const res: any[] = []; + res.length = end - start; + + for (let i = start; i < end; i++) { + res[i] = this[start + i]; + } + + return res; + } + public splice(this: any[], start = 0, count = this.length - start, ...vals: any[]) { + start = limitI(wrapI(start, this.length), this.length); + count = limitI(wrapI(count, this.length), this.length - start); + + const res: any[] = []; + const change = vals.length - count; + + for (let i = start; i < start + count; i++) { + res[i - start] = this[i]; + } + + if (change < 0) { + for (let i = start - change; i < this.length; i++) { + this[i + change] = this[i]; + } + this.length = this.length + change; + } + else { + for (let i = this.length - 1; i >= start - change; i--) { + this[i + change] = this[i]; + } + } + + for (let i = 0; i < vals.length; i++) { + this[i + start] = vals[i]; + } + + return res; + } + + public map(this: any[], cb: Function, self?: any) { + const res = []; + res.length = this.length; + + for (let i = 0; i < arguments.length; i++) { + if (i in this) res[i] = func.invoke(cb, self, [this[i], i, this]); + } + + return res; + } + public filter(this: any[], cb: Function, self?: any) { + const res = []; + + for (let i = 0; i < arguments.length; i++) { + if (i in this && func.invoke(cb, self, [this[i], i, this])) res[res.length] = this[i]; + } + + return res; + } + public some(this: any[], cb: Function, self?: any) { + for (let i = 0; i < arguments.length; i++) { + if (i in this && func.invoke(cb, self, [this[i], i, this])) return true; + } + + return false; + } + + public [symbols.iterator](this: any[]) { let i = 0; let arr: any[] | undefined = this; return { next() { if (arr == null) return { done: true, value: undefined }; - if (i > arr.length) { + if (i >= arr.length) { arr = undefined; return { done: true, value: undefined }; } @@ -26,7 +145,7 @@ export const Array = (() => { return { done: false, value: val }; } }, - [Symbol.iterator]() { return this; } + [symbols.iterator]() { return this; } }; } @@ -40,8 +159,45 @@ export const Array = (() => { else throw new Error("Spreading not implemented"); } - public static isArray(val: any[]) { - object.isArray(val); + public static isArray(val: any): val is any[] { + return object.isArray(val); + } + public static from(val: any, cb?: Function, self?: any): any[] { + if (symbols.iterator in val) { + const res = []; + const it = val[symbols.iterator](); + + if (cb) { + for (let val = it.next(); !val.done; val = it.next()) { + res[res.length] = func.invoke(cb, self, [val.value]); + } + } + else { + for (let val = it.next(); !val.done; val = it.next()) { + res[res.length] = val.value; + } + } + + return res; + } + else if ("length" in val) { + const res = []; + + if (cb) { + for (let i = 0; i < val.length; i++) { + if (i in val) res[i] = func.invoke(cb, self, [val[i]]); + } + } + else { + for (let i = 0; i < val.length; i++) { + if (i in val) res[i] = val[i]; + } + } + + return res; + } + else if (val == null) throw new Error("Illegal argument"); + else return []; } } diff --git a/src/lib/libs/date.ts b/src/lib/libs/date.ts new file mode 100644 index 0000000..ecaa1a2 --- /dev/null +++ b/src/lib/libs/date.ts @@ -0,0 +1,20 @@ +import { now, symbol } from "./primordials.ts"; + +const timeKey: unique symbol = symbol.makeSymbol("") as any; + +export const Date = (() => { + class Date { + [timeKey]!: number; + + public constructor() { + + } + + public static now() { + return now(); + } + }; + + return Date as any as typeof Date & ((val?: unknown) => string); +})(); +export type Date = InstanceType; diff --git a/src/lib/libs/errors.ts b/src/lib/libs/errors.ts index fd08a40..7d9a442 100644 --- a/src/lib/libs/errors.ts +++ b/src/lib/libs/errors.ts @@ -18,22 +18,22 @@ export class Error { this.message = String(msg); } } -object.defineField(Error.prototype, "name", true, false, true, "Error"); -object.defineField(Error.prototype, "message", true, false, true, ""); +object.defineField(Error.prototype, "name", { c: true, e: false, w: true, v: "Error" }); +object.defineField(Error.prototype, "message", { c: true, e: false, w: true, v: "" }); func.setCallable(Error, true); func.setConstructable(Error, true); export class SyntaxError extends Error { } -object.defineField(SyntaxError.prototype, "name", true, false, true, "SyntaxError"); +object.defineField(SyntaxError.prototype, "name", { c: true, e: false, w: true, v: "SyntaxError" }); func.setCallable(SyntaxError, true); func.setConstructable(SyntaxError, true); export class TypeError extends Error { } -object.defineField(TypeError.prototype, "name", true, false, true, "TypeError"); +object.defineField(TypeError.prototype, "name", { c: true, e: false, w: true, v: "TypeError" }); func.setCallable(TypeError, true); func.setConstructable(TypeError, true); export class RangeError extends Error { } -object.defineField(RangeError.prototype, "name", true, false, true, "RangeError"); +object.defineField(RangeError.prototype, "name", { c: true, e: false, w: true, v: "RangeError" }); func.setCallable(RangeError, true); func.setConstructable(RangeError, true); diff --git a/src/lib/libs/function.ts b/src/lib/libs/function.ts index 4e19113..6035a60 100644 --- a/src/lib/libs/function.ts +++ b/src/lib/libs/function.ts @@ -17,9 +17,30 @@ export const Function = (() => { public apply(this: (...args: any) => any, self: any, args: any[]) { return func.invoke(this, self, args); } - public call(this: (...args: any) => any, self: any, ...args: any[]) { + public call(this: (...args: any) => any, self: any) { + const args: any[] = []; + for (let i = arguments.length - 1; i >= 1; i--) args[i - 1] = arguments[i]; return func.invoke(this, self, args); } + public bind(this: (...args: any) => any, self: any) { + const cb = this; + if (arguments.length === 0) return function (this: any) { return func.invoke(cb, this, arguments as any) }; + if (arguments.length <= 1) return function () { return func.invoke(cb, self, arguments as any); } + + const base: any[] = []; + const offset = arguments.length - 1; + base.length = offset; + + for (let i = 0; i < offset; i++) base[i] = arguments[i + 1]; + + return function () { + for (let i = 0; i < arguments.length; i++) { + base[offset + i] = arguments[i]; + } + + return func.invoke(cb, self, base); + }; + } public constructor (...args: string[]) { const parts = ["(function anonymous("]; diff --git a/src/lib/libs/map.ts b/src/lib/libs/map.ts index 30cb355..3611c56 100644 --- a/src/lib/libs/map.ts +++ b/src/lib/libs/map.ts @@ -1,7 +1,8 @@ -import { func, map } from "./primordials.ts"; -import { Symbol } from "./symbol.ts"; +import { Array } from "./array.ts"; +import { func, map, object, symbol } from "./primordials.ts"; +import { symbols } from "./utils.ts"; -const mapKey: unique symbol = Symbol("Map.impl") as any; +const mapKey: unique symbol = symbol.makeSymbol("Map.impl") as any; export class Map { private [mapKey]: InstanceType; @@ -23,6 +24,9 @@ export class Map { return true; } } + public clear() { + this[mapKey].clear(); + } public keys(): K[] { return this[mapKey].keys(); @@ -42,20 +46,79 @@ export class Map { return res; } - public [Symbol.iterator](): Iterator<[K, V]> { - return func.invoke(Array.prototype[Symbol.iterator as any], this.entries(), []) as any; + public forEach(cb: Function, self?: any) { + const entries = this.entries(); + for (let i = 0; i < entries.length; i++) { + func.invoke(cb, self, [entries[i][1], entries[i][0], this]); + } + } + + public [symbols.iterator](): Iterator<[K, V]> { + return func.invoke(Array.prototype[symbols.iterator], this.entries(), []) as any; } public constructor(iterable?: Iterable<[K, V]>) { const _map = this[mapKey] = new map(); if (iterable != null) { - const it = (iterable as any)[Symbol.iterator](); - for (let val = it.next(); !val.done; val = it.next()) { - _map.set(val.value[0], val.value[1]); + if (Array.isArray(iterable)) { + for (let i = 0; i < iterable.length; i++) { + if (!(i in iterable)) continue; + _map.set(iterable[i][0], iterable[i][1]); + } + } + else { + const it = (iterable as any)[symbols.iterator](); + for (let val = it.next(); !val.done; val = it.next()) { + _map.set(val.value[0], val.value[1]); + } + } + } + } +} +export class WeakMap { + private [mapKey]: InstanceType; + + public get(key: K): V { + return this[mapKey].get(key); + } + public has(key: K): boolean { + return this[mapKey].has(key); + } + public set(key: K, val: V) { + this[mapKey].set(key, val); + return this; + } + public delete(key: K): boolean { + if (!this[mapKey].has(key)) return false; + else { + this[mapKey].delete(key); + return true; + } + } + public clear() { + this[mapKey].clear(); + } + + public constructor(iterable?: Iterable<[K, V]>) { + const _map = this[mapKey] = new map(true); + + if (iterable != null) { + if (Array.isArray(iterable)) { + for (let i = 0; i < iterable.length; i++) { + if (!(i in iterable)) continue; + _map.set(iterable[i][0], iterable[i][1]); + } + } + else { + const it = (iterable as any)[symbols.iterator](); + for (let val = it.next(); !val.done; val = it.next()) { + _map.set(val.value[0], val.value[1]); + } } } } } func.setCallable(Map, false); +func.setCallable(WeakMap, false); diff --git a/src/lib/libs/math.ts b/src/lib/libs/math.ts new file mode 100644 index 0000000..8de38fe --- /dev/null +++ b/src/lib/libs/math.ts @@ -0,0 +1,27 @@ +import { number, object } from "./primordials"; + +export const Math = {}; + +function method(name: string, func: Function) { + object.defineField(Math, name, { c: true, e: false, w: true, v: func }); +} + +method("max", function max() { + let res = -number.Infinity; + + for (let i = 0; i < arguments.length; i++) { + if (res < arguments[i]) res = arguments[i]; + } + + return res; +}); + +method("floor", function floor(val: number) { + val = val - 0; + if (number.isNaN(val)) return number.NaN; + + let rem = val % 1; + if (rem < 0) rem += 1; + + return val - rem; +}); diff --git a/src/lib/libs/number.ts b/src/lib/libs/number.ts index e0a03ef..00fe148 100644 --- a/src/lib/libs/number.ts +++ b/src/lib/libs/number.ts @@ -61,14 +61,14 @@ export const Number = (() => { public static readonly MIN_VALUE: number; } - object.defineField(Number, "EPSILON", false, false, false, 2.220446049250313e-16); - object.defineField(Number, "MIN_SAFE_INTEGER", false, false, false, -9007199254740991); - object.defineField(Number, "MAX_SAFE_INTEGER", false, false, false, 9007199254740991); - object.defineField(Number, "POSITIVE_INFINITY", false, false, false, +number.Infinity); - object.defineField(Number, "NEGATIVE_INFINITY", false, false, false, -number.Infinity); - object.defineField(Number, "NaN", false, false, false, number.NaN); - object.defineField(Number, "MAX_VALUE", false, false, false, 1.7976931348623157e+308); - object.defineField(Number, "MIN_VALUE", false, false, false, 5e-324); + object.defineField(Number, "EPSILON", { c: false, e: false, w: false, v: 2.220446049250313e-16 }); + object.defineField(Number, "MIN_SAFE_INTEGER", { c: false, e: false, w: false, v: -9007199254740991 }); + object.defineField(Number, "MAX_SAFE_INTEGER", { c: false, e: false, w: false, v: 9007199254740991 }); + object.defineField(Number, "POSITIVE_INFINITY", { c: false, e: false, w: false, v: +number.Infinity }); + object.defineField(Number, "NEGATIVE_INFINITY", { c: false, e: false, w: false, v: -number.Infinity }); + object.defineField(Number, "NaN", { c: false, e: false, w: false, v: number.NaN }); + object.defineField(Number, "MAX_VALUE", { c: false, e: false, w: false, v: 1.7976931348623157e+308 }); + object.defineField(Number, "MIN_VALUE", { c: false, e: false, w: false, v: 5e-324 }); func.setCallable(Number, true); func.setConstructable(Number, true); diff --git a/src/lib/libs/object.ts b/src/lib/libs/object.ts index 127b247..eb17d94 100644 --- a/src/lib/libs/object.ts +++ b/src/lib/libs/object.ts @@ -3,8 +3,8 @@ import { TypeError } from "./errors.ts"; import { Number } from "./number.ts"; import { func, object } from "./primordials.ts"; import { String } from "./string.ts"; +import { symbols, valueKey } from "./utils.ts"; import { Symbol } from "./symbol.ts"; -import { valueKey } from "./utils.ts"; export const Object = (() => { class Object { @@ -12,7 +12,7 @@ export const Object = (() => { if (this === undefined) return "[object Undefined]"; else if (this === null) return "[object Null]"; else if (typeof this === "object") { - if (Symbol.toStringTag in this) return "[object " + this[Symbol.toStringTag] + "]"; + if (symbols.toStringTag in this) return "[object " + this[symbols.toStringTag] + "]"; else return "[object Object]"; } else if (typeof this === "number" || this instanceof Object) return "[object Object]"; @@ -24,6 +24,9 @@ export const Object = (() => { public valueOf() { return this; } + public hasOwnProperty(key: string) { + return object.getOwnMember(this, key) != null; + } public constructor (value?: unknown) { if (typeof value === 'object' && value !== null) return value as any; @@ -31,7 +34,7 @@ export const Object = (() => { if (typeof value === 'number') return new Number(value) as any; if (typeof value === 'boolean') return new Boolean(value) as any; if (typeof value === 'symbol') { - var res: Symbol = {} as any; + const res: Symbol = {} as any; object.setPrototype(res, Symbol.prototype); res[valueKey] = value; return res as any; @@ -40,30 +43,49 @@ export const Object = (() => { return {} as any; } - public static defineProperty(obj: object, key: string | symbol, desc: PropertyDescriptor) { - if (obj === null || typeof obj !== "function" && typeof obj !== "object") throw new TypeError("Object.defineProperty called on non-object"); - if (desc === null || typeof desc !== "function" && typeof desc !== "object") throw new TypeError("Property description must be an object: " + desc); - if ("get" in desc || "set" in desc) { - var get = desc.get, set = desc.set; + public static getOwnPropertyDescriptor(obj: object, key: any) { + return object.getOwnMember(obj, key); + } - if (get !== undefined && typeof get !== "function") throw new TypeError("Getter must be a function: " + get); - if (set !== undefined && typeof set !== "function") throw new TypeError("Setter must be a function: " + set); - if ("value" in desc || "writable" in desc) { - throw new TypeError("Invalid property descriptor. Cannot both specify accessors and a value or writable attribute"); - } - if (!object.defineProperty(obj, key, !!desc.enumerable, !!desc.configurable, get, set)) { - throw new TypeError("Cannot redefine property: " + String(key)); - } + public static defineProperty(obj: object, key: string | symbol, desc: PropertyDescriptor) { + if (obj === null || typeof obj !== "function" && typeof obj !== "object") { + throw new TypeError("Object.defineProperty called on non-object"); } - else if (!object.defineField(obj, key, !!desc.writable, !!desc.enumerable, !!desc.configurable, desc.value)) { - throw new TypeError("Cannot redefine property: " + String(key)); + if (desc === null || typeof desc !== "function" && typeof desc !== "object") { + throw new TypeError("Property description must be an object: " + desc); + } + const res: any = {}; + + if ("get" in desc || "set" in desc) { + if ("get" in desc) { + const get = desc.get; + if (get !== undefined && typeof get !== "function") throw new TypeError("Getter must be a function: " + get); + res.g = get; + } + if ("set" in desc) { + const set = desc.set; + if (set !== undefined && typeof set !== "function") throw new TypeError("Setter must be a function: " + set); + res.s = set; + } + if ("enumerable" in desc) res.e = !!desc.enumerable; + if ("configurable" in desc) res.e = !!desc.configurable; + + if (!object.defineProperty(obj, key, res)) throw new TypeError("Cannot redefine property: " + String(key)); + } + else { + if ("enumerable" in desc) res.e = !!desc.enumerable; + if ("configurable" in desc) res.e = !!desc.configurable; + if ("writable" in desc) res.w = !!desc.writable; + if ("value" in desc) res.v = desc.value; + + if (!object.defineField(obj, key, res)) throw new TypeError("Cannot redefine property: " + String(key)); } return obj; } public static defineProperties(obj: object, desc: PropertyDescriptorMap) { - const keys = object.getOwnMembers(obj, false); - const symbols = object.getOwnSymbolMembers(obj, false); + const keys = object.getOwnMembers(desc, false); + const symbols = object.getOwnSymbolMembers(desc, false); for (let i = 0; i < keys.length; i++) { Object.defineProperty(obj, keys[i], desc[keys[i]]); @@ -80,8 +102,87 @@ export const Object = (() => { return res; } + public static assign(target: any) { + for (let i = 1; i < arguments.length; i++) { + const obj = arguments[i]; + const keys = object.getOwnMembers(obj, false); + const symbols = object.getOwnSymbolMembers(obj, false); + + for (let j = 0; j < keys.length; j++) { + target[keys[j]] = obj[keys[j]]; + } + for (let j = 0; j < symbols.length; j++) { + target[symbols[j]] = obj[symbols[j]]; + } + } + + return target; + } + + + public static setPrototypeOf(obj: object, proto: object | null) { + object.setPrototype(obj, proto!); + } + public static getPrototypeOf(obj: object) { + return object.getPrototype(obj) || null; + } + + public static keys(obj: any) { + const res: any[] = []; + const keys = object.getOwnMembers(obj, false); + const symbols = object.getOwnSymbolMembers(obj, false); + + for (let i = 0; i < keys.length; i++) { + res[res.length] = keys[i]; + } + for (let i = 0; i < symbols.length; i++) { + res[res.length] = symbols[i]; + } + + return res; + } + public static values(obj: any) { + const res: any[] = []; + const keys = object.getOwnMembers(obj, false); + const symbols = object.getOwnSymbolMembers(obj, false); + + for (let i = 0; i < keys.length; i++) { + res[res.length] = obj[keys[i]]; + } + for (let i = 0; i < symbols.length; i++) { + res[res.length] = obj[symbols[i]]; + } + + return res; + } + public static entries(obj: any) { + const res: [any, any][] = []; + const keys = object.getOwnMembers(obj, false); + const symbols = object.getOwnSymbolMembers(obj, false); + + for (let i = 0; i < keys.length; i++) { + res[res.length] = [keys[i], obj[keys[i]]]; + } + for (let i = 0; i < symbols.length; i++) { + res[res.length] = [symbols[i], obj[symbols[i]]]; + } + + return res; + } + + public static preventExtensions(obj: object) { + object.preventExt(obj); + } + public static seal(obj: object) { + object.seal(obj); + } + public static freeze(obj: object) { + object.freeze(obj); + } } + object.setPrototype(Object.prototype, undefined); + func.setCallable(Object, true); func.setConstructable(Object, true); diff --git a/src/lib/libs/polyfills/createClass.js b/src/lib/libs/polyfills/createClass.js index 9f9e7a5..76fa6e0 100644 --- a/src/lib/libs/polyfills/createClass.js +++ b/src/lib/libs/polyfills/createClass.js @@ -2,24 +2,25 @@ import { object } from "../primordials.ts"; function _defineProperties(target, arr) { if (!arr) return; - for (var t = 0; t < arr.length; t++) { - var desc = arr[t]; - + for (var i = 0; i < arr.length; i++) { + var desc = arr[i]; + var res; + if ("value" in desc) { - object.defineField(target, desc.key, desc.writable || true, desc.enumerable || false, desc.configurable || true, desc.value); + res = object.defineField(target, desc.key, { w: desc.writable || true, e: desc.enumerable || true, c: desc.configurable || true, v: desc.value }); } else { - object.defineProperty(target, desc.key, desc.enumerable || false, desc.configurable || true, desc.get, desc.set); + res = object.defineProperty(target, desc.key, { e: desc.enumerable || true, c: desc.configurable || true, g: desc.get, s: desc.set }); } + + if (!res) throw "Couldn't set property"; } } /* __#PURE__ */ export default function _createClass(clazz, instance, nonInstance) { - if (instance) { - _defineProperties(clazz.prototype, instance); - _defineProperties(clazz, nonInstance); - } + _defineProperties(clazz.prototype, instance); + _defineProperties(clazz, nonInstance); return clazz; } \ No newline at end of file diff --git a/src/lib/libs/polyfills/defineProperty.js b/src/lib/libs/polyfills/defineProperty.js index 14cb53b..c04e49a 100644 --- a/src/lib/libs/polyfills/defineProperty.js +++ b/src/lib/libs/polyfills/defineProperty.js @@ -2,6 +2,6 @@ import { object } from "../primordials.ts"; export default function _defineProperty(obj, key, val) { if (obj == null) return; - object.defineField(obj, key, true, true, true, val); + object.defineField(obj, key, { c: true, e: true, w: true, v: val }); return obj; } diff --git a/src/lib/libs/polyfills/inherits.js b/src/lib/libs/polyfills/inherits.js index babaf4f..ad73632 100644 --- a/src/lib/libs/polyfills/inherits.js +++ b/src/lib/libs/polyfills/inherits.js @@ -8,4 +8,4 @@ export default function _inherits(t, e) { object.setPrototype(t.prototype, e.prototype); object.setPrototype(t, e); } -} \ No newline at end of file +} diff --git a/src/lib/libs/polyfills/readOnlyError.js b/src/lib/libs/polyfills/readOnlyError.js new file mode 100644 index 0000000..23a186e --- /dev/null +++ b/src/lib/libs/polyfills/readOnlyError.js @@ -0,0 +1,3 @@ +export default function _readOnlyError(name) { + throw name; +} \ No newline at end of file diff --git a/src/lib/libs/primordials.ts b/src/lib/libs/primordials.ts index 4b784ed..6899ce3 100644 --- a/src/lib/libs/primordials.ts +++ b/src/lib/libs/primordials.ts @@ -15,17 +15,28 @@ export interface StringPrimordials { stringBuild(parts: string[]): string; fromCharCode(char: number): string; fromCodePoint(char: number): string; + toCharCode(char: string): number; + toCodePoint(char: string, i: number): number; + indexOf(str: string, search: string, start: number, reverse?: boolean): number; + substring(str: string, start: number, end: number): string; + lower(str: string): string; + upper(str: string): string; } export interface ObjectPrimordials { - defineProperty(obj: object, key: string | number | symbol, enumerable: boolean, configurable: boolean, get?: Function, set?: Function): boolean; - defineField(obj: object, key: string | number | symbol, writable: boolean, enumerable: boolean, configurable: boolean, value: any): boolean; - getOwnMember(): any; - getOwnSymbolMember(): any; + defineProperty(obj: object, key: string | number | symbol, conf: { g?: Function, s?: Function, e?: boolean, c?: boolean }): boolean; + defineField(obj: object, key: string | number | symbol, conf: { v?: any, e?: boolean, c?: boolean, w?: boolean }): boolean; + getOwnMember(obj: object, key: any): PropertyDescriptor | undefined; getOwnMembers(obj: object, onlyEnumerable: boolean): string[]; getOwnSymbolMembers(obj: object, onlyEnumerable: boolean): symbol[]; getPrototype(obj: object): object | undefined; setPrototype(obj: object, proto?: object): object; isArray(obj: any[]): boolean; + + preventExt(obj: object): void; + seal(obj: object): void; + freeze(obj: object): void; + + memcpy(src: any[], dst: any[], srcI: number, dstI: number, n: number): void; } export interface FunctionPrimordials { invokeType(args: IArguments, self: any): "new" | "call"; @@ -33,8 +44,8 @@ export interface FunctionPrimordials { target(): Function | null | undefined; setConstructable(func: Function, flag: boolean): void; setCallable(func: Function, flag: boolean): void; - invoke(func: Function, self: any, args: any[]): void; - construct(func: Function, self: any, args: any[]): void; + invoke(func: Function, self: any, args: any[]): any; + construct(func: Function, self: any, args: any[]): any; } export interface JSONPrimordials { parse(data: string): any; @@ -48,20 +59,22 @@ export interface Primordials { object: ObjectPrimordials; function: FunctionPrimordials; json: JSONPrimordials; - map: new () => { + map: new (weak?: boolean) => { get(key: any): any; has(key: any): boolean; set(key: any, val: any): void; delete(key: any): void; keys(): any[]; + clear(): void; }; - regex: new (source: string) => { - exec(target: string, offset: number, indices: false): { - - }; + + regex: new (source: string, multiline: boolean, noCase: boolean, dotall: boolean, unicode: boolean, unicodeClass: boolean) => { + exec(target: string, offset: number, indices: boolean): { matches: RegExpMatchArray, end: number } | null; + groupCount(): number; }; compile(src: string): Function; setGlobalPrototypes(prototype: Record): void; + now(): number; } globalThis.undefined = void 0; @@ -79,4 +92,5 @@ export const { regex, setGlobalPrototypes, compile, + now, } = primordials; diff --git a/src/lib/libs/regex.ts b/src/lib/libs/regex.ts index 4fed215..95c6a4a 100644 --- a/src/lib/libs/regex.ts +++ b/src/lib/libs/regex.ts @@ -1,8 +1,11 @@ -import { func, regex } from "./primordials.ts"; +import { func, regex, symbol } from "./primordials.ts"; import { String } from "./string.ts"; -import { Symbol } from "./symbol.ts"; +import { ReplaceRange } from "./utils.ts"; +import { applyReplaces } from "./utils.ts"; +import { applySplits } from "./utils.ts"; +import { symbols } from "./utils.ts"; -const regexKey: unique symbol = Symbol("RegExp.impl") as any; +const regexKey: unique symbol = symbol.makeSymbol("RegExp.impl") as any; export class RegExp { private [regexKey]: InstanceType; @@ -11,11 +14,18 @@ export class RegExp { public readonly flags: string; public lastIndex = 0; + public readonly indices: boolean; + public readonly global: boolean; + public readonly ignoreCase: boolean; + public readonly multiline: boolean; + public readonly dotall: boolean; + public readonly unicode: boolean; + public readonly unicodeSets: boolean; + public readonly sticky: boolean; + public constructor(source: any, flags: string) { source = this.source = String(typeof source === "object" && "source" in source ? source.source : source); flags = String(flags); - - const _regex = this[regexKey] = new regex(source); let indices = false; let global = false; @@ -49,6 +59,79 @@ export class RegExp { if (unicodeSets) flags += "v"; if (sticky) flags += "y"; this.flags = flags; + this.indices = indices; + this.global = global; + this.ignoreCase = ignoreCase; + this.multiline = multiline; + this.dotall = dotall; + this.unicode = unicode; + this.unicodeSets = unicodeSets; + this.sticky = sticky; + + this[regexKey] = new regex(source, multiline, ignoreCase, dotall, unicode, unicodeSets); + } + + public exec(target: string) { + const useLast = this.global || this.sticky; + const start = useLast ? this.lastIndex : 0; + + const match = this[regexKey].exec(target, start, this.indices); + if (match != null && !(this.sticky && match.matches.index !== start)) { + if (useLast) this.lastIndex = match.end; + return match.matches; + } + + if (useLast) this.lastIndex = 0; + return null; + } + public test(target: string) { + return this.exec(target) != null; + } + + public [symbols.split](target: string, limit?: number) { + return applySplits(target, limit, offset => { + const val = this[regexKey].exec(target, offset, false); + if (val == null) return undefined; + + return { start: val.matches.index!, end: val.end }; + }); + } + public [symbols.replace](target: string, replacer: any) { + const matches: ReplaceRange[] = []; + const regex = this[regexKey]; + + if (this.global) { + let offset = 0; + + while (true) { + const match = regex.exec(target, offset, false); + if (match == null) break; + + const start = match.matches.index; + const end = match.end; + const arr: string[] = []; + for (let i = 0; i < match.matches.length; i++) { + arr[i] = match.matches[i]; + } + + matches[matches.length] = { start: match.matches.index!, end: match.end, matches: arr }; + + if (start === end) offset = start + 1; + else offset = end; + } + + return applyReplaces(target, matches, replacer, regex.groupCount() + 1); + } + else { + const match = this.exec(target); + if (match != null) matches[0] = { + start: match.index!, + end: match.index! + match[0].length, + matches: match, + } + } + + return applyReplaces(target, matches, replacer, regex.groupCount() + 1); } } func.setCallable(RegExp, false); diff --git a/src/lib/libs/set.ts b/src/lib/libs/set.ts new file mode 100644 index 0000000..7e10d1c --- /dev/null +++ b/src/lib/libs/set.ts @@ -0,0 +1,117 @@ +import { Array } from "./array.ts"; +import { func, map, symbol } from "./primordials.ts"; +import { symbols } from "./utils.ts"; + +const mapKey: unique symbol = symbol.makeSymbol("Set.impl") as any; + +export class Set { + private [mapKey]: InstanceType; + + public has(key: T): boolean { + return this[mapKey].has(key); + } + public add(val: T) { + this[mapKey].set(val, true); + return this; + } + public delete(val: T): boolean { + if (!this[mapKey].has(val)) return false; + else { + this[mapKey].delete(val); + return true; + } + } + public clear() { + this[mapKey].clear(); + } + + public keys(): T[] { + return this[mapKey].keys(); + } + public values(): T[] { + return this[mapKey].keys(); + } + public entries(): [T, T][] { + const res = this[mapKey].keys(); + + for (let i = 0; i < res.length; i++) { + res[i] = [res[i], res[i]]; + } + return res; + } + + public forEach(cb: Function, self?: any) { + const vals = this.values(); + + for (let i = 0; i < vals.length; i++) { + func.invoke(cb, self, [vals[i], vals[i], this]); + } + } + + public [symbols.iterator](): Iterator { + return func.invoke(Array.prototype[symbols.iterator], this.values(), []) as any; + } + + public constructor(iterable?: Iterable) { + const _map = this[mapKey] = new map(); + + if (iterable != null) { + if (Array.isArray(iterable)) { + for (let i = 0; i < iterable.length; i++) { + if (!(i in iterable)) continue; + _map.set(iterable[i], true); + } + } + else { + const it = (iterable as any)[symbols.iterator](); + for (let val = it.next(); !val.done; val = it.next()) { + _map.set(val.value, true); + } + } + } + } +} + +export class WeakSet { + private [mapKey]: InstanceType; + + public has(key: T): boolean { + return this[mapKey].has(key); + } + public add(val: T) { + this[mapKey].set(val, true); + return this; + } + public delete(val: T): boolean { + if (!this[mapKey].has(val)) return false; + else { + this[mapKey].delete(val); + return true; + } + } + public clear() { + this[mapKey].clear(); + } + + public constructor(iterable?: Iterable) { + const _map = this[mapKey] = new map(true); + + if (iterable != null) { + if (Array.isArray(iterable)) { + for (let i = 0; i < iterable.length; i++) { + if (!(i in iterable)) continue; + _map.set(iterable[i], true); + } + } + else { + const it = (iterable as any)[symbols.iterator](); + for (let val = it.next(); !val.done; val = it.next()) { + _map.set(val.value, true); + } + } + } + } +} + +func.setCallable(Set, false); +func.setCallable(WeakSet, false); diff --git a/src/lib/libs/string.ts b/src/lib/libs/string.ts index 5aae40a..8a1c713 100644 --- a/src/lib/libs/string.ts +++ b/src/lib/libs/string.ts @@ -1,6 +1,6 @@ -import { func, string } from "./primordials.ts"; -import { Symbol } from "./symbol.ts"; -import { unwrapThis, valueKey } from "./utils.ts"; +import { func, number, string } from "./primordials.ts"; +import { RegExp } from "./regex.ts"; +import { applyReplaces, applySplits, limitI, ReplaceRange, symbols, unwrapThis, valueKey, wrapI } from "./utils.ts"; export const String = (() => { class String { @@ -17,15 +17,119 @@ export const String = (() => { return unwrapThis(this, "string", String, "String.prototype.valueOf"); } - // public split(val: string) { - // const res: string[] = []; + public includes(search: string, offset = 0) { + const self = unwrapThis(this, "string", String, "String.prototype.indexOf"); + return string.indexOf(self, (String as any)(search), +offset, false) >= 0; + } - // while (true) { - // val.indexOf(); - // } - // } + public indexOf(search: string, offset = 0) { + const self = unwrapThis(this, "string", String, "String.prototype.indexOf"); + offset = +offset; + return string.indexOf(self, search, offset, false); + } + public lastIndexOf(search: string, offset = 0) { + const self = unwrapThis(this, "string", String, "String.prototype.lastIndexOf"); + offset = +offset; + return string.indexOf(self, search, offset, true); + } - public [Symbol.iterator]() { + public charAt(i: number) { + const self = unwrapThis(this, "string", String, "String.prototype.charAt"); + return self[i]; + } + public charCodeAt(i: number) { + const self = unwrapThis(this, "string", String, "String.prototype.charCodeAt"); + return self[i] ? string.toCharCode(self[i]) : number.NaN; + } + public codePointAt(i: number) { + const self = unwrapThis(this, "string", String, "String.prototype.charCodeAt"); + return i > 0 && i <= self.length ? string.toCodePoint(self, i) : number.NaN; + } + + public split(val?: any, limit?: number) { + const self = unwrapThis(this, "string", String, "String.prototype.split"); + if (val === undefined) return [self]; + if (val !== null && typeof val === "object" && symbols.split in val) { + return val[symbols.split](self, limit); + } + + val = (String as any)(val); + + return applySplits(self, limit, offset => { + const start = string.indexOf(self, val, offset, false); + if (start < 0) return undefined; + else return { start, end: start + val.length }; + }); + } + public replace(val: any, replacer: any) { + const self = unwrapThis(this, "string", String, "String.prototype.replace"); + if (val !== null && typeof val === "object" && symbols.replace in val) { + return val[symbols.replace](self, replacer); + } + else val = (String as any)(val); + + const i = string.indexOf(self, val, 0); + return applyReplaces(self, [{ start: i, end: i + val.length, matches: [val] }], replacer, false); + } + public replaceAll(val: any, replacer: any) { + const self = unwrapThis(this, "string", String, "String.prototype.replaceAll"); + if (val !== null && typeof val === "object" && symbols.replace in val) { + if (val instanceof RegExp && !val.global) throw new TypeError("replaceAll must be called with a global RegExp"); + return val[symbols.replace](self, replacer); + } + else val = (String as any)(val); + + let offset = 0; + const matches: ReplaceRange[] = []; + const add = val.length === 0 ? 1 : val.length; + + while (true) { + const i = string.indexOf(self, val, offset); + if (i < 0) break; + + matches[matches.length] = { start: i, end: i + val.length, matches: [val] }; + if (val.length === 0) + offset = i + add; + } + + return applyReplaces(self, matches, replacer, false); + } + + public slice(this: string, start = 0, end = this.length) { + const self = unwrapThis(this, "string", String, "String.prototype.slice"); + start = limitI(wrapI(start, this.length), this.length); + end = limitI(wrapI(end, this.length), this.length); + + if (end <= start) return ""; + return string.substring(self, start, end); + } + public substring(this: string, start = 0, end = this.length) { + const self = unwrapThis(this, "string", String, "String.prototype.substring"); + start = limitI(start, this.length); + end = limitI(end, this.length); + + if (end <= start) return ""; + return string.substring(self, start, end); + } + public substr(this: string, start = 0, count = this.length - start) { + const self = unwrapThis(this, "string", String, "String.prototype.substr"); + start = limitI(start, this.length); + count = limitI(count, this.length - start); + + if (count <= 0) return ""; + return string.substring(self, start, count + start); + } + + public toLowerCase(this: string) { + const self = unwrapThis(this, "string", String, "String.prototype.toLowerCase"); + return string.lower(self); + } + public toUpperCase(this: string) { + const self = unwrapThis(this, "string", String, "String.prototype.toLowerCase"); + return string.upper(self); + } + + public [symbols.iterator]() { var i = 0; var arr: string | undefined = unwrapThis(this, "string", String, "String.prototype[Symbol.iterator]"); @@ -42,7 +146,7 @@ export const String = (() => { return { done: false, value: val }; } }, - [Symbol.iterator]() { return this; } + [symbols.iterator]() { return this; } }; } diff --git a/src/lib/libs/symbol.ts b/src/lib/libs/symbol.ts index bb84c8d..4af4d38 100644 --- a/src/lib/libs/symbol.ts +++ b/src/lib/libs/symbol.ts @@ -1,5 +1,5 @@ import { func, object, symbol } from "./primordials.ts"; -import { unwrapThis, valueKey } from "./utils.ts"; +import { symbols, unwrapThis, valueKey } from "./utils.ts"; export const Symbol = (() => { class Symbol { @@ -24,29 +24,30 @@ export const Symbol = (() => { return symbol.getSymbol(name + ""); } - public static readonly asyncIterator: unique symbol; - public static readonly iterator: unique symbol; - public static readonly match: unique symbol; - public static readonly matchAll: unique symbol; - public static readonly replace: unique symbol; - public static readonly search: unique symbol; - public static readonly split: unique symbol; - public static readonly toStringTag: unique symbol; + declare public static readonly asyncIterator: unique symbol; + declare public static readonly iterator: unique symbol; + declare public static readonly match: unique symbol; + declare public static readonly matchAll: unique symbol; + declare public static readonly replace: unique symbol; + declare public static readonly search: unique symbol; + declare public static readonly split: unique symbol; + declare public static readonly toStringTag: unique symbol; }; func.setCallable(Symbol, true); func.setConstructable(Symbol, false); - object.defineField(Symbol, "asyncIterator", false, false, false, symbol.getSymbol("Symbol.asyncIterator")); - object.defineField(Symbol, "iterator", false, false, false, symbol.getSymbol("Symbol.iterator")); - object.defineField(Symbol, "match", false, false, false, symbol.getSymbol("Symbol.match")); - object.defineField(Symbol, "matchAll", false, false, false, symbol.getSymbol("Symbol.matchAll")); - object.defineField(Symbol, "replace", false, false, false, symbol.getSymbol("Symbol.replace")); - object.defineField(Symbol, "search", false, false, false, symbol.getSymbol("Symbol.search")); - object.defineField(Symbol, "split", false, false, false, symbol.getSymbol("Symbol.split")); - object.defineField(Symbol, "toStringTag", false, false, false, symbol.getSymbol("Symbol.toStringTag")); + object.defineField(Symbol, "asyncIterator", { c: false, e: false, w: false, v: symbols.asyncIterator }); + object.defineField(Symbol, "iterator", { c: false, e: false, w: false, v: symbols.iterator }); + object.defineField(Symbol, "match", { c: false, e: false, w: false, v: symbols.match }); + object.defineField(Symbol, "matchAll", { c: false, e: false, w: false, v: symbols.matchAll }); + object.defineField(Symbol, "replace", { c: false, e: false, w: false, v: symbols.replace }); + object.defineField(Symbol, "search", { c: false, e: false, w: false, v: symbols.search }); + object.defineField(Symbol, "split", { c: false, e: false, w: false, v: symbols.split }); + object.defineField(Symbol, "toStringTag", { c: false, e: false, w: false, v: symbols.toStringTag }); + object.defineField(Symbol, "isConcatSpreadable", { c: false, e: false, w: false, v: symbols.isConcatSpreadable }); - return Symbol as any as typeof Symbol & ((name?: string) => symbol); + return Symbol as any as typeof Symbol & ((name?: string) => ReturnType); })(); export type Symbol = InstanceType; diff --git a/src/lib/libs/test.js b/src/lib/libs/test.js new file mode 100644 index 0000000..4ef63df --- /dev/null +++ b/src/lib/libs/test.js @@ -0,0 +1,23 @@ +function split(val, limit) { + var self = unwrapThis(this, "string", String, "String.prototype.split"); + if (val === undefined) return [self]; + if (val !== null && typeof val === "object" && Symbol.split in val) { + return val[Symbol.split](self, limit); + } + val = String(val); + var offset = 0; + var res = []; + while (true) { + var start = string.indexOf(self, val, offset, false); + if (start < 0) { + res[res.length] = string.substring(self, offset, self.length); + break; + } + var end = start + val.length; + res[res.length] = string.substring(self, offset, start); + offset = end; + } + return res; +} + +split(print(), print()); \ No newline at end of file diff --git a/src/lib/libs/utils.ts b/src/lib/libs/utils.ts index d2737f9..e769b75 100644 --- a/src/lib/libs/utils.ts +++ b/src/lib/libs/utils.ts @@ -1,6 +1,17 @@ -import { symbol } from "./primordials.ts"; +import { func, string, symbol } from "./primordials.ts"; export const valueKey: unique symbol = symbol.makeSymbol("Primitive.value") as any; +export namespace symbols { + export const asyncIterator: unique symbol = symbol.makeSymbol("Symbol.asyncIterator") as any; + export const iterator: unique symbol = symbol.makeSymbol("Symbol.iterator") as any; + export const match: unique symbol = symbol.makeSymbol("Symbol.match") as any; + export const matchAll: unique symbol = symbol.makeSymbol("Symbol.matchAll") as any; + export const replace: unique symbol = symbol.makeSymbol("Symbol.replace") as any; + export const search: unique symbol = symbol.makeSymbol("Symbol.search") as any; + export const split: unique symbol = symbol.makeSymbol("Symbol.split") as any; + export const toStringTag: unique symbol = symbol.makeSymbol("Symbol.toStringTag") as any; + export const isConcatSpreadable: unique symbol = symbol.makeSymbol("Symbol.isConcatSpreadable") as any; +} export interface TypeMap { undefined: undefined; @@ -18,4 +29,190 @@ export function unwrapThis(self: any, type: T, constr: if (typeof self === type) return self; if (defaultVal !== undefined) return defaultVal; throw new TypeError(name + " requires that '" + arg + "' be a " + constr.name); -} \ No newline at end of file +} +export function wrapI(i: number, length: number) { + if (i < 0) return (i + length) | 0; + else return i | 0; +} +export function limitI(i: number, max: number) { + i |= 0; + if (i < 0) return 0; + else if (i > max) return max; + else return i; +} + +export type ReplaceRange = { start: number; end: number; matches: string[]; groups?: Record; }; +type ReplaceLiteral = (string | ((_: { groups: string[]; prev: () => string; next: () => string; }) => string))[]; + +function parseReplacer(replacer: string, groupN: number) { + const parts: ReplaceLiteral = []; + let lastI = 0; + let lastSlice = 0; + + while (true) { + const i = string.indexOf(replacer, "$", lastI); + if (i < 0 || i + 1 >= replacer.length) break; + lastI = i + 1; + + switch (replacer[i + 1]) { + case "$": + parts[parts.length] = string.substring(replacer, lastSlice, i); + parts[parts.length] = "$"; + lastSlice = i + 2; + continue; + case "&": + parts[parts.length] = string.substring(replacer, lastSlice, i); + parts[parts.length] = ({ groups }) => groups[0]; + lastSlice = i + 2; + continue; + case "`": + parts[parts.length] = string.substring(replacer, lastSlice, i); + parts[parts.length] = ({ prev }) => prev(); + lastSlice = i + 2; + continue; + case "'": + parts[parts.length] = string.substring(replacer, lastSlice, i); + parts[parts.length] = ({ next }) => next(); + lastSlice = i + 2; + continue; + } + + let groupI = 0; + let hasGroup = false; + let consumedN = 1; + + while (i + consumedN < replacer.length) { + const code = string.toCharCode(replacer[i + consumedN]); + if (code >= 48 && code <= 57) { + const newGroupI = groupI * 10 + code - 48; + if (newGroupI < 1 || newGroupI >= groupN) break; + + groupI = newGroupI; + hasGroup = true; + } + consumedN++; + } + + if (hasGroup) { + parts[parts.length] = string.substring(replacer, lastSlice, i); + parts[parts.length] = ({ groups }) => groups[groupI]; + lastSlice = i + consumedN; + continue; + } + + } + + if (lastSlice === 0) return [replacer]; + else parts[parts.length] = string.substring(replacer, lastSlice, replacer.length); + + return parts; +} +function executeReplacer(text: string, match: ReplaceRange, literal: ReplaceLiteral, prevEnd?: number, nextStart?: number) { + const res = []; + + for (let i = 0; i < literal.length; i++) { + const curr = literal[i]; + if (typeof curr === "function") res[i] = curr({ + groups: match.matches, + next: () => string.substring(text, prevEnd ?? 0, match.start), + prev: () => string.substring(text, match.end, nextStart ?? 0), + }); + else res[i] = curr; + } + + return string.stringBuild(res); +} +export function applyReplaces(text: string, ranges: ReplaceRange[], replace: any, groupN?: number | false) { + if (ranges.length === 0) return text; + + const res: string[] = []; + let offset = 0; + + if (groupN !== false && typeof replace === "string") { + if (groupN == null) { + for (let i = 0; i < ranges.length; i++) { + const prevEnd = i - 1 >= 0 ? ranges[i - 1].end : undefined; + const nextStart = i + 1 < ranges.length ? ranges[i + 1].start : undefined; + const range = ranges[i]; + res[res.length] = string.substring(text, offset, range.start); + res[res.length] = executeReplacer(text, range, parseReplacer(replace, range.matches.length), prevEnd, nextStart); + offset = range.end; + } + + res[res.length] = string.substring(text, offset, text.length); + } + else { + const literal = parseReplacer(replace, groupN); + + for (let i = 0; i < ranges.length; i++) { + const prevEnd = i - 1 >= 0 ? ranges[i - 1].end : undefined; + const nextStart = i + 1 < ranges.length ? ranges[i + 1].start : undefined; + const range = ranges[i]; + res[res.length] = string.substring(text, offset, range.start); + res[res.length] = executeReplacer(text, range, literal, prevEnd, nextStart); + offset = range.end; + } + + res[res.length] = string.substring(text, offset, text.length); + } + return string.stringBuild(res); + } + + if (typeof replace === "string") { + for (let i = 0; i < ranges.length; i++) { + const range = ranges[i]; + res[res.length] = string.substring(text, offset, range.start); + res[res.length] = replace; + offset = range.end; + } + + res[res.length] = string.substring(text, offset, text.length); + } + else { + for (let i = 0; i < ranges.length; i++) { + const range = ranges[i]; + const args: any[] = range.matches; + args[args.length] = range.start; + args[args.length] = text; + args[args.length] = range.groups; + + res[res.length] = string.substring(text, offset, range.start); + res[res.length] = func.invoke(replace, undefined, args); + offset = range.end; + } + + res[res.length] = string.substring(text, offset, text.length); + } + + return string.stringBuild(res); +} + +export function applySplits(text: string, limit: number | undefined, next: (offset: number) => { start: number; end: number; } | undefined) { + let lastEnd = 0; + let lastEmpty = true; + let offset = 0; + + const res: string[] = []; + + while (true) { + if (limit != null && res.length >= limit) break; + + const curr = next(offset); + + if (curr == null) { + if (!lastEmpty) res[res.length] = string.substring(text, lastEnd, text.length); + break; + } + + const { start, end } = curr; + const empty = start === end; + + if (offset > 0 || !empty) res[res.length] = string.substring(text, lastEnd, start); + + lastEnd = end; + offset = empty ? end + 1 : end; + lastEmpty = empty; + } + + return res; +} diff --git a/src/lib/ts/_entry.ts b/src/lib/ts/_entry.ts index 74f8845..b696fc3 100644 --- a/src/lib/ts/_entry.ts +++ b/src/lib/ts/_entry.ts @@ -1,19 +1,28 @@ -import { createDocumentRegistry, createLanguageService, ModuleKind, ScriptSnapshot, ScriptTarget, type Diagnostic, type CompilerOptions, type IScriptSnapshot, flattenDiagnosticMessageText, CompilerHost } from "typescript"; +import { createDocumentRegistry, createLanguageService, ModuleKind, ScriptSnapshot, ScriptTarget, type Diagnostic, type CompilerOptions, type IScriptSnapshot, flattenDiagnosticMessageText, CompilerHost, LanguageService } from "typescript"; -declare function getResource(name: string): string; +// declare function getResource(name: string): string | undefined; declare function print(...args: any[]): void; -declare function register(factory: CompilerFactory): void; +// declare function register(factory: CompilerFactory): void; type CompilerFactory = (next: Compiler) => Compiler; type Compiler = (filename: string, src: string, maps: any[]) => Function; const resources: Record = {}; +function getResource(name: string): string | undefined { + if (name === "/lib.d.ts") return "declare var a = 10;"; + return undefined; +} + function resource(name: string) { if (name in resources) return resources[name]; else return resources[name] = getResource(name); } +function register(factory: CompilerFactory): void { + factory((filename, src) => Function(src)); +} + register(next => { const files: Record = {}; const versions: Record = {}; @@ -32,34 +41,39 @@ register(next => { declaration: true, }; - const service = createLanguageService({ - getCurrentDirectory: () => "/", - getDefaultLibFileName: () => "/lib.d.ts", - getScriptFileNames: () => { - const res = ["/src.ts", "/lib.d.ts"]; - for (let i = 0; i < declI; i++) res.push("/src." + i + ".d.ts"); - return res; - }, - getCompilationSettings: () => settings, - log: print, - fileExists: filename => filename in files || resource(filename) != null, - - getScriptSnapshot: (filename) => { - if (filename in files) return files[filename]; - else { - const src = resource(filename); - if (src == null) return undefined; - return files[filename] = ScriptSnapshot.fromString(src); - } - }, - getScriptVersion: (filename) => String(versions[filename] || 0), - - readFile: () => { throw "no"; }, - writeFile: () => { throw "no"; }, - }, createDocumentRegistry()); - - service.getEmitOutput("/lib.d.ts"); - print("Loaded typescript..."); + let service: LanguageService; + + measure(() => { + service = createLanguageService({ + getCurrentDirectory: () => "/", + getDefaultLibFileName: () => "/lib.d.ts", + getScriptFileNames: () => { + const res = ["/src.ts", "/lib.d.ts"]; + for (let i = 0; i < declI; i++) res.push("/src." + i + ".d.ts"); + return res; + }, + getCompilationSettings: () => settings, + log: print, + fileExists: filename => filename in files || resource(filename) != null, + + getScriptSnapshot: (filename) => { + if (filename in files) return files[filename]; + else { + const src = resource(filename); + if (src == null) return undefined; + return files[filename] = ScriptSnapshot.fromString(src); + } + }, + getScriptVersion: (filename) => String(versions[filename] || 0), + + readFile: () => { throw "no"; }, + writeFile: () => { throw "no"; }, + }, createDocumentRegistry()); + }); + measure(() => { + service.getEmitOutput("/lib.d.ts"); + }); + print("Loaded typescript!"); return (code, filename, mapChain) => { files["/src.ts"] = ScriptSnapshot.fromString(code); diff --git a/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java b/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java index 0b3cd79..702ada4 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java @@ -5,9 +5,13 @@ import java.io.FileInputStream; import java.io.IOException; import java.util.Arrays; import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; +import java.util.WeakHashMap; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import me.topchetoeu.jscript.common.Metadata; import me.topchetoeu.jscript.common.Reading; @@ -18,8 +22,6 @@ import me.topchetoeu.jscript.common.json.JSON; import me.topchetoeu.jscript.common.parsing.Filename; import me.topchetoeu.jscript.runtime.debug.DebugContext; import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.Member.FieldMember; -import me.topchetoeu.jscript.runtime.values.Member.PropertyMember; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; import me.topchetoeu.jscript.runtime.values.functions.NativeFunction; @@ -104,82 +106,145 @@ public class SimpleRepl { var prototype = new ObjectValue[1]; NativeFunction mapConstr = new NativeFunction(args -> { - return UserValue.of(new LinkedHashMap<>(), prototype[0]); + var isWeak = args.get(0).toBoolean(); + return UserValue.of(isWeak ? new WeakHashMap<>() : new LinkedHashMap<>(), prototype[0]); }); - mapConstr.prototype.defineOwnMember(env, "get", new NativeFunction(getArgs -> { - var map = getArgs.self(LinkedHashMap.class); + mapConstr.prototype.defineOwnField(env, "get", new NativeFunction(getArgs -> { + var map = getArgs.self(Map.class); var key = getArgs.get(0); var val = map.get(key); return val == null ? Value.UNDEFINED : (Value)val; })); - mapConstr.prototype.defineOwnMember(env, "set", new NativeFunction(getArgs -> { - var map = getArgs.self(LinkedHashMap.class); + mapConstr.prototype.defineOwnField(env, "set", new NativeFunction(getArgs -> { + var map = getArgs.self(Map.class); var key = getArgs.get(0); var val = getArgs.get(1); map.put(key, val); return Value.UNDEFINED; })); - mapConstr.prototype.defineOwnMember(env, "has", new NativeFunction(getArgs -> { - var map = getArgs.self(LinkedHashMap.class); + mapConstr.prototype.defineOwnField(env, "has", new NativeFunction(getArgs -> { + var map = getArgs.self(Map.class); var key = getArgs.get(0); return BoolValue.of(map.containsKey(key)); })); - mapConstr.prototype.defineOwnMember(env, "delete", new NativeFunction(getArgs -> { - var map = getArgs.self(LinkedHashMap.class); + mapConstr.prototype.defineOwnField(env, "delete", new NativeFunction(getArgs -> { + var map = getArgs.self(Map.class); var key = getArgs.get(0); map.remove(key); return Value.UNDEFINED; })); - mapConstr.prototype.defineOwnMember(env, "keys", new NativeFunction(getArgs -> { - var map = getArgs.self(LinkedHashMap.class); + mapConstr.prototype.defineOwnField(env, "keys", new NativeFunction(getArgs -> { + var map = getArgs.self(Map.class); return ArrayValue.of(map.keySet()); })); + mapConstr.prototype.defineOwnField(env, "clear", new NativeFunction(getArgs -> { + getArgs.self(Map.class).clear(); + return Value.UNDEFINED; + })); prototype[0] = (ObjectValue)mapConstr.prototype; return mapConstr; } + public static String processRegex(String src) { + var n = 0; + + var source = new StringBuilder(); + + var inBrackets = false; + + while (true) { + if (n >= src.length()) break; + var c = src.charAt(n++); + + if (c == '\\') { + if (n >= src.length()) throw new PatternSyntaxException("Unexpected end", src, n); + c = src.charAt(n++); + source.append('\\').append(c); + } + else if (c == '[') { + if (inBrackets) source.append("\\["); + else { + inBrackets = true; + source.append('['); + } + } + else if (c == ']') { + if (inBrackets) { + inBrackets = false; + source.append(']'); + } + else throw new PatternSyntaxException("Unexpected ']'", src, n); + } + else source.append(c); + } + + return source.toString(); + } + private static ObjectValue regexPrimordials(Environment env) { var res = new ObjectValue(); res.setPrototype(null, null); var prototype = new ObjectValue[1]; NativeFunction mapConstr = new NativeFunction(args -> { - var pattern = Pattern.compile(args.get(0).toString(args.env)); - return UserValue.of(pattern, prototype[0]); + var flags = 0; + if (args.get(1).toBoolean()) flags |= Pattern.MULTILINE; + if (args.get(2).toBoolean()) flags |= Pattern.CASE_INSENSITIVE; + if (args.get(3).toBoolean()) flags |= Pattern.DOTALL; + if (args.get(4).toBoolean()) flags |= Pattern.UNICODE_CASE | Pattern.CANON_EQ; + if (args.get(5).toBoolean()) flags |= Pattern.UNICODE_CHARACTER_CLASS; + try { + var pattern = Pattern.compile(processRegex(args.get(0).toString(args.env)), flags); + return UserValue.of(pattern, prototype[0]); + } + catch (PatternSyntaxException e) { + throw EngineException.ofSyntax("(regex):" + e.getIndex() + ": " + e.getDescription()); + } }); - mapConstr.prototype.defineOwnMember(env, "exec", new NativeFunction(args -> { + mapConstr.prototype.defineOwnField(env, "exec", new NativeFunction(args -> { var pattern = args.self(Pattern.class); var target = args.get(0).toString(args.env); var offset = args.get(1).toNumber(args.env).getInt(); var index = args.get(2).toBoolean(); - var matcher = pattern.matcher(target).region(offset, target.length()); + if (offset > target.length()) return Value.NULL; - var obj = new ArrayValue(); - for (var i = 0; i < matcher.groupCount(); i++) { - obj.set(args.env, i, StringValue.of(matcher.group(i))); + var matcher = pattern.matcher(target).region(offset, target.length()); + if (!matcher.find()) return Value.NULL; + + var matchesArr = new ArrayValue(matcher.groupCount() + 1); + for (var i = 0; i < matcher.groupCount() + 1; i++) { + var group = matcher.group(i); + if (group == null) continue; + matchesArr.set(args.env, i, StringValue.of(group)); } - obj.defineOwnMember(args.env, "index", NumberValue.of(matcher.start())); - obj.defineOwnMember(args.env, "input", StringValue.of(target)); + matchesArr.defineOwnField(args.env, "index", NumberValue.of(matcher.start())); + matchesArr.defineOwnField(args.env, "input", StringValue.of(target)); if (index) { var indices = new ArrayValue(); indices.setPrototype(args.env, null); for (var i = 0; i < matcher.groupCount(); i++) { - obj.set(args.env, i, ArrayValue.of(Arrays.asList( + matchesArr.set(args.env, i, ArrayValue.of(Arrays.asList( NumberValue.of(matcher.start(i)), NumberValue.of(matcher.end(i)) ))); } - } + var obj = new ObjectValue(); + obj.defineOwnField(args.env, "matches", matchesArr); + obj.defineOwnField(args.env, "end", NumberValue.of(matcher.end())); - return Value.UNDEFINED; + return obj; // return val == null ? Value.UNDEFINED : (Value)val; })); + mapConstr.prototype.defineOwnField(env, "groupCount", new NativeFunction(args -> { + var pattern = args.self(Pattern.class); + return NumberValue.of(pattern.matcher("").groupCount()); + })); prototype[0] = (ObjectValue)mapConstr.prototype; return mapConstr; @@ -189,10 +254,10 @@ public class SimpleRepl { var res = new ObjectValue(); res.setPrototype(null, null); - res.defineOwnMember(env, "makeSymbol", new NativeFunction(args -> new SymbolValue(args.get(0).toString(args.env)))); - res.defineOwnMember(env, "getSymbol", new NativeFunction(args -> SymbolValue.get(args.get(0).toString(args.env)))); - res.defineOwnMember(env, "getSymbolKey", new NativeFunction(args -> ((SymbolValue)args.get(0)).key())); - res.defineOwnMember(env, "getSymbolDescriptor", new NativeFunction(args -> StringValue.of(((SymbolValue)args.get(0)).value))); + res.defineOwnField(env, "makeSymbol", new NativeFunction(args -> new SymbolValue(args.get(0).toString(args.env)))); + res.defineOwnField(env, "getSymbol", new NativeFunction(args -> SymbolValue.get(args.get(0).toString(args.env)))); + res.defineOwnField(env, "getSymbolKey", new NativeFunction(args -> ((SymbolValue)args.get(0)).key())); + res.defineOwnField(env, "getSymbolDescriptor", new NativeFunction(args -> StringValue.of(((SymbolValue)args.get(0)).value))); return res; } @@ -201,7 +266,7 @@ public class SimpleRepl { var res = new ObjectValue(); res.setPrototype(null, null); - res.defineOwnMember(env, "parseInt", new NativeFunction(args -> { + res.defineOwnField(env, "parseInt", new NativeFunction(args -> { var nradix = args.get(1).toNumber(env); var radix = nradix.isInt() ? nradix.getInt() : 10; @@ -211,15 +276,15 @@ public class SimpleRepl { } else return NumberValue.parseInt(args.get(0).toString(), radix, false); })); - res.defineOwnMember(env, "parseFloat", new NativeFunction(args -> { + res.defineOwnField(env, "parseFloat", new NativeFunction(args -> { if (args.get(0) instanceof NumberValue) { return args.get(0); } else return NumberValue.parseFloat(args.get(0).toString(), false); })); - res.defineOwnMember(env, "isNaN", new NativeFunction(args -> BoolValue.of(args.get(0).isNaN()))); - res.defineOwnMember(env, "NaN", NumberValue.NAN); - res.defineOwnMember(env, "Infinity", NumberValue.of(Double.POSITIVE_INFINITY)); + res.defineOwnField(env, "isNaN", new NativeFunction(args -> BoolValue.of(args.get(0).isNaN()))); + res.defineOwnField(env, "NaN", NumberValue.NAN); + res.defineOwnField(env, "Infinity", NumberValue.of(Double.POSITIVE_INFINITY)); return res; } @@ -228,7 +293,7 @@ public class SimpleRepl { var res = new ObjectValue(); res.setPrototype(null, null); - res.defineOwnMember(env, "stringBuild", new NativeFunction(args -> { + res.defineOwnField(env, "stringBuild", new NativeFunction(args -> { var parts = ((ArrayValue)args.get(0)).toArray(); var sb = new StringBuilder(); @@ -239,15 +304,46 @@ public class SimpleRepl { return StringValue.of(sb.toString()); })); - res.defineOwnMember(env, "fromCharCode", new NativeFunction(args -> { - var parts = ((ArrayValue)args.get(0)).toArray(); - var sb = new StringBuilder(); + res.defineOwnField(env, "fromCharCode", new NativeFunction(args -> { + return StringValue.of(new String(new char[] { (char)args.get(0).toNumber(args.env).getInt() })); + })); - for (var i = 0; i < parts.length; i++) { - sb.append(((StringValue)parts[i]).value); - } + res.defineOwnField(env, "toCharCode", new NativeFunction(args -> { + return NumberValue.of(args.get(0).toString(args.env).charAt(0)); + })); + res.defineOwnField(env, "toCodePoint", new NativeFunction(args -> { + return NumberValue.of(args.get(0).toString(args.env).codePointAt(args.get(1).toNumber(args.env).getInt())); + })); - return StringValue.of(sb.toString()); + res.defineOwnField(env, "substring", new NativeFunction(args -> { + var str = args.get(0).toString(args.env); + var start = args.get(1).toNumber(args.env).getInt(); + var end = args.get(2).toNumber(args.env).getInt(); + + if (end <= start) return StringValue.of(""); + + start = Math.max(Math.min(start, str.length()), 0); + end = Math.max(Math.min(end, str.length()), 0); + + return StringValue.of(str.substring(start, end)); + })); + + res.defineOwnField(env, "indexOf", new NativeFunction(args -> { + var str = args.get(0).toString(args.env); + var search = args.get(1).toString(args.env); + var start = args.get(2).toNumber(args.env).getInt(); + if (start > str.length()) return NumberValue.of(-1); + var reverse = args.get(3).toBoolean(); + + if (reverse) return NumberValue.of(str.lastIndexOf(search, start)); + else return NumberValue.of(str.indexOf(search, start)); + })); + + res.defineOwnField(env, "lower", new NativeFunction(args -> { + return StringValue.of(args.get(0).toString(args.env).toLowerCase()); + })); + res.defineOwnField(env, "upper", new NativeFunction(args -> { + return StringValue.of(args.get(0).toString(args.env).toUpperCase()); })); return res; @@ -257,35 +353,61 @@ public class SimpleRepl { var res = new ObjectValue(); res.setPrototype(null, null); - res.defineOwnMember(env, "defineField", new NativeFunction(args -> { + res.defineOwnField(env, "defineField", new NativeFunction(args -> { var obj = (ObjectValue)args.get(0); var key = args.get(1); - var writable = args.get(2).toBoolean(); - var enumerable = args.get(3).toBoolean(); - var configurable = args.get(4).toBoolean(); - var value = args.get(5); + var desc = (ObjectValue)args.get(2); - return BoolValue.of(obj.defineOwnMember(args.env, key, FieldMember.of(obj, value, configurable, enumerable, writable))); + var valField = desc.getOwnMember(env, "v"); + var writeField = desc.getOwnMember(env, "w"); + var configField = desc.getOwnMember(env, "c"); + var enumField = desc.getOwnMember(env, "e"); + + var enumerable = enumField == null ? null : enumField.get(env, desc).toBoolean(); + var configurable = configField == null ? null : configField.get(env, desc).toBoolean(); + var writable = writeField == null ? null : writeField.get(env, desc).toBoolean(); + var value = valField == null ? null : valField.get(env, desc); + + return BoolValue.of(obj.defineOwnField(args.env, key, value, configurable, enumerable, writable)); })); - res.defineOwnMember(env, "defineProperty", new NativeFunction(args -> { + res.defineOwnField(env, "defineProperty", new NativeFunction(args -> { var obj = (ObjectValue)args.get(0); var key = args.get(1); - var enumerable = args.get(2).toBoolean(); - var configurable = args.get(3).toBoolean(); - var getter = args.get(4) instanceof VoidValue ? null : (FunctionValue)args.get(4); - var setter = args.get(5) instanceof VoidValue ? null : (FunctionValue)args.get(5); + var desc = args.get(2); - return BoolValue.of(obj.defineOwnMember(args.env, key, new PropertyMember(obj, getter, setter, configurable, enumerable))); + var configField = desc.getOwnMember(env, "c"); + var enumField = desc.getOwnMember(env, "e"); + var getField = desc.getOwnMember(env, "g"); + var setField = desc.getOwnMember(env, "s"); + + var enumerable = enumField == null ? null : enumField.get(env, desc).toBoolean(); + var configurable = configField == null ? null : configField.get(env, desc).toBoolean(); + Optional getter = null, setter = null; + + if (getField != null) { + var getVal = getField.get(env, desc); + if (getVal == Value.UNDEFINED) getter = Optional.empty(); + else getter = Optional.of((FunctionValue)getVal); + } + if (setField != null) { + var setVal = setField.get(env, desc); + if (setVal == Value.UNDEFINED) setter = Optional.empty(); + else setter = Optional.of((FunctionValue)setVal); + } + + return BoolValue.of(obj.defineOwnProperty(args.env, key, getter, setter, configurable, enumerable)); })); - res.defineOwnMember(env, "getPrototype", new NativeFunction(args -> { - return args.get(0).getPrototype(env); + res.defineOwnField(env, "getPrototype", new NativeFunction(args -> { + var proto = args.get(0).getPrototype(env); + if (proto == null) return Value.NULL; + else return proto; })); - res.defineOwnMember(env, "setPrototype", new NativeFunction(args -> { + res.defineOwnField(env, "setPrototype", new NativeFunction(args -> { var proto = args.get(1) instanceof VoidValue ? null : (ObjectValue)args.get(1); args.get(0).setPrototype(env, proto); return args.get(0); })); - res.defineOwnMember(env, "getOwnMembers", new NativeFunction(args -> { + res.defineOwnField(env, "getOwnMembers", new NativeFunction(args -> { var val = new ArrayValue(); for (var key : args.get(0).getOwnMembers(env, args.get(1).toBoolean())) { @@ -294,12 +416,43 @@ public class SimpleRepl { return val; })); - res.defineOwnMember(env, "getOwnSymbolMembers", new NativeFunction(args -> { + res.defineOwnField(env, "getOwnSymbolMembers", new NativeFunction(args -> { return ArrayValue.of(args.get(0).getOwnSymbolMembers(env, args.get(1).toBoolean())); })); - res.defineOwnMember(env, "isArray", new NativeFunction(args -> { + res.defineOwnField(env, "getOwnMember", new NativeFunction(args -> { + var obj = args.get(0); + var key = args.get(1); + + var member = obj.getOwnMember(args.env, key); + if (member == null) return Value.UNDEFINED; + else return member.descriptor(args.env, obj); + })); + res.defineOwnField(env, "isArray", new NativeFunction(args -> { return BoolValue.of(args.get(0) instanceof ArrayValue); })); + res.defineOwnField(env, "preventExt", new NativeFunction(args -> { + args.get(0).preventExtensions(); + return VoidValue.UNDEFINED; + })); + res.defineOwnField(env, "seal", new NativeFunction(args -> { + args.get(0).seal(); + return VoidValue.UNDEFINED; + })); + res.defineOwnField(env, "freeze", new NativeFunction(args -> { + args.get(0).freeze(); + return VoidValue.UNDEFINED; + })); + res.defineOwnField(env, "memcpy", new NativeFunction(args -> { + var src = (ArrayValue)args.get(0); + var dst = (ArrayValue)args.get(1); + var srcI = args.get(2).toNumber(args.env).getInt(); + var dstI = args.get(3).toNumber(args.env).getInt(); + var n = args.get(4).toNumber(args.env).getInt(); + + src.copyTo(dst, srcI, dstI, n); + + return VoidValue.UNDEFINED; + })); return res; } @@ -308,39 +461,39 @@ public class SimpleRepl { var res = new ObjectValue(); res.setPrototype(null, null); - res.defineOwnMember(env, "setCallable", new NativeFunction(args -> { + res.defineOwnField(env, "setCallable", new NativeFunction(args -> { var func = (FunctionValue)args.get(0); func.enableApply = args.get(1).toBoolean(); return Value.UNDEFINED; })); - res.defineOwnMember(env, "setConstructable", new NativeFunction(args -> { + res.defineOwnField(env, "setConstructable", new NativeFunction(args -> { var func = (FunctionValue)args.get(0); func.enableConstruct = args.get(1).toBoolean(); return Value.UNDEFINED; })); - res.defineOwnMember(env, "invokeType", new NativeFunction(args -> { + res.defineOwnField(env, "invokeType", new NativeFunction(args -> { if (((ArgumentsValue)args.get(0)).frame.isNew) return StringValue.of("new"); else return StringValue.of("call"); })); - res.defineOwnMember(env, "invokeTypeInfer", new NativeFunction(args -> { + res.defineOwnField(env, "invokeTypeInfer", new NativeFunction(args -> { var frame = Frame.get(args.env, args.get(0).toNumber(args.env).getInt()); if (frame.isNew) return StringValue.of("new"); else return StringValue.of("call"); })); - res.defineOwnMember(env, "target", new NativeFunction(args -> { + res.defineOwnField(env, "target", new NativeFunction(args -> { var frame = Frame.get(args.env, args.get(0).toNumber(args.env).getInt()); if (frame.target == null) return Value.UNDEFINED; else return frame.target; })); - res.defineOwnMember(env, "invoke", new NativeFunction(args -> { + res.defineOwnField(env, "invoke", new NativeFunction(args -> { var func = (FunctionValue)args.get(0); var self = args.get(1); var funcArgs = (ArrayValue)args.get(2); return func.apply(env, self, funcArgs.toArray()); })); - res.defineOwnMember(env, "construct", new NativeFunction(args -> { + res.defineOwnField(env, "construct", new NativeFunction(args -> { var func = (FunctionValue)args.get(0); var target = args.get(1); var funcArgs = (ArrayValue)args.get(2); @@ -356,10 +509,10 @@ public class SimpleRepl { var res = new ObjectValue(); res.setPrototype(null, null); - res.defineOwnMember(env, "stringify", new NativeFunction(args -> { + res.defineOwnField(env, "stringify", new NativeFunction(args -> { return StringValue.of(JSON.stringify(JSONConverter.fromJs(env, args.get(0)))); })); - res.defineOwnMember(env, "parse", new NativeFunction(args -> { + res.defineOwnField(env, "parse", new NativeFunction(args -> { return JSONConverter.toJs(JSON.parse(null, args.get(0).toString(env))); })); @@ -377,18 +530,18 @@ public class SimpleRepl { var res = new ObjectValue(); res.setPrototype(null, null); - res.defineOwnMember(env, "symbol", symbolPrimordials(env)); - res.defineOwnMember(env, "number", numberPrimordials(env)); - res.defineOwnMember(env, "string", stringPrimordials(env)); - res.defineOwnMember(env, "object", objectPrimordials(env)); - res.defineOwnMember(env, "function", functionPrimordials(env)); - res.defineOwnMember(env, "json", jsonPrimordials(env)); - res.defineOwnMember(env, "map", mapPrimordials(env)); - res.defineOwnMember(env, "regex", regexPrimordials(env)); + res.defineOwnField(env, "symbol", symbolPrimordials(env)); + res.defineOwnField(env, "number", numberPrimordials(env)); + res.defineOwnField(env, "string", stringPrimordials(env)); + res.defineOwnField(env, "object", objectPrimordials(env)); + res.defineOwnField(env, "function", functionPrimordials(env)); + res.defineOwnField(env, "json", jsonPrimordials(env)); + res.defineOwnField(env, "map", mapPrimordials(env)); + res.defineOwnField(env, "regex", regexPrimordials(env)); int[] i = new int[1]; - res.defineOwnMember(env, "setGlobalPrototypes", new NativeFunction(args -> { + res.defineOwnField(env, "setGlobalPrototypes", new NativeFunction(args -> { var obj = (ObjectValue)args.get(0); setProto(args.env, env, Value.OBJECT_PROTO, obj, "object"); @@ -408,7 +561,7 @@ public class SimpleRepl { } return Value.UNDEFINED; })); - res.defineOwnMember(env, "setIntrinsic", new NativeFunction(args -> { + res.defineOwnField(env, "setIntrinsic", new NativeFunction(args -> { var name = args.get(0).toString(env); var val = args.get(1); @@ -416,9 +569,12 @@ public class SimpleRepl { return Value.UNDEFINED; })); - res.defineOwnMember(env, "compile", new NativeFunction(args -> { + res.defineOwnField(env, "compile", new NativeFunction(args -> { return Compiler.compileFunc(env, new Filename("jscript", "func" + i[0]++ + ".js"), args.get(0).toString(env)); })); + res.defineOwnField(env, "now", new NativeFunction(args -> { + return NumberValue.of(System.currentTimeMillis()); + })); return res; } @@ -426,8 +582,8 @@ public class SimpleRepl { private static Environment createESEnv() throws InterruptedException, ExecutionException { var env = initEnv(); var stubEnv = initEnv(); - Value.global(stubEnv).defineOwnMember(stubEnv, "target", Value.global(env)); - Value.global(stubEnv).defineOwnMember(stubEnv, "primordials", primordials(env)); + Value.global(stubEnv).defineOwnField(stubEnv, "target", Value.global(env)); + Value.global(stubEnv).defineOwnField(stubEnv, "primordials", primordials(env)); EventLoop.get(stubEnv).pushMsg( false, stubEnv, @@ -443,29 +599,29 @@ public class SimpleRepl { env.add(EventLoop.KEY, engine); env.add(DebugContext.KEY, new DebugContext()); env.add(Compiler.KEY, Compiler.DEFAULT); - // environment.add(CompileResult.DEBUG_LOG); + // env.add(CompileResult.DEBUG_LOG); var glob = Value.global(env); - glob.defineOwnMember(null, "exit", new NativeFunction("exit", args -> { + glob.defineOwnField(null, "exit", new NativeFunction("exit", args -> { Thread.currentThread().interrupt(); throw new CancellationException(); })); - glob.defineOwnMember(null, "print", new NativeFunction("print", args -> { + glob.defineOwnField(null, "print", new NativeFunction("print", args -> { for (var el : args.args) { - if (el instanceof StringValue) System.out.print(((StringValue)el).value); - else System.out.print(el.toReadable(args.env)); + if (el instanceof StringValue) System.out.print(((StringValue)el).value + " \t"); + else System.out.print(el.toReadable(args.env) + " \t"); } System.out.println(); return Value.UNDEFINED; })); - glob.defineOwnMember(null, "measure", new NativeFunction("measure", args -> { + glob.defineOwnField(null, "measure", new NativeFunction("measure", args -> { var start = System.nanoTime(); ((FunctionValue)args.get(0)).apply(args.env, Value.UNDEFINED); - System.out.println(String.format("Finished in %sns", System.nanoTime() - start)); + System.out.println(String.format("Finished in %sms", (System.nanoTime() - start) / 1000000.)); return Value.UNDEFINED; }));