Core library reprogramming #5
@ -11,6 +11,7 @@ interface Internals {
|
|||||||
promise: PromiseConstructor;
|
promise: PromiseConstructor;
|
||||||
bool: BooleanConstructor;
|
bool: BooleanConstructor;
|
||||||
number: NumberConstructor;
|
number: NumberConstructor;
|
||||||
|
string: StringConstructor;
|
||||||
|
|
||||||
markSpecial(...funcs: Function[]): void;
|
markSpecial(...funcs: Function[]): void;
|
||||||
getEnv(func: Function): Environment | undefined;
|
getEnv(func: Function): Environment | undefined;
|
||||||
@ -50,11 +51,13 @@ try {
|
|||||||
var Promise = env.global.Promise = internals.promise;
|
var Promise = env.global.Promise = internals.promise;
|
||||||
var Boolean = env.global.Boolean = internals.bool;
|
var Boolean = env.global.Boolean = internals.bool;
|
||||||
var Number = env.global.Number = internals.number;
|
var Number = env.global.Number = internals.number;
|
||||||
|
var String = env.global.String = internals.string;
|
||||||
|
|
||||||
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);
|
env.setProto('array', Array.prototype);
|
||||||
env.setProto('number', Number.prototype);
|
env.setProto('number', Number.prototype);
|
||||||
|
env.setProto('string', String.prototype);
|
||||||
|
|
||||||
(Object.prototype as any).__proto__ = null;
|
(Object.prototype as any).__proto__ = null;
|
||||||
|
|
||||||
@ -64,7 +67,6 @@ try {
|
|||||||
run('values/symbol');
|
run('values/symbol');
|
||||||
run('values/errors');
|
run('values/errors');
|
||||||
run('values/string');
|
run('values/string');
|
||||||
// run('values/number');
|
|
||||||
run('map');
|
run('map');
|
||||||
run('set');
|
run('set');
|
||||||
run('regex');
|
run('regex');
|
||||||
|
@ -5,8 +5,6 @@
|
|||||||
"utils.ts",
|
"utils.ts",
|
||||||
"values/symbol.ts",
|
"values/symbol.ts",
|
||||||
"values/errors.ts",
|
"values/errors.ts",
|
||||||
"values/string.ts",
|
|
||||||
"values/number.ts",
|
|
||||||
"map.ts",
|
"map.ts",
|
||||||
"set.ts",
|
"set.ts",
|
||||||
"regex.ts",
|
"regex.ts",
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
define("values/number", () => {
|
|
||||||
var Number = env.global.Number = function(this: Number | undefined, arg: any) {
|
|
||||||
var val;
|
|
||||||
if (arguments.length === 0) val = 0;
|
|
||||||
else val = arg - 0;
|
|
||||||
if (this === undefined || this === null) return val;
|
|
||||||
else (this as any).value = val;
|
|
||||||
} as NumberConstructor;
|
|
||||||
|
|
||||||
env.setProto('number', Number.prototype);
|
|
||||||
setConstr(Number.prototype, Number);
|
|
||||||
|
|
||||||
setProps(Number.prototype, {
|
|
||||||
valueOf() {
|
|
||||||
if (typeof this === 'number') return this;
|
|
||||||
else return (this as any).value;
|
|
||||||
},
|
|
||||||
toString() {
|
|
||||||
if (typeof this === 'number') return this + '';
|
|
||||||
else return (this as any).value + '';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setProps(Number, {
|
|
||||||
parseInt(val) { return Math.trunc(val as any - 0); },
|
|
||||||
parseFloat(val) { return val as any - 0; },
|
|
||||||
});
|
|
||||||
|
|
||||||
env.global.parseInt = Number.parseInt;
|
|
||||||
env.global.parseFloat = Number.parseFloat;
|
|
||||||
env.global.Object.defineProperty(env.global, 'NaN', { value: 0 / 0, writable: false });
|
|
||||||
env.global.Object.defineProperty(env.global, 'Infinity', { value: 1 / 0, writable: false });
|
|
||||||
});
|
|
@ -1,267 +0,0 @@
|
|||||||
define("values/string", () => {
|
|
||||||
var String = env.global.String = function(this: String | undefined, arg: any) {
|
|
||||||
var val;
|
|
||||||
if (arguments.length === 0) val = '';
|
|
||||||
else val = arg + '';
|
|
||||||
if (this === undefined || this === null) return val;
|
|
||||||
else (this as any).value = val;
|
|
||||||
} as StringConstructor;
|
|
||||||
|
|
||||||
env.setProto('string', String.prototype);
|
|
||||||
setConstr(String.prototype, String);
|
|
||||||
|
|
||||||
setProps(String.prototype, {
|
|
||||||
toString() {
|
|
||||||
if (typeof this === 'string') return this;
|
|
||||||
else return (this as any).value;
|
|
||||||
},
|
|
||||||
valueOf() {
|
|
||||||
if (typeof this === 'string') return this;
|
|
||||||
else return (this as any).value;
|
|
||||||
},
|
|
||||||
|
|
||||||
substring(start, end) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.substring(start, end);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
start = start ?? 0 | 0;
|
|
||||||
end = (end ?? this.length) | 0;
|
|
||||||
|
|
||||||
const res = [];
|
|
||||||
|
|
||||||
for (let i = start; i < end; i++) {
|
|
||||||
if (i >= 0 && i < this.length) res[res.length] = this[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return internals.stringFromStrings(res);
|
|
||||||
},
|
|
||||||
substr(start, length) {
|
|
||||||
start = start ?? 0 | 0;
|
|
||||||
|
|
||||||
if (start >= this.length) start = this.length - 1;
|
|
||||||
if (start < 0) start = 0;
|
|
||||||
|
|
||||||
length = (length ?? this.length - start) | 0;
|
|
||||||
const end = length + start;
|
|
||||||
const res = [];
|
|
||||||
|
|
||||||
for (let i = start; i < end; i++) {
|
|
||||||
if (i >= 0 && i < this.length) res[res.length] = this[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return internals.stringFromStrings(res);
|
|
||||||
},
|
|
||||||
|
|
||||||
toLowerCase() {
|
|
||||||
// TODO: Implement localization
|
|
||||||
const res = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < this.length; i++) {
|
|
||||||
const c = internals.char(this[i]);
|
|
||||||
|
|
||||||
if (c >= 65 && c <= 90) res[i] = c - 65 + 97;
|
|
||||||
else res[i] = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
return internals.stringFromChars(res);
|
|
||||||
},
|
|
||||||
toUpperCase() {
|
|
||||||
// TODO: Implement localization
|
|
||||||
const res = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < this.length; i++) {
|
|
||||||
const c = internals.char(this[i]);
|
|
||||||
|
|
||||||
if (c >= 97 && c <= 122) res[i] = c - 97 + 65;
|
|
||||||
else res[i] = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
return internals.stringFromChars(res);
|
|
||||||
},
|
|
||||||
|
|
||||||
charAt(pos) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.charAt(pos);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = pos | 0;
|
|
||||||
if (pos < 0 || pos >= this.length) return '';
|
|
||||||
return this[pos];
|
|
||||||
},
|
|
||||||
charCodeAt(pos) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.charAt(pos);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = pos | 0;
|
|
||||||
if (pos < 0 || pos >= this.length) return 0 / 0;
|
|
||||||
return internals.char(this[pos]);
|
|
||||||
},
|
|
||||||
|
|
||||||
startsWith(term, pos) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.startsWith(term, pos);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
pos = pos! | 0;
|
|
||||||
term = term + "";
|
|
||||||
|
|
||||||
if (pos < 0 || this.length < term.length + pos) return false;
|
|
||||||
|
|
||||||
for (let i = 0; i < term.length; i++) {
|
|
||||||
if (this[i + pos] !== term[i]) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
endsWith(term, pos) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.endsWith(term, pos);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
pos = (pos ?? this.length) | 0;
|
|
||||||
term = term + "";
|
|
||||||
|
|
||||||
const start = pos - term.length;
|
|
||||||
|
|
||||||
if (start < 0 || this.length < term.length + start) return false;
|
|
||||||
|
|
||||||
for (let i = 0; i < term.length; i++) {
|
|
||||||
if (this[i + start] !== term[i]) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
indexOf(term: any, start) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.indexOf(term, start);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof term[env.global.Symbol.search] !== 'function') term = RegExp.escape(term);
|
|
||||||
|
|
||||||
return term[env.global.Symbol.search](this, false, start);
|
|
||||||
},
|
|
||||||
lastIndexOf(term: any, start) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.indexOf(term, start);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof term[env.global.Symbol.search] !== 'function') term = RegExp.escape(term);
|
|
||||||
|
|
||||||
return term[env.global.Symbol.search](this, true, start);
|
|
||||||
},
|
|
||||||
includes(term, start) {
|
|
||||||
return this.indexOf(term, start) >= 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
replace(pattern: any, val) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.replace(pattern, val);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof pattern[env.global.Symbol.replace] !== 'function') pattern = RegExp.escape(pattern);
|
|
||||||
|
|
||||||
return pattern[env.global.Symbol.replace](this, val);
|
|
||||||
},
|
|
||||||
replaceAll(pattern: any, val) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.replace(pattern, val);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof pattern[env.global.Symbol.replace] !== 'function') pattern = RegExp.escape(pattern, "g");
|
|
||||||
if (pattern instanceof RegExp && !pattern.global) pattern = new pattern.constructor(pattern.source, pattern.flags + "g");
|
|
||||||
|
|
||||||
return pattern[env.global.Symbol.replace](this, val);
|
|
||||||
},
|
|
||||||
|
|
||||||
match(pattern: any) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.match(pattern);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof pattern[env.global.Symbol.match] !== 'function') pattern = RegExp.escape(pattern);
|
|
||||||
|
|
||||||
return pattern[env.global.Symbol.match](this);
|
|
||||||
},
|
|
||||||
matchAll(pattern: any) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.matchAll(pattern);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof pattern[env.global.Symbol.match] !== 'function') pattern = RegExp.escape(pattern, "g");
|
|
||||||
if (pattern instanceof RegExp && !pattern.global) pattern = new pattern.constructor(pattern.source, pattern.flags + "g");
|
|
||||||
|
|
||||||
return pattern[env.global.Symbol.match](this);
|
|
||||||
},
|
|
||||||
|
|
||||||
split(pattern: any, lim, sensible) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.split(pattern, lim, sensible);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof pattern[env.global.Symbol.split] !== 'function') pattern = RegExp.escape(pattern, "g");
|
|
||||||
|
|
||||||
return pattern[env.global.Symbol.split](this, lim, sensible);
|
|
||||||
},
|
|
||||||
slice(start, end) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.slice(start, end);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
start = wrapI(this.length, start ?? 0 | 0);
|
|
||||||
end = wrapI(this.length, end ?? this.length | 0);
|
|
||||||
|
|
||||||
if (start > end) return '';
|
|
||||||
|
|
||||||
return this.substring(start, end);
|
|
||||||
},
|
|
||||||
|
|
||||||
concat(...args) {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.concat(...args);
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
var res = this;
|
|
||||||
for (var arg of args) res += arg;
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
|
|
||||||
trim() {
|
|
||||||
return this
|
|
||||||
.replace(/^\s+/g, '')
|
|
||||||
.replace(/\s+$/g, '');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setProps(String, {
|
|
||||||
fromCharCode(val) {
|
|
||||||
return internals.stringFromChars([val | 0]);
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
env.global.Object.defineProperty(String.prototype, 'length', {
|
|
||||||
get() {
|
|
||||||
if (typeof this !== 'string') {
|
|
||||||
if (this instanceof String) return (this as any).value.length;
|
|
||||||
else throw new Error('This function may be used only with primitive or object strings.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return internals.strlen(this);
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: false,
|
|
||||||
});
|
|
||||||
});
|
|
@ -14,7 +14,7 @@ import me.topchetoeu.jscript.interop.Native;
|
|||||||
public class Internals {
|
public class Internals {
|
||||||
public final Environment targetEnv;
|
public final Environment targetEnv;
|
||||||
|
|
||||||
@Native public final FunctionValue object, function, promise, array, bool, number;
|
@Native public final FunctionValue object, function, promise, array, bool, number, string;
|
||||||
|
|
||||||
@Native public void markSpecial(FunctionValue ...funcs) {
|
@Native public void markSpecial(FunctionValue ...funcs) {
|
||||||
for (var func : funcs) {
|
for (var func : funcs) {
|
||||||
@ -159,5 +159,6 @@ public class Internals {
|
|||||||
this.array = targetEnv.wrappersProvider.getConstr(ArrayPolyfill.class);
|
this.array = targetEnv.wrappersProvider.getConstr(ArrayPolyfill.class);
|
||||||
this.bool = targetEnv.wrappersProvider.getConstr(BooleanPolyfill.class);
|
this.bool = targetEnv.wrappersProvider.getConstr(BooleanPolyfill.class);
|
||||||
this.number = targetEnv.wrappersProvider.getConstr(NumberPolyfill.class);
|
this.number = targetEnv.wrappersProvider.getConstr(NumberPolyfill.class);
|
||||||
|
this.string = targetEnv.wrappersProvider.getConstr(StringPolyfill.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
254
src/me/topchetoeu/jscript/polyfills/StringPolyfill.java
Normal file
254
src/me/topchetoeu/jscript/polyfills/StringPolyfill.java
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
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.exceptions.EngineException;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
|
|
||||||
|
// TODO: implement index wrapping properly
|
||||||
|
public class StringPolyfill {
|
||||||
|
public final String value;
|
||||||
|
|
||||||
|
private static String passThis(String funcName, Object val) {
|
||||||
|
if (val instanceof StringPolyfill) return ((StringPolyfill)val).value;
|
||||||
|
else if (val instanceof String) return (String)val;
|
||||||
|
else throw EngineException.ofType(String.format("'%s' may be called upon object and primitve strings.", funcName));
|
||||||
|
}
|
||||||
|
private static int normalizeI(int i, int len, boolean clamp) {
|
||||||
|
if (i < 0) i += len;
|
||||||
|
if (clamp) {
|
||||||
|
if (i < 0) i = 0;
|
||||||
|
if (i >= len) i = len;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeGetter(thisArg = true) public static int length(Context ctx, Object thisArg) {
|
||||||
|
return passThis("substring", thisArg).length();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static String substring(Context ctx, Object thisArg, int start, Object _end) throws InterruptedException {
|
||||||
|
var val = passThis("substring", thisArg);
|
||||||
|
start = normalizeI(start, val.length(), true);
|
||||||
|
int end = normalizeI(_end == null ? val.length() : (int)Values.toNumber(ctx, _end), val.length(), true);
|
||||||
|
|
||||||
|
return val.substring(start, end);
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static String substr(Context ctx, Object thisArg, int start, Object _len) throws InterruptedException {
|
||||||
|
var val = passThis("substr", thisArg);
|
||||||
|
int len = _len == null ? val.length() - start : (int)Values.toNumber(ctx, _len);
|
||||||
|
return substring(ctx, val, start, start + len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static String toLowerCase(Context ctx, Object thisArg) {
|
||||||
|
return passThis("toLowerCase", thisArg).toLowerCase();
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static String toUpperCase(Context ctx, Object thisArg) {
|
||||||
|
return passThis("toUpperCase", thisArg).toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static String charAt(Context ctx, Object thisArg, int i) {
|
||||||
|
return passThis("charAt", thisArg).charAt(i) + "";
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static int charCodeAt(Context ctx, Object thisArg, int i) {
|
||||||
|
return passThis("charCodeAt", thisArg).charAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static boolean startsWith(Context ctx, Object thisArg, String term, int pos) {
|
||||||
|
return passThis("startsWith", thisArg).startsWith(term, pos);
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static boolean endsWith(Context ctx, Object thisArg, String term, int pos) throws InterruptedException {
|
||||||
|
var val = passThis("endsWith", thisArg);
|
||||||
|
return val.lastIndexOf(term, pos) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static int indexOf(Context ctx, Object thisArg, Object term, int start) throws InterruptedException {
|
||||||
|
var val = passThis("indexOf", thisArg);
|
||||||
|
|
||||||
|
if (term != null && term != Values.NULL && !(term instanceof String)) {
|
||||||
|
var search = Values.getMember(ctx, term, ctx.env.symbol("Symbol.search"));
|
||||||
|
if (search instanceof FunctionValue) {
|
||||||
|
return (int)Values.toNumber(ctx, ((FunctionValue)search).call(ctx, term, val, false, start));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return val.indexOf(Values.toString(ctx, term), start);
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static int lastIndexOf(Context ctx, Object thisArg, Object term, int pos) throws InterruptedException {
|
||||||
|
var val = passThis("lastIndexOf", thisArg);
|
||||||
|
|
||||||
|
if (term != null && term != Values.NULL && !(term instanceof String)) {
|
||||||
|
var search = Values.getMember(ctx, term, ctx.env.symbol("Symbol.search"));
|
||||||
|
if (search instanceof FunctionValue) {
|
||||||
|
return (int)Values.toNumber(ctx, ((FunctionValue)search).call(ctx, term, val, true, pos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return val.lastIndexOf(Values.toString(ctx, term), pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static boolean includes(Context ctx, Object thisArg, Object term, int pos) throws InterruptedException {
|
||||||
|
return lastIndexOf(ctx, passThis("includes", thisArg), term, pos) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static String replace(Context ctx, Object thisArg, Object term, String replacement) throws InterruptedException {
|
||||||
|
var val = passThis("replace", thisArg);
|
||||||
|
|
||||||
|
if (term != null && term != Values.NULL && !(term instanceof String)) {
|
||||||
|
var replace = Values.getMember(ctx, term, ctx.env.symbol("Symbol.replace"));
|
||||||
|
if (replace instanceof FunctionValue) {
|
||||||
|
return Values.toString(ctx, ((FunctionValue)replace).call(ctx, term, val, replacement));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return val.replaceFirst(Pattern.quote(Values.toString(ctx, term)), replacement);
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static String replaceAll(Context ctx, Object thisArg, Object term, String replacement) throws InterruptedException {
|
||||||
|
var val = passThis("replaceAll", thisArg);
|
||||||
|
|
||||||
|
if (term != null && term != Values.NULL && !(term instanceof String)) {
|
||||||
|
var replace = Values.getMember(ctx, term, ctx.env.symbol("Symbol.replace"));
|
||||||
|
if (replace instanceof FunctionValue) {
|
||||||
|
return Values.toString(ctx, ((FunctionValue)replace).call(ctx, term, val, replacement));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return val.replaceFirst(Pattern.quote(Values.toString(ctx, term)), replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static ArrayValue match(Context ctx, Object thisArg, Object term, String replacement) throws InterruptedException {
|
||||||
|
var val = passThis("match", thisArg);
|
||||||
|
|
||||||
|
FunctionValue match;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var _match = Values.getMember(ctx, term, ctx.env.symbol("Symbol.match"));
|
||||||
|
if (_match instanceof FunctionValue) match = (FunctionValue)_match;
|
||||||
|
else if (ctx.env.regexConstructor != null) {
|
||||||
|
var regex = Values.callNew(ctx, ctx.env.regexConstructor, Values.toString(ctx, term), "");
|
||||||
|
_match = Values.getMember(ctx, regex, ctx.env.symbol("Symbol.match"));
|
||||||
|
if (_match instanceof FunctionValue) match = (FunctionValue)_match;
|
||||||
|
else throw EngineException.ofError("Regular expressions don't support matching.");
|
||||||
|
}
|
||||||
|
else throw EngineException.ofError("Regular expressions not supported.");
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) { return new ArrayValue(ctx, ""); }
|
||||||
|
|
||||||
|
var res = match.call(ctx, term, val);
|
||||||
|
if (res instanceof ArrayValue) return (ArrayValue)res;
|
||||||
|
else return new ArrayValue(ctx, "");
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static Object matchAll(Context ctx, Object thisArg, Object term, String replacement) throws InterruptedException {
|
||||||
|
var val = passThis("matchAll", thisArg);
|
||||||
|
|
||||||
|
FunctionValue match = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var _match = Values.getMember(ctx, term, ctx.env.symbol("Symbol.matchAll"));
|
||||||
|
if (_match instanceof FunctionValue) match = (FunctionValue)_match;
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) { }
|
||||||
|
|
||||||
|
if (match == null && ctx.env.regexConstructor != null) {
|
||||||
|
var regex = Values.callNew(ctx, ctx.env.regexConstructor, Values.toString(ctx, term), "g");
|
||||||
|
var _match = Values.getMember(ctx, regex, ctx.env.symbol("Symbol.matchAll"));
|
||||||
|
if (_match instanceof FunctionValue) match = (FunctionValue)_match;
|
||||||
|
else throw EngineException.ofError("Regular expressions don't support matching.");
|
||||||
|
}
|
||||||
|
else throw EngineException.ofError("Regular expressions not supported.");
|
||||||
|
|
||||||
|
return match.call(ctx, term, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static ArrayValue split(Context ctx, Object thisArg, Object term, Object lim, boolean sensible) throws InterruptedException {
|
||||||
|
var val = passThis("split", thisArg);
|
||||||
|
|
||||||
|
if (lim != null) lim = Values.toNumber(ctx, lim);
|
||||||
|
|
||||||
|
if (term != null && term != Values.NULL && !(term instanceof String)) {
|
||||||
|
var replace = Values.getMember(ctx, term, ctx.env.symbol("Symbol.replace"));
|
||||||
|
if (replace instanceof FunctionValue) {
|
||||||
|
var tmp = ((FunctionValue)replace).call(ctx, term, val, lim, sensible);
|
||||||
|
|
||||||
|
if (tmp instanceof ArrayValue) {
|
||||||
|
var parts = new ArrayValue(((ArrayValue)tmp).size());
|
||||||
|
for (int i = 0; i < parts.size(); i++) parts.set(ctx, i, Values.toString(ctx, ((ArrayValue)tmp).get(i)));
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] parts;
|
||||||
|
var pattern = Pattern.quote(Values.toString(ctx, term));
|
||||||
|
|
||||||
|
if (lim == null) parts = val.split(pattern);
|
||||||
|
else if (sensible) parts = val.split(pattern, (int)(double)lim);
|
||||||
|
else {
|
||||||
|
var limit = (int)(double)lim;
|
||||||
|
parts = val.split(pattern, limit + 1);
|
||||||
|
ArrayValue res;
|
||||||
|
|
||||||
|
if (parts.length > limit) res = new ArrayValue(limit);
|
||||||
|
else res = new ArrayValue(parts.length);
|
||||||
|
|
||||||
|
for (var i = 0; i < parts.length; i++) res.set(ctx, i, parts[i]);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
var res = new ArrayValue(parts.length);
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
for (; i < parts.length; i++) {
|
||||||
|
if (lim != null && (double)lim <= i) break;
|
||||||
|
res.set(ctx, i, parts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static String slice(Context ctx, Object thisArg, int start, Object _end) throws InterruptedException {
|
||||||
|
return substring(ctx, passThis("slice", thisArg), start, _end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native(thisArg = true) public static String concat(Context ctx, Object thisArg, Object... args) throws InterruptedException {
|
||||||
|
var res = new StringBuilder(passThis("concat", thisArg));
|
||||||
|
|
||||||
|
for (var el : args) res.append(Values.toString(ctx, el));
|
||||||
|
|
||||||
|
return res.toString();
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static String trim(Context ctx, Object thisArg) throws InterruptedException {
|
||||||
|
return passThis("trim", thisArg).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object val) throws InterruptedException {
|
||||||
|
val = Values.toString(ctx, val);
|
||||||
|
if (thisArg instanceof ObjectValue) return new StringPolyfill((String)val);
|
||||||
|
else return val;
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static String toString(Context ctx, Object thisArg) throws InterruptedException {
|
||||||
|
return Values.toString(ctx, Values.toNumber(ctx, thisArg));
|
||||||
|
}
|
||||||
|
@Native(thisArg = true) public static String valueOf(Context ctx, Object thisArg) throws InterruptedException {
|
||||||
|
if (thisArg instanceof StringPolyfill) return ((StringPolyfill)thisArg).value;
|
||||||
|
else return Values.toString(ctx, thisArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public static String fromCharCode(int ...val) {
|
||||||
|
char[] arr = new char[val.length];
|
||||||
|
for (var i = 0; i < val.length; i++) arr[i] = (char)val[i];
|
||||||
|
return new String(arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringPolyfill(String val) {
|
||||||
|
this.value = val;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user