feat: implement array polyfills in java
This commit is contained in:
parent
d8071af480
commit
f2cd50726d
22
lib/core.ts
22
lib/core.ts
@ -7,7 +7,8 @@ interface Environment {
|
|||||||
interface Internals {
|
interface Internals {
|
||||||
object: ObjectConstructor;
|
object: ObjectConstructor;
|
||||||
function: FunctionConstructor;
|
function: FunctionConstructor;
|
||||||
promise: typeof Promise;
|
array: ArrayConstructor;
|
||||||
|
promise: PromiseConstructor;
|
||||||
|
|
||||||
markSpecial(...funcs: Function[]): void;
|
markSpecial(...funcs: Function[]): void;
|
||||||
getEnv(func: Function): Environment | undefined;
|
getEnv(func: Function): Environment | undefined;
|
||||||
@ -35,32 +36,31 @@ interface Internals {
|
|||||||
|
|
||||||
sort(arr: any[], comaprator: (a: any, b: any) => number): void;
|
sort(arr: any[], comaprator: (a: any, b: any) => number): void;
|
||||||
|
|
||||||
constructor: {
|
log(...args: any[]): void;
|
||||||
log(...args: any[]): void;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var env: Environment = arguments[0], internals: Internals = arguments[1];
|
|
||||||
globalThis.log = internals.constructor.log;
|
|
||||||
var i = 0.0;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
var env: Environment = arguments[0], internals: Internals = arguments[1];
|
||||||
|
|
||||||
var Object = env.global.Object = internals.object;
|
var Object = env.global.Object = internals.object;
|
||||||
var Function = env.global.Function = internals.function;
|
var Function = env.global.Function = internals.function;
|
||||||
|
var Array = env.global.Array = internals.array;
|
||||||
var Promise = env.global.Promise = internals.promise;
|
var Promise = env.global.Promise = internals.promise;
|
||||||
|
|
||||||
env.setProto('object', Object.prototype);
|
env.setProto('object', Object.prototype);
|
||||||
env.setProto('function', Function.prototype);
|
env.setProto('function', Function.prototype);
|
||||||
|
env.setProto('array', Array.prototype);
|
||||||
(Object.prototype as any).__proto__ = null;
|
(Object.prototype as any).__proto__ = null;
|
||||||
|
|
||||||
run('values/symbol');
|
internals.getEnv(run)?.setProto('array', Array.prototype);
|
||||||
|
|
||||||
|
globalThis.log = (...args) => internals.apply(internals.log, internals, args);
|
||||||
|
|
||||||
|
run('values/symbol');
|
||||||
run('values/errors');
|
run('values/errors');
|
||||||
run('values/string');
|
run('values/string');
|
||||||
run('values/number');
|
run('values/number');
|
||||||
run('values/boolean');
|
run('values/boolean');
|
||||||
run('values/array');
|
|
||||||
run('map');
|
run('map');
|
||||||
run('set');
|
run('set');
|
||||||
run('regex');
|
run('regex');
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
"values/string.ts",
|
"values/string.ts",
|
||||||
"values/number.ts",
|
"values/number.ts",
|
||||||
"values/boolean.ts",
|
"values/boolean.ts",
|
||||||
"values/array.ts",
|
|
||||||
"map.ts",
|
"map.ts",
|
||||||
"set.ts",
|
"set.ts",
|
||||||
"regex.ts",
|
"regex.ts",
|
||||||
|
@ -7,7 +7,7 @@ function setProps<
|
|||||||
}
|
}
|
||||||
>(target: TargetT, desc: DescT) {
|
>(target: TargetT, desc: DescT) {
|
||||||
var props = internals.keys(desc, false);
|
var props = internals.keys(desc, false);
|
||||||
for (var i = 0; i < props.length; i++) {
|
for (var i = 0; i in props; i++) {
|
||||||
var key = props[i];
|
var key = props[i];
|
||||||
internals.defineField(
|
internals.defineField(
|
||||||
target, key, (desc as any)[key],
|
target, key, (desc as any)[key],
|
||||||
|
@ -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);
|
|
||||||
});
|
|
@ -15,7 +15,6 @@ 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 {
|
||||||
@ -63,9 +62,8 @@ public class Main {
|
|||||||
var in = new BufferedReader(new InputStreamReader(System.in));
|
var in = new BufferedReader(new InputStreamReader(System.in));
|
||||||
engine = new Engine();
|
engine = new Engine();
|
||||||
|
|
||||||
// TODO: Replace type register with safer accessor
|
env = new Environment(null, null, null);
|
||||||
env = new Environment(null, new NativeTypeRegister(), null);
|
var builderEnv = new Environment(null, null, null);
|
||||||
var builderEnv = new Environment(null, new NativeTypeRegister(), null);
|
|
||||||
var exited = new boolean[1];
|
var exited = new boolean[1];
|
||||||
|
|
||||||
env.global.define("exit", ctx -> {
|
env.global.define("exit", ctx -> {
|
||||||
@ -83,7 +81,12 @@ public class Main {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
engine.pushMsg(false, new Context(builderEnv, new MessageContext(engine)), "core.js", resourceToString("js/core.js"), null, env, new Internals()).toObservable().on(valuePrinter);
|
engine.pushMsg(
|
||||||
|
false,
|
||||||
|
new Context(builderEnv, new MessageContext(engine)),
|
||||||
|
"core.js", resourceToString("js/core.js"),
|
||||||
|
null, env, new Internals(env)
|
||||||
|
).toObservable().on(valuePrinter);
|
||||||
|
|
||||||
task = engine.start();
|
task = engine.start();
|
||||||
var reader = new Thread(() -> {
|
var reader = new Thread(() -> {
|
||||||
|
@ -11,6 +11,7 @@ import me.topchetoeu.jscript.exceptions.EngineException;
|
|||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
import me.topchetoeu.jscript.interop.NativeSetter;
|
import me.topchetoeu.jscript.interop.NativeSetter;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeWrapperProvider;
|
||||||
|
|
||||||
public class Environment {
|
public class Environment {
|
||||||
private HashMap<String, ObjectValue> prototypes = new HashMap<>();
|
private HashMap<String, ObjectValue> prototypes = new HashMap<>();
|
||||||
@ -33,7 +34,8 @@ public class Environment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Native public Symbol symbol(String name) {
|
@Native public Symbol symbol(String name) {
|
||||||
if (symbols.containsKey(name)) return symbols.get(name);
|
if (symbols.containsKey(name))
|
||||||
|
return symbols.get(name);
|
||||||
else {
|
else {
|
||||||
var res = new Symbol(name);
|
var res = new Symbol(name);
|
||||||
symbols.put(name, res);
|
symbols.put(name, res);
|
||||||
@ -79,14 +81,7 @@ public class Environment {
|
|||||||
|
|
||||||
public Environment(FunctionValue compile, WrappersProvider nativeConverter, GlobalScope global) {
|
public Environment(FunctionValue compile, WrappersProvider nativeConverter, GlobalScope global) {
|
||||||
if (compile == null) compile = new NativeFunction("compile", (ctx, thisArg, args) -> args.length == 0 ? "" : args[0]);
|
if (compile == null) compile = new NativeFunction("compile", (ctx, thisArg, args) -> args.length == 0 ? "" : args[0]);
|
||||||
if (nativeConverter == null) nativeConverter = new WrappersProvider() {
|
if (nativeConverter == null) nativeConverter = new NativeWrapperProvider(this);
|
||||||
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();
|
if (global == null) global = new GlobalScope();
|
||||||
|
|
||||||
this.wrappersProvider = nativeConverter;
|
this.wrappersProvider = nativeConverter;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -268,6 +268,10 @@ public class CodeFrame {
|
|||||||
if (res != Runners.NO_RETURN) return res;
|
if (res != Runners.NO_RETURN) return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Throwable e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
finally {
|
finally {
|
||||||
ctx.message.popFrame(this);
|
ctx.message.popFrame(this);
|
||||||
}
|
}
|
||||||
|
@ -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]));
|
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) {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -517,7 +517,7 @@ 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 new ConvertException(type(obj), clazz.getName());
|
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) throws InterruptedException {
|
||||||
@ -631,7 +631,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(" ] ");
|
||||||
}
|
}
|
||||||
|
@ -8,6 +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;
|
public boolean thisArg() default false;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +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;
|
public boolean thisArg() default false;
|
||||||
}
|
}
|
||||||
|
@ -1,210 +1,227 @@
|
|||||||
package me.topchetoeu.jscript.interop;
|
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.WrappersProvider;
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.WrappersProvider;
|
||||||
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
public class NativeTypeRegister implements WrappersProvider {
|
|
||||||
private final HashMap<Class<?>, FunctionValue> constructors = new HashMap<>();
|
public class NativeWrapperProvider implements WrappersProvider {
|
||||||
private final HashMap<Class<?>, ObjectValue> prototypes = new HashMap<>();
|
private final HashMap<Class<?>, FunctionValue> constructors = new HashMap<>();
|
||||||
|
private final HashMap<Class<?>, ObjectValue> prototypes = new HashMap<>();
|
||||||
private static void applyMethods(boolean member, ObjectValue target, Class<?> clazz) {
|
private final Environment env;
|
||||||
for (var method : clazz.getDeclaredMethods()) {
|
|
||||||
var nat = method.getAnnotation(Native.class);
|
private static void applyMethods(Environment env, boolean member, ObjectValue target, Class<?> clazz) {
|
||||||
var get = method.getAnnotation(NativeGetter.class);
|
for (var method : clazz.getDeclaredMethods()) {
|
||||||
var set = method.getAnnotation(NativeSetter.class);
|
var nat = method.getAnnotation(Native.class);
|
||||||
var memberMismatch = !Modifier.isStatic(method.getModifiers()) != member;
|
var get = method.getAnnotation(NativeGetter.class);
|
||||||
|
var set = method.getAnnotation(NativeSetter.class);
|
||||||
if (nat != null) {
|
var memberMismatch = !Modifier.isStatic(method.getModifiers()) != member;
|
||||||
if (nat.thisArg() != member && memberMismatch) continue;
|
|
||||||
|
if (nat != null) {
|
||||||
var name = nat.value();
|
if (nat.thisArg() != member && memberMismatch) continue;
|
||||||
var val = target.values.get(name);
|
|
||||||
|
Object name = nat.value();
|
||||||
if (name.equals("")) name = method.getName();
|
if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2));
|
||||||
if (!(val instanceof OverloadFunction)) target.defineProperty(null, name, val = new OverloadFunction(name));
|
else if (name.equals("")) name = method.getName();
|
||||||
|
|
||||||
((OverloadFunction)val).overloads.add(Overload.fromMethod(method, nat.thisArg()));
|
var val = target.values.get(name);
|
||||||
}
|
|
||||||
else {
|
if (!(val instanceof OverloadFunction)) target.defineProperty(null, name, val = new OverloadFunction(name.toString()));
|
||||||
if (get != null) {
|
|
||||||
if (get.thisArg() != member && memberMismatch) continue;
|
((OverloadFunction)val).overloads.add(Overload.fromMethod(method, nat.thisArg()));
|
||||||
|
}
|
||||||
var name = get.value();
|
else {
|
||||||
var prop = target.properties.get(name);
|
if (get != null) {
|
||||||
OverloadFunction getter = null;
|
if (get.thisArg() != member && memberMismatch) continue;
|
||||||
var setter = prop == null ? null : prop.setter;
|
|
||||||
|
Object name = get.value();
|
||||||
if (prop != null && prop.getter instanceof OverloadFunction) getter = (OverloadFunction)prop.getter;
|
if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2));
|
||||||
else getter = new OverloadFunction("get " + name);
|
else if (name.equals("")) name = method.getName();
|
||||||
|
|
||||||
getter.overloads.add(Overload.fromMethod(method, get.thisArg()));
|
var prop = target.properties.get(name);
|
||||||
target.defineProperty(null, name, getter, setter, true, true);
|
OverloadFunction getter = null;
|
||||||
}
|
var setter = prop == null ? null : prop.setter;
|
||||||
if (set != null) {
|
|
||||||
if (set.thisArg() != member && memberMismatch) continue;
|
if (prop != null && prop.getter instanceof OverloadFunction) getter = (OverloadFunction)prop.getter;
|
||||||
|
else getter = new OverloadFunction("get " + name);
|
||||||
var name = set.value();
|
|
||||||
var prop = target.properties.get(name);
|
getter.overloads.add(Overload.fromMethod(method, get.thisArg()));
|
||||||
var getter = prop == null ? null : prop.getter;
|
target.defineProperty(null, name, getter, setter, true, true);
|
||||||
OverloadFunction setter = null;
|
}
|
||||||
|
if (set != null) {
|
||||||
if (prop != null && prop.setter instanceof OverloadFunction) setter = (OverloadFunction)prop.setter;
|
if (set.thisArg() != member && memberMismatch) continue;
|
||||||
else setter = new OverloadFunction("set " + name);
|
|
||||||
|
Object name = set.value();
|
||||||
setter.overloads.add(Overload.fromMethod(method, set.thisArg()));
|
if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2));
|
||||||
target.defineProperty(null, name, getter, setter, true, true);
|
else if (name.equals("")) name = method.getName();
|
||||||
}
|
|
||||||
}
|
var prop = target.properties.get(name);
|
||||||
}
|
var getter = prop == null ? null : prop.getter;
|
||||||
}
|
OverloadFunction setter = null;
|
||||||
private static void applyFields(boolean member, ObjectValue target, Class<?> clazz) {
|
|
||||||
for (var field : clazz.getDeclaredFields()) {
|
if (prop != null && prop.setter instanceof OverloadFunction) setter = (OverloadFunction)prop.setter;
|
||||||
if (!Modifier.isStatic(field.getModifiers()) != member) continue;
|
else setter = new OverloadFunction("set " + name);
|
||||||
var nat = field.getAnnotation(Native.class);
|
|
||||||
|
setter.overloads.add(Overload.fromMethod(method, set.thisArg()));
|
||||||
if (nat != null) {
|
target.defineProperty(null, name, getter, setter, true, true);
|
||||||
var name = nat.value();
|
}
|
||||||
if (name.equals("")) name = field.getName();
|
}
|
||||||
var getter = new OverloadFunction("get " + name).add(Overload.getterFromField(field));
|
}
|
||||||
var setter = new OverloadFunction("set " + name).add(Overload.setterFromField(field));
|
}
|
||||||
target.defineProperty(null, name, getter, setter, true, false);
|
private static void applyFields(Environment env, boolean member, ObjectValue target, Class<?> clazz) {
|
||||||
}
|
for (var field : clazz.getDeclaredFields()) {
|
||||||
}
|
if (!Modifier.isStatic(field.getModifiers()) != member) continue;
|
||||||
}
|
var nat = field.getAnnotation(Native.class);
|
||||||
private static void applyClasses(boolean member, ObjectValue target, Class<?> clazz) {
|
|
||||||
for (var cl : clazz.getDeclaredClasses()) {
|
if (nat != null) {
|
||||||
if (!Modifier.isStatic(cl.getModifiers()) != member) continue;
|
Object name = nat.value();
|
||||||
var nat = cl.getAnnotation(Native.class);
|
if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2));
|
||||||
|
else if (name.equals("")) name = field.getName();
|
||||||
if (nat != null) {
|
|
||||||
var name = nat.value();
|
var getter = new OverloadFunction("get " + name).add(Overload.getterFromField(field));
|
||||||
if (name.equals("")) name = cl.getSimpleName();
|
var setter = new OverloadFunction("set " + name).add(Overload.setterFromField(field));
|
||||||
|
target.defineProperty(null, name, getter, setter, true, false);
|
||||||
var getter = new NativeFunction("get " + name, (ctx, thisArg, args) -> cl);
|
}
|
||||||
|
}
|
||||||
target.defineProperty(null, name, getter, null, true, false);
|
}
|
||||||
}
|
private static void applyClasses(Environment env, boolean member, ObjectValue target, Class<?> clazz) {
|
||||||
}
|
for (var cl : clazz.getDeclaredClasses()) {
|
||||||
}
|
if (!Modifier.isStatic(cl.getModifiers()) != member) continue;
|
||||||
|
var nat = cl.getAnnotation(Native.class);
|
||||||
/**
|
|
||||||
* Generates a prototype for the given class.
|
if (nat != null) {
|
||||||
* The returned object will have appropriate wrappers for all instance members.
|
Object name = nat.value();
|
||||||
* All accessors and methods will expect the this argument to be a native wrapper of the given class type.
|
if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2));
|
||||||
* @param clazz The class for which a prototype should be generated
|
else if (name.equals("")) name = cl.getName();
|
||||||
*/
|
|
||||||
public static ObjectValue makeProto(Class<?> clazz) {
|
var getter = new NativeFunction("get " + name, (ctx, thisArg, args) -> cl);
|
||||||
var res = new ObjectValue();
|
|
||||||
|
target.defineProperty(null, name, getter, null, true, false);
|
||||||
applyMethods(true, res, clazz);
|
}
|
||||||
applyFields(true, res, clazz);
|
}
|
||||||
applyClasses(true, res, clazz);
|
}
|
||||||
|
|
||||||
return res;
|
/**
|
||||||
}
|
* Generates a prototype for the given class.
|
||||||
/**
|
* The returned object will have appropriate wrappers for all instance members.
|
||||||
* Generates a constructor for the given class.
|
* All accessors and methods will expect the this argument to be a native wrapper of the given class type.
|
||||||
* The returned function will have appropriate wrappers for all static members.
|
* @param clazz The class for which a prototype should be generated
|
||||||
* 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
|
public static ObjectValue makeProto(Environment ctx, Class<?> clazz) {
|
||||||
*/
|
var res = new ObjectValue();
|
||||||
public static FunctionValue makeConstructor(Class<?> clazz) {
|
|
||||||
FunctionValue func = new OverloadFunction(clazz.getName());
|
applyMethods(ctx, true, res, clazz);
|
||||||
|
applyFields(ctx, true, res, clazz);
|
||||||
for (var overload : clazz.getConstructors()) {
|
applyClasses(ctx, true, res, clazz);
|
||||||
var nat = overload.getAnnotation(Native.class);
|
|
||||||
if (nat == null) continue;
|
return res;
|
||||||
((OverloadFunction)func).add(Overload.fromConstructor(overload, nat.thisArg()));
|
}
|
||||||
}
|
/**
|
||||||
for (var overload : clazz.getMethods()) {
|
* Generates a constructor for the given class.
|
||||||
var constr = overload.getAnnotation(NativeConstructor.class);
|
* The returned function will have appropriate wrappers for all static members.
|
||||||
if (constr == null) continue;
|
* When the function gets called, the underlying constructor will get called, unless the constructor is inaccessible.
|
||||||
((OverloadFunction)func).add(Overload.fromMethod(overload, constr.thisArg()));
|
* @param clazz The class for which a constructor should be generated
|
||||||
}
|
*/
|
||||||
|
public static FunctionValue makeConstructor(Environment ctx, Class<?> clazz) {
|
||||||
if (((OverloadFunction)func).overloads.size() == 0) {
|
FunctionValue func = new OverloadFunction(clazz.getName());
|
||||||
func = new NativeFunction(clazz.getName(), (a, b, c) -> { throw EngineException.ofError("This constructor is not invokable."); });
|
|
||||||
}
|
for (var overload : clazz.getConstructors()) {
|
||||||
|
var nat = overload.getAnnotation(Native.class);
|
||||||
applyMethods(false, func, clazz);
|
if (nat == null) continue;
|
||||||
applyFields(false, func, clazz);
|
((OverloadFunction)func).add(Overload.fromConstructor(overload, nat.thisArg()));
|
||||||
applyClasses(false, func, clazz);
|
}
|
||||||
|
for (var overload : clazz.getMethods()) {
|
||||||
func.special = true;
|
var constr = overload.getAnnotation(NativeConstructor.class);
|
||||||
|
if (constr == null) continue;
|
||||||
return func;
|
((OverloadFunction)func).add(Overload.fromMethod(overload, constr.thisArg()));
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Generates a namespace for the given class.
|
if (((OverloadFunction)func).overloads.size() == 0) {
|
||||||
* The returned function will have appropriate wrappers for all static members.
|
func = new NativeFunction(clazz.getName(), (a, b, c) -> { throw EngineException.ofError("This constructor is not invokable."); });
|
||||||
* This method behaves almost like {@link NativeTypeRegister#makeConstructor}, but will return an object instead.
|
}
|
||||||
* @param clazz The class for which a constructor should be generated
|
|
||||||
*/
|
applyMethods(ctx, false, func, clazz);
|
||||||
public static ObjectValue makeNamespace(Class<?> clazz) {
|
applyFields(ctx, false, func, clazz);
|
||||||
ObjectValue res = new ObjectValue();
|
applyClasses(ctx, false, func, clazz);
|
||||||
|
|
||||||
applyMethods(false, res, clazz);
|
func.special = true;
|
||||||
applyFields(false, res, clazz);
|
|
||||||
applyClasses(false, res, clazz);
|
return func;
|
||||||
|
}
|
||||||
return res;
|
/**
|
||||||
}
|
* Generates a namespace for the given class.
|
||||||
|
* The returned function will have appropriate wrappers for all static members.
|
||||||
private void initType(Class<?> clazz, FunctionValue constr, ObjectValue proto) {
|
* This method behaves almost like {@link NativeWrapperProvider#makeConstructor}, but will return an object instead.
|
||||||
if (constr != null && proto != null) return;
|
* @param clazz The class for which a constructor should be generated
|
||||||
// i vomit
|
*/
|
||||||
if (
|
public static ObjectValue makeNamespace(Environment ctx, Class<?> clazz) {
|
||||||
clazz == Object.class ||
|
ObjectValue res = new ObjectValue();
|
||||||
clazz == Void.class ||
|
|
||||||
clazz == Number.class || clazz == Double.class || clazz == Float.class ||
|
applyMethods(ctx, false, res, clazz);
|
||||||
clazz == Long.class || clazz == Integer.class || clazz == Short.class ||
|
applyFields(ctx, false, res, clazz);
|
||||||
clazz == Character.class || clazz == Byte.class || clazz == Boolean.class ||
|
applyClasses(ctx, false, res, clazz);
|
||||||
clazz.isPrimitive() ||
|
|
||||||
clazz.isArray() ||
|
return res;
|
||||||
clazz.isAnonymousClass() ||
|
}
|
||||||
clazz.isEnum() ||
|
|
||||||
clazz.isInterface() ||
|
private void initType(Class<?> clazz, FunctionValue constr, ObjectValue proto) {
|
||||||
clazz.isSynthetic()
|
if (constr != null && proto != null) return;
|
||||||
) return;
|
// i vomit
|
||||||
|
if (
|
||||||
if (constr == null) constr = makeConstructor(clazz);
|
clazz == Object.class ||
|
||||||
if (proto == null) proto = makeProto(clazz);
|
clazz == Void.class ||
|
||||||
|
clazz == Number.class || clazz == Double.class || clazz == Float.class ||
|
||||||
proto.values.put("constructor", constr);
|
clazz == Long.class || clazz == Integer.class || clazz == Short.class ||
|
||||||
constr.values.put("prototype", proto);
|
clazz == Character.class || clazz == Byte.class || clazz == Boolean.class ||
|
||||||
|
clazz.isPrimitive() ||
|
||||||
prototypes.put(clazz, proto);
|
clazz.isArray() ||
|
||||||
constructors.put(clazz, constr);
|
clazz.isAnonymousClass() ||
|
||||||
|
clazz.isEnum() ||
|
||||||
var parent = clazz.getSuperclass();
|
clazz.isInterface() ||
|
||||||
if (parent == null) return;
|
clazz.isSynthetic()
|
||||||
|
) return;
|
||||||
var parentProto = getProto(parent);
|
|
||||||
var parentConstr = getConstr(parent);
|
if (constr == null) constr = makeConstructor(env, clazz);
|
||||||
|
if (proto == null) proto = makeProto(env, clazz);
|
||||||
if (parentProto != null) proto.setPrototype(null, parentProto);
|
|
||||||
if (parentConstr != null) constr.setPrototype(null, parentConstr);
|
proto.values.put("constructor", constr);
|
||||||
}
|
constr.values.put("prototype", proto);
|
||||||
|
|
||||||
public ObjectValue getProto(Class<?> clazz) {
|
prototypes.put(clazz, proto);
|
||||||
initType(clazz, constructors.get(clazz), prototypes.get(clazz));
|
constructors.put(clazz, constr);
|
||||||
return prototypes.get(clazz);
|
|
||||||
}
|
var parent = clazz.getSuperclass();
|
||||||
public FunctionValue getConstr(Class<?> clazz) {
|
if (parent == null) return;
|
||||||
initType(clazz, constructors.get(clazz), prototypes.get(clazz));
|
|
||||||
return constructors.get(clazz);
|
var parentProto = getProto(parent);
|
||||||
}
|
var parentConstr = getConstr(parent);
|
||||||
|
|
||||||
public void setProto(Class<?> clazz, ObjectValue value) {
|
if (parentProto != null) proto.setPrototype(null, parentProto);
|
||||||
prototypes.put(clazz, value);
|
if (parentConstr != null) constr.setPrototype(null, parentConstr);
|
||||||
}
|
}
|
||||||
public void setConstr(Class<?> clazz, FunctionValue value) {
|
|
||||||
constructors.put(clazz, value);
|
public ObjectValue getProto(Class<?> clazz) {
|
||||||
}
|
initType(clazz, constructors.get(clazz), prototypes.get(clazz));
|
||||||
}
|
return prototypes.get(clazz);
|
||||||
|
}
|
||||||
|
public FunctionValue getConstr(Class<?> clazz) {
|
||||||
|
initType(clazz, constructors.get(clazz), prototypes.get(clazz));
|
||||||
|
return constructors.get(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProto(Class<?> clazz, ObjectValue value) {
|
||||||
|
prototypes.put(clazz, value);
|
||||||
|
}
|
||||||
|
public void setConstr(Class<?> clazz, FunctionValue value) {
|
||||||
|
constructors.put(clazz, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NativeWrapperProvider(Environment env) {
|
||||||
|
this.env = env;
|
||||||
|
}
|
||||||
|
}
|
@ -59,7 +59,10 @@ public class OverloadFunction extends FunctionValue {
|
|||||||
Object _this = thisArgType == null ? null : Values.convert(ctx, thisArg, thisArgType);
|
Object _this = thisArgType == null ? null : Values.convert(ctx, thisArg, thisArgType);
|
||||||
|
|
||||||
if (consumesEngine) newArgs[0] = ctx;
|
if (consumesEngine) newArgs[0] = ctx;
|
||||||
if (overload.passThis) newArgs[consumesEngine ? 1 : 0] = _this;
|
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));
|
||||||
|
350
src/me/topchetoeu/jscript/polyfills/ArrayPolyfill.java
Normal file
350
src/me/topchetoeu/jscript/polyfills/ArrayPolyfill.java
Normal file
@ -0,0 +1,350 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
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.NativeConstructor;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -16,7 +16,7 @@ public class FunctionPolyfill {
|
|||||||
|
|
||||||
return func.call(ctx, thisArg, args);
|
return func.call(ctx, thisArg, args);
|
||||||
}
|
}
|
||||||
@Native(thisArg = true) public static Object bind(Context ctx, FunctionValue func, Object thisArg, Object... args) {
|
@Native(thisArg = true) public static FunctionValue bind(Context ctx, FunctionValue func, Object thisArg, Object... args) {
|
||||||
if (!(func instanceof FunctionValue)) throw EngineException.ofError("Expected this to be a function.");
|
if (!(func instanceof FunctionValue)) throw EngineException.ofError("Expected this to be a function.");
|
||||||
|
|
||||||
return new NativeFunction(func.name + " (bound)", (callCtx, _0, callArgs) -> {
|
return new NativeFunction(func.name + " (bound)", (callCtx, _0, callArgs) -> {
|
||||||
|
@ -12,9 +12,12 @@ import me.topchetoeu.jscript.engine.values.Values;
|
|||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
|
||||||
public class Internals {
|
public class Internals {
|
||||||
@Native public final Class<ObjectPolyfill> object = ObjectPolyfill.class;
|
public final Environment targetEnv;
|
||||||
@Native public final Class<FunctionPolyfill> function = FunctionPolyfill.class;
|
|
||||||
@Native public final Class<PromisePolyfill> promise = PromisePolyfill.class;
|
@Native public final FunctionValue object;
|
||||||
|
@Native public final FunctionValue function;
|
||||||
|
@Native public final FunctionValue promise;
|
||||||
|
@Native public final FunctionValue array;
|
||||||
|
|
||||||
@Native public void markSpecial(FunctionValue ...funcs) {
|
@Native public void markSpecial(FunctionValue ...funcs) {
|
||||||
for (var func : funcs) {
|
for (var func : funcs) {
|
||||||
@ -79,7 +82,7 @@ public class Internals {
|
|||||||
return str.value;
|
return str.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public static void log(Context ctx, Object ...args) throws InterruptedException {
|
@Native public void log(Context ctx, Object ...args) throws InterruptedException {
|
||||||
for (var arg : args) {
|
for (var arg : args) {
|
||||||
Values.printValue(ctx, arg);
|
Values.printValue(ctx, arg);
|
||||||
}
|
}
|
||||||
@ -150,4 +153,12 @@ public class Internals {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Internals(Environment targetEnv) {
|
||||||
|
this.targetEnv = targetEnv;
|
||||||
|
this.object = targetEnv.wrappersProvider.getConstr(ObjectPolyfill.class);
|
||||||
|
this.function = targetEnv.wrappersProvider.getConstr(FunctionPolyfill.class);
|
||||||
|
this.promise = targetEnv.wrappersProvider.getConstr(PromisePolyfill.class);
|
||||||
|
this.array = targetEnv.wrappersProvider.getConstr(ArrayPolyfill.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,7 +262,7 @@ public class PromisePolyfill {
|
|||||||
if (handles.size() == 0) {
|
if (handles.size() == 0) {
|
||||||
ctx.message.engine.pushMsg(true, ctx.message, new NativeFunction((_ctx, _thisArg, _args) -> {
|
ctx.message.engine.pushMsg(true, ctx.message, new NativeFunction((_ctx, _thisArg, _args) -> {
|
||||||
if (!handled) {
|
if (!handled) {
|
||||||
try { Values.printError(new EngineException(val), "(in promise)"); }
|
try { Values.printError(new EngineException(val).setContext(ctx), "(in promise)"); }
|
||||||
catch (InterruptedException ex) { }
|
catch (InterruptedException ex) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user