a lot of fixes
This commit is contained in:
parent
c1b84689c4
commit
0dacaaeb4c
76
lib/core.ts
76
lib/core.ts
@ -17,7 +17,8 @@ interface Internals {
|
|||||||
syntax: SyntaxErrorConstructor;
|
syntax: SyntaxErrorConstructor;
|
||||||
type: TypeErrorConstructor;
|
type: TypeErrorConstructor;
|
||||||
range: RangeErrorConstructor;
|
range: RangeErrorConstructor;
|
||||||
|
|
||||||
|
regexp: typeof RegExp;
|
||||||
map: typeof Map;
|
map: typeof Map;
|
||||||
set: typeof Set;
|
set: typeof Set;
|
||||||
|
|
||||||
@ -50,44 +51,51 @@ interface Internals {
|
|||||||
log(...args: any[]): void;
|
log(...args: any[]): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var env: Environment = arguments[0], internals: Internals = arguments[1];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var env: Environment = arguments[0], internals: Internals = arguments[1];
|
|
||||||
|
|
||||||
const Object = env.global.Object = internals.object;
|
const values = {
|
||||||
const Function = env.global.Function = internals.function;
|
Object: env.global.Object = internals.object,
|
||||||
const Array = env.global.Array = internals.array;
|
Function: env.global.Function = internals.function,
|
||||||
const Promise = env.global.Promise = internals.promise;
|
Array: env.global.Array = internals.array,
|
||||||
const Boolean = env.global.Boolean = internals.bool;
|
Promise: env.global.Promise = internals.promise,
|
||||||
const Number = env.global.Number = internals.number;
|
Boolean: env.global.Boolean = internals.bool,
|
||||||
const String = env.global.String = internals.string;
|
Number: env.global.Number = internals.number,
|
||||||
const Symbol = env.global.Symbol = internals.symbol;
|
String: env.global.String = internals.string,
|
||||||
const Error = env.global.Error = internals.error;
|
Symbol: env.global.Symbol = internals.symbol,
|
||||||
const SyntaxError = env.global.SyntaxError = internals.syntax;
|
Error: env.global.Error = internals.error,
|
||||||
const TypeError = env.global.TypeError = internals.type;
|
SyntaxError: env.global.SyntaxError = internals.syntax,
|
||||||
const RangeError = env.global.RangeError = internals.range;
|
TypeError: env.global.TypeError = internals.type,
|
||||||
|
RangeError: env.global.RangeError = internals.range,
|
||||||
|
RegExp: env.global.RegExp = internals.regexp,
|
||||||
|
Map: env.global.Map = internals.map,
|
||||||
|
Set: env.global.Set = internals.set,
|
||||||
|
}
|
||||||
|
const Array = values.Array;
|
||||||
|
|
||||||
const Map = env.global.Map = internals.map;
|
env.setProto('object', env.global.Object.prototype);
|
||||||
const Set = env.global.Set = internals.set;
|
env.setProto('function', env.global.Function.prototype);
|
||||||
|
env.setProto('array', env.global.Array.prototype);
|
||||||
|
env.setProto('number', env.global.Number.prototype);
|
||||||
|
env.setProto('string', env.global.String.prototype);
|
||||||
|
env.setProto('symbol', env.global.Symbol.prototype);
|
||||||
|
env.setProto('bool', env.global.Boolean.prototype);
|
||||||
|
|
||||||
env.setProto('object', Object.prototype);
|
env.setProto('error', env.global.Error.prototype);
|
||||||
env.setProto('function', Function.prototype);
|
env.setProto('rangeErr', env.global.RangeError.prototype);
|
||||||
env.setProto('array', Array.prototype);
|
env.setProto('typeErr', env.global.TypeError.prototype);
|
||||||
env.setProto('number', Number.prototype);
|
env.setProto('syntaxErr', env.global.SyntaxError.prototype);
|
||||||
env.setProto('string', String.prototype);
|
(env.global.Object.prototype as any).__proto__ = null;
|
||||||
env.setProto('symbol', Symbol.prototype);
|
|
||||||
env.setProto('bool', Boolean.prototype);
|
|
||||||
|
|
||||||
env.setProto('error', Error.prototype);
|
|
||||||
env.setProto('rangeErr', RangeError.prototype);
|
|
||||||
env.setProto('typeErr', TypeError.prototype);
|
|
||||||
env.setProto('syntaxErr', SyntaxError.prototype);
|
|
||||||
|
|
||||||
(Object.prototype as any).__proto__ = null;
|
|
||||||
|
|
||||||
internals.getEnv(run)?.setProto('array', Array.prototype);
|
internals.getEnv(run)?.setProto('array', Array.prototype);
|
||||||
globalThis.log = (...args) => internals.apply(internals.log, internals, args);
|
globalThis.log = (...args) => internals.apply(internals.log, internals, args);
|
||||||
|
|
||||||
run('regex');
|
for (const key in values) {
|
||||||
|
(values as any)[key].prototype[env.symbol('Symbol.typeName')] = key;
|
||||||
|
log();
|
||||||
|
}
|
||||||
|
|
||||||
run('timeout');
|
run('timeout');
|
||||||
|
|
||||||
env.global.log = log;
|
env.global.log = log;
|
||||||
@ -103,7 +111,7 @@ catch (e: any) {
|
|||||||
if ('name' in e) err += e.name + ": " + e.message;
|
if ('name' in e) err += e.name + ": " + e.message;
|
||||||
else err += 'Error: ' + e.message;
|
else err += 'Error: ' + e.message;
|
||||||
}
|
}
|
||||||
else err += e;
|
else err += "[unknown]";
|
||||||
|
|
||||||
log(e);
|
internals.log(err);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"lib.d.ts",
|
"lib.d.ts",
|
||||||
"modules.ts",
|
"modules.ts",
|
||||||
"utils.ts",
|
|
||||||
"regex.ts",
|
"regex.ts",
|
||||||
"timeout.ts",
|
"timeout.ts",
|
||||||
"core.ts"
|
"core.ts"
|
||||||
|
27
lib/utils.ts
27
lib/utils.ts
@ -1,27 +0,0 @@
|
|||||||
function setProps<
|
|
||||||
TargetT extends object,
|
|
||||||
DescT extends {
|
|
||||||
[x in Exclude<keyof TargetT, 'constructor'> ]?: TargetT[x] extends ((...args: infer ArgsT) => infer RetT) ?
|
|
||||||
((this: TargetT, ...args: ArgsT) => RetT) :
|
|
||||||
TargetT[x]
|
|
||||||
}
|
|
||||||
>(target: TargetT, desc: DescT) {
|
|
||||||
var props = internals.keys(desc, false);
|
|
||||||
for (var i = 0; i in props; i++) {
|
|
||||||
var key = props[i];
|
|
||||||
internals.defineField(
|
|
||||||
target, key, (desc as any)[key],
|
|
||||||
true, // writable
|
|
||||||
false, // enumerable
|
|
||||||
true // configurable
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function setConstr(target: object, constr: Function) {
|
|
||||||
internals.defineField(
|
|
||||||
target, 'constructor', constr,
|
|
||||||
true, // writable
|
|
||||||
false, // enumerable
|
|
||||||
true // configurable
|
|
||||||
);
|
|
||||||
}
|
|
@ -22,7 +22,7 @@ public class Environment {
|
|||||||
|
|
||||||
@Native public FunctionValue compile;
|
@Native public FunctionValue compile;
|
||||||
@Native public FunctionValue regexConstructor = new NativeFunction("RegExp", (ctx, thisArg, args) -> {
|
@Native public FunctionValue regexConstructor = new NativeFunction("RegExp", (ctx, thisArg, args) -> {
|
||||||
throw EngineException.ofError("Regular expressions not supported.");
|
throw EngineException.ofError("Regular expressions not supported.").setContext(ctx);
|
||||||
});
|
});
|
||||||
@Native public ObjectValue proto(String name) {
|
@Native public ObjectValue proto(String name) {
|
||||||
return prototypes.get(name);
|
return prototypes.get(name);
|
||||||
|
@ -24,7 +24,7 @@ public class CodeFrame {
|
|||||||
public final int tryStart, catchStart, finallyStart, end;
|
public final int tryStart, catchStart, finallyStart, end;
|
||||||
public int state;
|
public int state;
|
||||||
public Object retVal;
|
public Object retVal;
|
||||||
public Object err;
|
public EngineException err;
|
||||||
public int jumpPtr;
|
public int jumpPtr;
|
||||||
|
|
||||||
public TryCtx(int tryStart, int tryN, int catchN, int finallyN) {
|
public TryCtx(int tryStart, int tryN, int catchN, int finallyN) {
|
||||||
@ -93,10 +93,11 @@ public class CodeFrame {
|
|||||||
stack[stackPtr++] = Values.normalize(ctx, val);
|
stack[stackPtr++] = Values.normalize(ctx, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setCause(Context ctx, Object err, Object cause) throws InterruptedException {
|
private void setCause(Context ctx, EngineException err, EngineException cause) throws InterruptedException {
|
||||||
if (err instanceof ObjectValue) {
|
if (err.value instanceof ObjectValue) {
|
||||||
Values.setMember(ctx, err, ctx.env.symbol("Symbol.cause"), cause);
|
Values.setMember(ctx, err, ctx.env.symbol("Symbol.cause"), cause);
|
||||||
}
|
}
|
||||||
|
err.cause = cause;
|
||||||
}
|
}
|
||||||
private Object nextNoTry(Context ctx) throws InterruptedException {
|
private Object nextNoTry(Context ctx) throws InterruptedException {
|
||||||
if (Thread.currentThread().isInterrupted()) throw new InterruptedException();
|
if (Thread.currentThread().isInterrupted()) throw new InterruptedException();
|
||||||
@ -116,12 +117,12 @@ public class CodeFrame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object next(Context ctx, Object value, Object returnValue, Object error) throws InterruptedException {
|
public Object next(Context ctx, Object value, Object returnValue, EngineException error) throws InterruptedException {
|
||||||
if (value != Runners.NO_RETURN) push(ctx, value);
|
if (value != Runners.NO_RETURN) push(ctx, value);
|
||||||
|
|
||||||
if (returnValue == Runners.NO_RETURN && error == Runners.NO_RETURN) {
|
if (returnValue == Runners.NO_RETURN && error == null) {
|
||||||
try { returnValue = nextNoTry(ctx); }
|
try { returnValue = nextNoTry(ctx); }
|
||||||
catch (EngineException e) { error = e.value; }
|
catch (EngineException e) { error = e; }
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!tryStack.empty()) {
|
while (!tryStack.empty()) {
|
||||||
@ -130,7 +131,7 @@ public class CodeFrame {
|
|||||||
|
|
||||||
switch (tryCtx.state) {
|
switch (tryCtx.state) {
|
||||||
case TryCtx.STATE_TRY:
|
case TryCtx.STATE_TRY:
|
||||||
if (error != Runners.NO_RETURN) {
|
if (error != null) {
|
||||||
if (tryCtx.hasCatch) {
|
if (tryCtx.hasCatch) {
|
||||||
tryCtx.err = error;
|
tryCtx.err = error;
|
||||||
newState = TryCtx.STATE_CATCH;
|
newState = TryCtx.STATE_CATCH;
|
||||||
@ -158,7 +159,7 @@ public class CodeFrame {
|
|||||||
else codePtr = tryCtx.end;
|
else codePtr = tryCtx.end;
|
||||||
break;
|
break;
|
||||||
case TryCtx.STATE_CATCH:
|
case TryCtx.STATE_CATCH:
|
||||||
if (error != Runners.NO_RETURN) {
|
if (error != null) {
|
||||||
if (tryCtx.hasFinally) {
|
if (tryCtx.hasFinally) {
|
||||||
tryCtx.err = error;
|
tryCtx.err = error;
|
||||||
newState = TryCtx.STATE_FINALLY_THREW;
|
newState = TryCtx.STATE_FINALLY_THREW;
|
||||||
@ -167,7 +168,7 @@ public class CodeFrame {
|
|||||||
}
|
}
|
||||||
else if (returnValue != Runners.NO_RETURN) {
|
else if (returnValue != Runners.NO_RETURN) {
|
||||||
if (tryCtx.hasFinally) {
|
if (tryCtx.hasFinally) {
|
||||||
tryCtx.retVal = error;
|
tryCtx.retVal = returnValue;
|
||||||
newState = TryCtx.STATE_FINALLY_RETURNED;
|
newState = TryCtx.STATE_FINALLY_RETURNED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -182,7 +183,7 @@ public class CodeFrame {
|
|||||||
else codePtr = tryCtx.end;
|
else codePtr = tryCtx.end;
|
||||||
break;
|
break;
|
||||||
case TryCtx.STATE_FINALLY_THREW:
|
case TryCtx.STATE_FINALLY_THREW:
|
||||||
if (error != Runners.NO_RETURN) setCause(ctx, error, tryCtx.err);
|
if (error != null) setCause(ctx, error, tryCtx.err);
|
||||||
else if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) error = tryCtx.err;
|
else if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) error = tryCtx.err;
|
||||||
else return Runners.NO_RETURN;
|
else return Runners.NO_RETURN;
|
||||||
break;
|
break;
|
||||||
@ -211,7 +212,7 @@ public class CodeFrame {
|
|||||||
tryCtx.state = newState;
|
tryCtx.state = newState;
|
||||||
switch (newState) {
|
switch (newState) {
|
||||||
case TryCtx.STATE_CATCH:
|
case TryCtx.STATE_CATCH:
|
||||||
scope.catchVars.add(new ValueVariable(false, tryCtx.err));
|
scope.catchVars.add(new ValueVariable(false, tryCtx.err.value));
|
||||||
codePtr = tryCtx.catchStart;
|
codePtr = tryCtx.catchStart;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -221,7 +222,7 @@ public class CodeFrame {
|
|||||||
return Runners.NO_RETURN;
|
return Runners.NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error != Runners.NO_RETURN) throw new EngineException(error);
|
if (error != null) throw error.setContext(ctx);
|
||||||
if (returnValue != Runners.NO_RETURN) return returnValue;
|
if (returnValue != Runners.NO_RETURN) return returnValue;
|
||||||
return Runners.NO_RETURN;
|
return Runners.NO_RETURN;
|
||||||
}
|
}
|
||||||
@ -230,7 +231,7 @@ public class CodeFrame {
|
|||||||
try {
|
try {
|
||||||
ctx.message.pushFrame(ctx, this);
|
ctx.message.pushFrame(ctx, this);
|
||||||
while (true) {
|
while (true) {
|
||||||
var res = next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, Runners.NO_RETURN);
|
var res = next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, null);
|
||||||
if (res != Runners.NO_RETURN) return res;
|
if (res != Runners.NO_RETURN) return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -685,7 +685,7 @@ public class Values {
|
|||||||
printValue(ctx, val, new HashSet<>(), 0);
|
printValue(ctx, val, new HashSet<>(), 0);
|
||||||
}
|
}
|
||||||
public static void printError(RuntimeException err, String prefix) throws InterruptedException {
|
public static void printError(RuntimeException err, String prefix) throws InterruptedException {
|
||||||
prefix = prefix == null ? "Uncauthg" : "Uncaught " + prefix;
|
prefix = prefix == null ? "Uncaught" : "Uncaught " + prefix;
|
||||||
try {
|
try {
|
||||||
if (err instanceof EngineException) {
|
if (err instanceof EngineException) {
|
||||||
System.out.println(prefix + " " + ((EngineException)err).toString(((EngineException)err).ctx));
|
System.out.println(prefix + " " + ((EngineException)err).toString(((EngineException)err).ctx));
|
||||||
|
@ -685,7 +685,7 @@ public class Parsing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
public static ParseRes<NewStatement> parseRegex(String filename, List<Token> tokens, int i) {
|
public static ParseRes<RegexStatement> parseRegex(String filename, List<Token> tokens, int i) {
|
||||||
var loc = getLoc(filename, tokens, i);
|
var loc = getLoc(filename, tokens, i);
|
||||||
try {
|
try {
|
||||||
if (tokens.get(i).isRegex()) {
|
if (tokens.get(i).isRegex()) {
|
||||||
@ -693,11 +693,7 @@ public class Parsing {
|
|||||||
var index = val.lastIndexOf('/');
|
var index = val.lastIndexOf('/');
|
||||||
var first = val.substring(1, index);
|
var first = val.substring(1, index);
|
||||||
var second = val.substring(index + 1);
|
var second = val.substring(index + 1);
|
||||||
return ParseRes.res(new NewStatement(loc,
|
return ParseRes.res(new RegexStatement(loc, first, second), 1);
|
||||||
new VariableStatement(null, "RegExp"),
|
|
||||||
new ConstantStatement(loc, first),
|
|
||||||
new ConstantStatement(loc, second)
|
|
||||||
), 1);
|
|
||||||
}
|
}
|
||||||
else return ParseRes.failed();
|
else return ParseRes.failed();
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ import me.topchetoeu.jscript.interop.NativeGetter;
|
|||||||
import me.topchetoeu.jscript.interop.NativeSetter;
|
import me.topchetoeu.jscript.interop.NativeSetter;
|
||||||
|
|
||||||
public class ArrayPolyfill {
|
public class ArrayPolyfill {
|
||||||
@Native("@@Symbol.typeName") public final String name = "AsyncFunction";
|
|
||||||
@NativeGetter(thisArg = true) public static int length(Context ctx, ArrayValue thisArg) throws InterruptedException {
|
@NativeGetter(thisArg = true) public static int length(Context ctx, ArrayValue thisArg) throws InterruptedException {
|
||||||
return thisArg.size();
|
return thisArg.size();
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ public class AsyncFunctionPolyfill extends FunctionValue {
|
|||||||
awaiting = false;
|
awaiting = false;
|
||||||
while (!awaiting) {
|
while (!awaiting) {
|
||||||
try {
|
try {
|
||||||
res = frame.next(ctx, inducedValue, Runners.NO_RETURN, inducedError);
|
res = frame.next(ctx, inducedValue, Runners.NO_RETURN, inducedError == Runners.NO_RETURN ? null : new EngineException(inducedError));
|
||||||
inducedValue = inducedError = Runners.NO_RETURN;
|
inducedValue = inducedError = Runners.NO_RETURN;
|
||||||
if (res != Runners.NO_RETURN) {
|
if (res != Runners.NO_RETURN) {
|
||||||
promise.fulfill(ctx, res);
|
promise.fulfill(ctx, res);
|
||||||
|
@ -39,7 +39,7 @@ public class AsyncGeneratorPolyfill extends FunctionValue {
|
|||||||
|
|
||||||
while (state == 0) {
|
while (state == 0) {
|
||||||
try {
|
try {
|
||||||
res = frame.next(ctx, inducedValue, inducedReturn, inducedError);
|
res = frame.next(ctx, inducedValue, inducedReturn, inducedError == Runners.NO_RETURN ? null : new EngineException(inducedError));
|
||||||
inducedValue = inducedReturn = inducedError = Runners.NO_RETURN;
|
inducedValue = inducedReturn = inducedError = Runners.NO_RETURN;
|
||||||
if (res != Runners.NO_RETURN) {
|
if (res != Runners.NO_RETURN) {
|
||||||
var obj = new ObjectValue();
|
var obj = new ObjectValue();
|
||||||
|
@ -7,8 +7,6 @@ import me.topchetoeu.jscript.interop.Native;
|
|||||||
import me.topchetoeu.jscript.interop.NativeConstructor;
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
|
|
||||||
public class BooleanPolyfill {
|
public class BooleanPolyfill {
|
||||||
@Native("@@Symbol.typeName") public final String name = "Boolean";
|
|
||||||
|
|
||||||
public static final BooleanPolyfill TRUE = new BooleanPolyfill(true);
|
public static final BooleanPolyfill TRUE = new BooleanPolyfill(true);
|
||||||
public static final BooleanPolyfill FALSE = new BooleanPolyfill(false);
|
public static final BooleanPolyfill FALSE = new BooleanPolyfill(false);
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ public class ErrorPolyfill {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String toString(Context ctx, Object name, Object message, ArrayValue stack) throws InterruptedException {
|
private static String toString(Context ctx, Object cause, Object name, Object message, ArrayValue stack) throws InterruptedException {
|
||||||
if (name == null) name = "";
|
if (name == null) name = "";
|
||||||
else name = Values.toString(ctx, name).trim();
|
else name = Values.toString(ctx, name).trim();
|
||||||
if (message == null) message = "";
|
if (message == null) message = "";
|
||||||
@ -39,7 +39,7 @@ public class ErrorPolyfill {
|
|||||||
|
|
||||||
if (!name.equals("")) res.append(name);
|
if (!name.equals("")) res.append(name);
|
||||||
if (!message.equals("") && !name.equals("")) res.append(": ");
|
if (!message.equals("") && !name.equals("")) res.append(": ");
|
||||||
if (!name.equals("")) res.append(message);
|
if (!message.equals("")) res.append(message);
|
||||||
|
|
||||||
if (stack != null) {
|
if (stack != null) {
|
||||||
for (var el : stack) {
|
for (var el : stack) {
|
||||||
@ -48,6 +48,8 @@ public class ErrorPolyfill {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cause instanceof ObjectValue) res.append(toString(ctx, cause));
|
||||||
|
|
||||||
return res.toString();
|
return res.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,6 +58,7 @@ public class ErrorPolyfill {
|
|||||||
var stack = Values.getMember(ctx, thisArg, "stack");
|
var stack = Values.getMember(ctx, thisArg, "stack");
|
||||||
if (!(stack instanceof ArrayValue)) stack = null;
|
if (!(stack instanceof ArrayValue)) stack = null;
|
||||||
return toString(ctx,
|
return toString(ctx,
|
||||||
|
Values.getMember(ctx, thisArg, "cause"),
|
||||||
Values.getMember(ctx, thisArg, "name"),
|
Values.getMember(ctx, thisArg, "name"),
|
||||||
Values.getMember(ctx, thisArg, "message"),
|
Values.getMember(ctx, thisArg, "message"),
|
||||||
(ArrayValue)stack
|
(ArrayValue)stack
|
||||||
|
@ -8,9 +8,7 @@ import me.topchetoeu.jscript.exceptions.EngineException;
|
|||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
|
||||||
public class FunctionPolyfill {
|
public class FunctionPolyfill {
|
||||||
@Native("@@Symbol.typeName") public final String name = "Function";
|
@Native(thisArg = true) public static Object apply(Context ctx, FunctionValue func, Object thisArg, ArrayValue args) throws InterruptedException {
|
||||||
|
|
||||||
@Native(thisArg = true) public static Object apply(Context ctx, FunctionValue func, Object thisArg, ArrayValue args) throws InterruptedException {
|
|
||||||
return func.call(ctx, thisArg, args.toArray());
|
return func.call(ctx, thisArg, args.toArray());
|
||||||
}
|
}
|
||||||
@Native(thisArg = true) public static Object call(Context ctx, FunctionValue func, Object thisArg, Object... args) throws InterruptedException {
|
@Native(thisArg = true) public static Object call(Context ctx, FunctionValue func, Object thisArg, Object... args) throws InterruptedException {
|
||||||
|
@ -35,7 +35,7 @@ public class GeneratorPolyfill extends FunctionValue {
|
|||||||
|
|
||||||
while (!yielding) {
|
while (!yielding) {
|
||||||
try {
|
try {
|
||||||
res = frame.next(ctx, inducedValue, inducedReturn, inducedError);
|
res = frame.next(ctx, inducedValue, inducedReturn, inducedError == Runners.NO_RETURN ? null : new EngineException(inducedError));
|
||||||
inducedReturn = inducedError = Runners.NO_RETURN;
|
inducedReturn = inducedError = Runners.NO_RETURN;
|
||||||
if (res != Runners.NO_RETURN) {
|
if (res != Runners.NO_RETURN) {
|
||||||
done = true;
|
done = true;
|
||||||
|
@ -20,7 +20,7 @@ public class Internals {
|
|||||||
@Native public final FunctionValue
|
@Native public final FunctionValue
|
||||||
object, function, array,
|
object, function, array,
|
||||||
bool, number, string, symbol,
|
bool, number, string, symbol,
|
||||||
promise, map, set,
|
promise, map, set, regexp,
|
||||||
error, syntax, type, range;
|
error, syntax, type, range;
|
||||||
|
|
||||||
@Native public void markSpecial(FunctionValue ...funcs) {
|
@Native public void markSpecial(FunctionValue ...funcs) {
|
||||||
@ -174,5 +174,6 @@ public class Internals {
|
|||||||
this.syntax = targetEnv.wrappersProvider.getConstr(SyntaxErrorPolyfill.class);
|
this.syntax = targetEnv.wrappersProvider.getConstr(SyntaxErrorPolyfill.class);
|
||||||
this.type = targetEnv.wrappersProvider.getConstr(TypeErrorPolyfill.class);
|
this.type = targetEnv.wrappersProvider.getConstr(TypeErrorPolyfill.class);
|
||||||
this.range = targetEnv.wrappersProvider.getConstr(RangeErrorPolyfill.class);
|
this.range = targetEnv.wrappersProvider.getConstr(RangeErrorPolyfill.class);
|
||||||
|
this.regexp = targetEnv.wrappersProvider.getConstr(RegExpPolyfill.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,85 +1,85 @@
|
|||||||
package me.topchetoeu.jscript.polyfills;
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
import me.topchetoeu.jscript.json.JSONElement;
|
import me.topchetoeu.jscript.json.JSONElement;
|
||||||
import me.topchetoeu.jscript.json.JSONList;
|
import me.topchetoeu.jscript.json.JSONList;
|
||||||
import me.topchetoeu.jscript.json.JSONMap;
|
import me.topchetoeu.jscript.json.JSONMap;
|
||||||
|
|
||||||
public class JSON {
|
public class JSONPolyfill {
|
||||||
private static Object toJS(JSONElement val) {
|
private static Object toJS(JSONElement val) {
|
||||||
if (val.isBoolean()) return val.bool();
|
if (val.isBoolean()) return val.bool();
|
||||||
if (val.isString()) return val.string();
|
if (val.isString()) return val.string();
|
||||||
if (val.isNumber()) return val.number();
|
if (val.isNumber()) return val.number();
|
||||||
if (val.isList()) return ArrayValue.of(null, val.list().stream().map(JSON::toJS).collect(Collectors.toList()));
|
if (val.isList()) return ArrayValue.of(null, val.list().stream().map(JSONPolyfill::toJS).collect(Collectors.toList()));
|
||||||
if (val.isMap()) {
|
if (val.isMap()) {
|
||||||
var res = new ObjectValue();
|
var res = new ObjectValue();
|
||||||
for (var el : val.map().entrySet()) {
|
for (var el : val.map().entrySet()) {
|
||||||
res.defineProperty(null, el.getKey(), toJS(el.getValue()));
|
res.defineProperty(null, el.getKey(), toJS(el.getValue()));
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
if (val.isNull()) return Values.NULL;
|
if (val.isNull()) return Values.NULL;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
private static JSONElement toJSON(Context ctx, Object val, HashSet<Object> prev) throws InterruptedException {
|
private static JSONElement toJSON(Context ctx, Object val, HashSet<Object> prev) throws InterruptedException {
|
||||||
if (val instanceof Boolean) return JSONElement.bool((boolean)val);
|
if (val instanceof Boolean) return JSONElement.bool((boolean)val);
|
||||||
if (val instanceof Number) return JSONElement.number(((Number)val).doubleValue());
|
if (val instanceof Number) return JSONElement.number(((Number)val).doubleValue());
|
||||||
if (val instanceof String) return JSONElement.string((String)val);
|
if (val instanceof String) return JSONElement.string((String)val);
|
||||||
if (val == Values.NULL) return JSONElement.NULL;
|
if (val == Values.NULL) return JSONElement.NULL;
|
||||||
if (val instanceof ObjectValue) {
|
if (val instanceof ObjectValue) {
|
||||||
if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
|
if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
|
||||||
prev.add(val);
|
prev.add(val);
|
||||||
|
|
||||||
var res = new JSONMap();
|
var res = new JSONMap();
|
||||||
|
|
||||||
for (var el : ((ObjectValue)val).keys(false)) {
|
for (var el : ((ObjectValue)val).keys(false)) {
|
||||||
var jsonEl = toJSON(ctx, ((ObjectValue)val).getMember(ctx, el), prev);
|
var jsonEl = toJSON(ctx, ((ObjectValue)val).getMember(ctx, el), prev);
|
||||||
if (jsonEl == null) continue;
|
if (jsonEl == null) continue;
|
||||||
if (el instanceof String || el instanceof Number) res.put(el.toString(), jsonEl);
|
if (el instanceof String || el instanceof Number) res.put(el.toString(), jsonEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
prev.remove(val);
|
prev.remove(val);
|
||||||
return JSONElement.of(res);
|
return JSONElement.of(res);
|
||||||
}
|
}
|
||||||
if (val instanceof ArrayValue) {
|
if (val instanceof ArrayValue) {
|
||||||
if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
|
if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
|
||||||
prev.add(val);
|
prev.add(val);
|
||||||
|
|
||||||
var res = new JSONList();
|
var res = new JSONList();
|
||||||
|
|
||||||
for (var el : ((ArrayValue)val).toArray()) {
|
for (var el : ((ArrayValue)val).toArray()) {
|
||||||
var jsonEl = toJSON(ctx, el, prev);
|
var jsonEl = toJSON(ctx, el, prev);
|
||||||
if (jsonEl == null) jsonEl = JSONElement.NULL;
|
if (jsonEl == null) jsonEl = JSONElement.NULL;
|
||||||
res.add(jsonEl);
|
res.add(jsonEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
prev.remove(val);
|
prev.remove(val);
|
||||||
return JSONElement.of(res);
|
return JSONElement.of(res);
|
||||||
}
|
}
|
||||||
if (val == null) return null;
|
if (val == null) return null;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native
|
@Native
|
||||||
public static Object parse(Context ctx, String val) throws InterruptedException {
|
public static Object parse(Context ctx, String val) throws InterruptedException {
|
||||||
try {
|
try {
|
||||||
return toJS(me.topchetoeu.jscript.json.JSON.parse("<value>", val));
|
return toJS(me.topchetoeu.jscript.json.JSON.parse("<value>", val));
|
||||||
}
|
}
|
||||||
catch (SyntaxException e) {
|
catch (SyntaxException e) {
|
||||||
throw EngineException.ofSyntax(e.msg);
|
throw EngineException.ofSyntax(e.msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Native
|
@Native
|
||||||
public static String stringify(Context ctx, Object val) throws InterruptedException {
|
public static String stringify(Context ctx, Object val) throws InterruptedException {
|
||||||
return me.topchetoeu.jscript.json.JSON.stringify(toJSON(ctx, val, new HashSet<>()));
|
return me.topchetoeu.jscript.json.JSON.stringify(toJSON(ctx, val, new HashSet<>()));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,7 +13,6 @@ import me.topchetoeu.jscript.interop.Native;
|
|||||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
|
|
||||||
public class MapPolyfill {
|
public class MapPolyfill {
|
||||||
@Native("@@Symbol.typeName") public final String name = "Map";
|
|
||||||
private LinkedHashMap<Object, Object> map = new LinkedHashMap<>();
|
private LinkedHashMap<Object, Object> map = new LinkedHashMap<>();
|
||||||
|
|
||||||
@Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) throws InterruptedException {
|
@Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) throws InterruptedException {
|
||||||
|
@ -7,8 +7,6 @@ import me.topchetoeu.jscript.interop.Native;
|
|||||||
import me.topchetoeu.jscript.interop.NativeConstructor;
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
|
|
||||||
public class NumberPolyfill {
|
public class NumberPolyfill {
|
||||||
@Native("@@Symbol.typeName") public final String name = "Number";
|
|
||||||
|
|
||||||
@Native public static final double EPSILON = java.lang.Math.ulp(1.0);
|
@Native public static final double EPSILON = java.lang.Math.ulp(1.0);
|
||||||
@Native public static final double MAX_SAFE_INTEGER = 9007199254740991.;
|
@Native public static final double MAX_SAFE_INTEGER = 9007199254740991.;
|
||||||
@Native public static final double MIN_SAFE_INTEGER = -MAX_SAFE_INTEGER;
|
@Native public static final double MIN_SAFE_INTEGER = -MAX_SAFE_INTEGER;
|
||||||
|
@ -11,8 +11,6 @@ import me.topchetoeu.jscript.interop.Native;
|
|||||||
import me.topchetoeu.jscript.interop.NativeConstructor;
|
import me.topchetoeu.jscript.interop.NativeConstructor;
|
||||||
|
|
||||||
public class ObjectPolyfill {
|
public class ObjectPolyfill {
|
||||||
@Native("@@Symbol.typeName") public final String name = "Object";
|
|
||||||
|
|
||||||
@Native public static ObjectValue assign(Context ctx, ObjectValue dst, Object... src) throws InterruptedException {
|
@Native public static ObjectValue assign(Context ctx, ObjectValue dst, Object... src) throws InterruptedException {
|
||||||
for (var obj : src) {
|
for (var obj : src) {
|
||||||
for (var key : Values.getMembers(ctx, obj, true, true)) {
|
for (var key : Values.getMembers(ctx, obj, true, true)) {
|
||||||
|
@ -28,8 +28,6 @@ public class PromisePolyfill {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native("@@Symbol.typeName") public final String name = "Promise";
|
|
||||||
|
|
||||||
@Native("resolve")
|
@Native("resolve")
|
||||||
public static PromisePolyfill ofResolved(Context ctx, Object val) throws InterruptedException {
|
public static PromisePolyfill ofResolved(Context ctx, Object val) throws InterruptedException {
|
||||||
var res = new PromisePolyfill();
|
var res = new PromisePolyfill();
|
||||||
|
@ -1,187 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.polyfills;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
|
||||||
import me.topchetoeu.jscript.engine.values.NativeWrapper;
|
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
|
||||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
|
||||||
import me.topchetoeu.jscript.interop.NativeSetter;
|
|
||||||
|
|
||||||
public class RegExp {
|
|
||||||
// I used Regex to analyze Regex
|
|
||||||
private static final Pattern NAMED_PATTERN = Pattern.compile("\\(\\?<([^=!].*?)>", Pattern.DOTALL);
|
|
||||||
private static final Pattern ESCAPE_PATTERN = Pattern.compile("[/\\-\\\\^$*+?.()|\\[\\]{}]");
|
|
||||||
|
|
||||||
private static String cleanupPattern(Context ctx, Object val) throws InterruptedException {
|
|
||||||
if (val == null) return "(?:)";
|
|
||||||
if (val instanceof RegExp) return ((RegExp)val).source;
|
|
||||||
if (val instanceof NativeWrapper && ((NativeWrapper)val).wrapped instanceof RegExp) {
|
|
||||||
return ((RegExp)((NativeWrapper)val).wrapped).source;
|
|
||||||
}
|
|
||||||
var res = Values.toString(ctx, val);
|
|
||||||
if (res.equals("")) return "(?:)";
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
private static String cleanupFlags(Context ctx, Object val) throws InterruptedException {
|
|
||||||
if (val == null) return "";
|
|
||||||
return Values.toString(ctx, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean checkEscaped(String s, int pos) {
|
|
||||||
int n = 0;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (pos <= 0) break;
|
|
||||||
if (s.charAt(pos) != '\\') break;
|
|
||||||
n++;
|
|
||||||
pos--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (n % 2) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Native
|
|
||||||
public static RegExp escape(Context ctx, Object raw, Object flags) throws InterruptedException {
|
|
||||||
return escape(Values.toString(ctx, raw), cleanupFlags(ctx, flags));
|
|
||||||
}
|
|
||||||
public static RegExp escape(String raw, String flags) {
|
|
||||||
return new RegExp(ESCAPE_PATTERN.matcher(raw).replaceAll("\\\\$0"), flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Pattern pattern;
|
|
||||||
private String[] namedGroups;
|
|
||||||
private int flags;
|
|
||||||
private int lastI = 0;
|
|
||||||
|
|
||||||
@Native
|
|
||||||
public final String source;
|
|
||||||
@Native
|
|
||||||
public final boolean hasIndices;
|
|
||||||
@Native
|
|
||||||
public final boolean global;
|
|
||||||
@Native
|
|
||||||
public final boolean sticky;
|
|
||||||
|
|
||||||
@NativeGetter("ignoreCase")
|
|
||||||
public boolean ignoreCase() { return (flags & Pattern.CASE_INSENSITIVE) != 0; }
|
|
||||||
@NativeGetter("multiline")
|
|
||||||
public boolean multiline() { return (flags & Pattern.MULTILINE) != 0; }
|
|
||||||
@NativeGetter("unicode")
|
|
||||||
public boolean unicode() { return (flags & Pattern.UNICODE_CHARACTER_CLASS) != 0; }
|
|
||||||
@NativeGetter("unicode")
|
|
||||||
public boolean dotAll() { return (flags & Pattern.DOTALL) != 0; }
|
|
||||||
|
|
||||||
@NativeGetter("lastIndex")
|
|
||||||
public int lastIndex() { return lastI; }
|
|
||||||
@NativeSetter("lastIndex")
|
|
||||||
public void setLastIndex(Context ctx, Object i) throws InterruptedException {
|
|
||||||
lastI = (int)Values.toNumber(ctx, i);
|
|
||||||
}
|
|
||||||
public void setLastIndex(int i) {
|
|
||||||
lastI = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NativeGetter("flags")
|
|
||||||
public final String flags() {
|
|
||||||
String res = "";
|
|
||||||
if (hasIndices) res += 'd';
|
|
||||||
if (global) res += 'g';
|
|
||||||
if (ignoreCase()) res += 'i';
|
|
||||||
if (multiline()) res += 'm';
|
|
||||||
if (dotAll()) res += 's';
|
|
||||||
if (unicode()) res += 'u';
|
|
||||||
if (sticky) res += 'y';
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Native
|
|
||||||
public Object exec(Context ctx, Object str) throws InterruptedException {
|
|
||||||
return exec(Values.toString(ctx, str));
|
|
||||||
}
|
|
||||||
public Object exec(String str) {
|
|
||||||
var matcher = pattern.matcher(str);
|
|
||||||
if (lastI > str.length() || !matcher.find(lastI) || sticky && matcher.start() != lastI) {
|
|
||||||
lastI = 0;
|
|
||||||
return Values.NULL;
|
|
||||||
}
|
|
||||||
if (sticky || global) {
|
|
||||||
lastI = matcher.end();
|
|
||||||
if (matcher.end() == matcher.start()) lastI++;
|
|
||||||
}
|
|
||||||
|
|
||||||
var obj = new ArrayValue();
|
|
||||||
var groups = new ObjectValue();
|
|
||||||
|
|
||||||
for (var el : namedGroups) {
|
|
||||||
try {
|
|
||||||
groups.defineProperty(null, el, matcher.group(el));
|
|
||||||
}
|
|
||||||
catch (IllegalArgumentException e) { }
|
|
||||||
}
|
|
||||||
if (groups.values.size() == 0) groups = null;
|
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < matcher.groupCount() + 1; i++) {
|
|
||||||
obj.set(null, i, matcher.group(i));
|
|
||||||
}
|
|
||||||
obj.defineProperty(null, "groups", groups);
|
|
||||||
obj.defineProperty(null, "index", matcher.start());
|
|
||||||
obj.defineProperty(null, "input", str);
|
|
||||||
|
|
||||||
if (hasIndices) {
|
|
||||||
var indices = new ArrayValue();
|
|
||||||
for (int i = 0; i < matcher.groupCount() + 1; i++) {
|
|
||||||
indices.set(null, i, new ArrayValue(null, matcher.start(i), matcher.end(i)));
|
|
||||||
}
|
|
||||||
var groupIndices = new ObjectValue();
|
|
||||||
for (var el : namedGroups) {
|
|
||||||
groupIndices.defineProperty(null, el, new ArrayValue(null, matcher.start(el), matcher.end(el)));
|
|
||||||
}
|
|
||||||
indices.defineProperty(null, "groups", groupIndices);
|
|
||||||
obj.defineProperty(null, "indices", indices);
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Native
|
|
||||||
public RegExp(Context ctx, Object pattern, Object flags) throws InterruptedException {
|
|
||||||
this(cleanupPattern(ctx, pattern), cleanupFlags(ctx, flags));
|
|
||||||
}
|
|
||||||
public RegExp(String pattern, String flags) {
|
|
||||||
if (pattern == null || pattern.equals("")) pattern = "(?:)";
|
|
||||||
if (flags == null || flags.equals("")) flags = "";
|
|
||||||
|
|
||||||
this.flags = 0;
|
|
||||||
this.hasIndices = flags.contains("d");
|
|
||||||
this.global = flags.contains("g");
|
|
||||||
this.sticky = flags.contains("y");
|
|
||||||
this.source = pattern;
|
|
||||||
|
|
||||||
if (flags.contains("i")) this.flags |= Pattern.CASE_INSENSITIVE;
|
|
||||||
if (flags.contains("m")) this.flags |= Pattern.MULTILINE;
|
|
||||||
if (flags.contains("s")) this.flags |= Pattern.DOTALL;
|
|
||||||
if (flags.contains("u")) this.flags |= Pattern.UNICODE_CHARACTER_CLASS;
|
|
||||||
|
|
||||||
this.pattern = Pattern.compile(pattern.replace("\\d", "[0-9]"), this.flags);
|
|
||||||
|
|
||||||
var matcher = NAMED_PATTERN.matcher(source);
|
|
||||||
var groups = new ArrayList<String>();
|
|
||||||
|
|
||||||
while (matcher.find()) {
|
|
||||||
if (!checkEscaped(source, matcher.start() - 1)) {
|
|
||||||
groups.add(matcher.group(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namedGroups = groups.toArray(String[]::new);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RegExp(String pattern) { this(pattern, null); }
|
|
||||||
public RegExp() { this(null, null); }
|
|
||||||
}
|
|
296
src/me/topchetoeu/jscript/polyfills/RegExpPolyfill.java
Normal file
296
src/me/topchetoeu/jscript/polyfills/RegExpPolyfill.java
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.NativeWrapper;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
|
|
||||||
|
public class RegExpPolyfill {
|
||||||
|
// I used Regex to analyze Regex
|
||||||
|
private static final Pattern NAMED_PATTERN = Pattern.compile("\\(\\?<([^=!].*?)>", Pattern.DOTALL);
|
||||||
|
private static final Pattern ESCAPE_PATTERN = Pattern.compile("[/\\-\\\\^$*+?.()|\\[\\]{}]");
|
||||||
|
|
||||||
|
private static String cleanupPattern(Context ctx, Object val) throws InterruptedException {
|
||||||
|
if (val == null) return "(?:)";
|
||||||
|
if (val instanceof RegExpPolyfill) return ((RegExpPolyfill)val).source;
|
||||||
|
if (val instanceof NativeWrapper && ((NativeWrapper)val).wrapped instanceof RegExpPolyfill) {
|
||||||
|
return ((RegExpPolyfill)((NativeWrapper)val).wrapped).source;
|
||||||
|
}
|
||||||
|
var res = Values.toString(ctx, val);
|
||||||
|
if (res.equals("")) return "(?:)";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
private static String cleanupFlags(Context ctx, Object val) throws InterruptedException {
|
||||||
|
if (val == null) return "";
|
||||||
|
return Values.toString(ctx, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean checkEscaped(String s, int pos) {
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (pos <= 0) break;
|
||||||
|
if (s.charAt(pos) != '\\') break;
|
||||||
|
n++;
|
||||||
|
pos--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (n % 2) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native
|
||||||
|
public static RegExpPolyfill escape(Context ctx, Object raw, Object flags) throws InterruptedException {
|
||||||
|
return escape(Values.toString(ctx, raw), cleanupFlags(ctx, flags));
|
||||||
|
}
|
||||||
|
public static RegExpPolyfill escape(String raw, String flags) {
|
||||||
|
return new RegExpPolyfill(ESCAPE_PATTERN.matcher(raw).replaceAll("\\\\$0"), flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pattern pattern;
|
||||||
|
private String[] namedGroups;
|
||||||
|
private int flags;
|
||||||
|
|
||||||
|
@Native public int lastI = 0;
|
||||||
|
@Native public final String source;
|
||||||
|
@Native public final boolean hasIndices;
|
||||||
|
@Native public final boolean global;
|
||||||
|
@Native public final boolean sticky;
|
||||||
|
|
||||||
|
@NativeGetter public boolean ignoreCase() { return (flags & Pattern.CASE_INSENSITIVE) != 0; }
|
||||||
|
@NativeGetter public boolean multiline() { return (flags & Pattern.MULTILINE) != 0; }
|
||||||
|
@NativeGetter public boolean unicode() { return (flags & Pattern.UNICODE_CHARACTER_CLASS) != 0; }
|
||||||
|
@NativeGetter public boolean dotAll() { return (flags & Pattern.DOTALL) != 0; }
|
||||||
|
|
||||||
|
@NativeGetter("flags") public final String flags() {
|
||||||
|
String res = "";
|
||||||
|
if (hasIndices) res += 'd';
|
||||||
|
if (global) res += 'g';
|
||||||
|
if (ignoreCase()) res += 'i';
|
||||||
|
if (multiline()) res += 'm';
|
||||||
|
if (dotAll()) res += 's';
|
||||||
|
if (unicode()) res += 'u';
|
||||||
|
if (sticky) res += 'y';
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Native public Object exec(String str) {
|
||||||
|
var matcher = pattern.matcher(str);
|
||||||
|
if (lastI > str.length() || !matcher.find(lastI) || sticky && matcher.start() != lastI) {
|
||||||
|
lastI = 0;
|
||||||
|
return Values.NULL;
|
||||||
|
}
|
||||||
|
if (sticky || global) {
|
||||||
|
lastI = matcher.end();
|
||||||
|
if (matcher.end() == matcher.start()) lastI++;
|
||||||
|
}
|
||||||
|
|
||||||
|
var obj = new ArrayValue();
|
||||||
|
var groups = new ObjectValue();
|
||||||
|
|
||||||
|
for (var el : namedGroups) {
|
||||||
|
try {
|
||||||
|
groups.defineProperty(null, el, matcher.group(el));
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) { }
|
||||||
|
}
|
||||||
|
if (groups.values.size() == 0) groups = null;
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < matcher.groupCount() + 1; i++) {
|
||||||
|
obj.set(null, i, matcher.group(i));
|
||||||
|
}
|
||||||
|
obj.defineProperty(null, "groups", groups);
|
||||||
|
obj.defineProperty(null, "index", matcher.start());
|
||||||
|
obj.defineProperty(null, "input", str);
|
||||||
|
|
||||||
|
if (hasIndices) {
|
||||||
|
var indices = new ArrayValue();
|
||||||
|
for (int i = 0; i < matcher.groupCount() + 1; i++) {
|
||||||
|
indices.set(null, i, new ArrayValue(null, matcher.start(i), matcher.end(i)));
|
||||||
|
}
|
||||||
|
var groupIndices = new ObjectValue();
|
||||||
|
for (var el : namedGroups) {
|
||||||
|
groupIndices.defineProperty(null, el, new ArrayValue(null, matcher.start(el), matcher.end(el)));
|
||||||
|
}
|
||||||
|
indices.defineProperty(null, "groups", groupIndices);
|
||||||
|
obj.defineProperty(null, "indices", indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native public boolean test(String str) {
|
||||||
|
return this.exec(str) != Values.NULL;
|
||||||
|
}
|
||||||
|
@Native public String toString() {
|
||||||
|
return "/" + source + "/" + flags();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native("@@Symvol.match") public Object match(Context ctx, String target) throws InterruptedException {
|
||||||
|
if (this.global) {
|
||||||
|
var res = new ArrayValue();
|
||||||
|
Object val;
|
||||||
|
while ((val = this.exec(target)) != Values.NULL) {
|
||||||
|
res.set(ctx, res.size(), Values.getMember(ctx, val, 0));
|
||||||
|
}
|
||||||
|
lastI = 0;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var res = this.exec(target);
|
||||||
|
if (!this.sticky) this.lastI = 0;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native("@@Symvol.matchAll") public Object matchAll(Context ctx, String target) throws InterruptedException {
|
||||||
|
var pattern = new RegExpPolyfill(this.source, this.flags() + "g");
|
||||||
|
|
||||||
|
return Values.fromJavaIterator(ctx, new Iterator<Object>() {
|
||||||
|
private Object val = null;
|
||||||
|
private boolean updated = false;
|
||||||
|
|
||||||
|
private void update() {
|
||||||
|
if (!updated) val = pattern.exec(target);
|
||||||
|
}
|
||||||
|
@Override public boolean hasNext() {
|
||||||
|
update();
|
||||||
|
return val != Values.NULL;
|
||||||
|
}
|
||||||
|
@Override public Object next() {
|
||||||
|
update();
|
||||||
|
updated = false;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native("@@Symvol.split") public ArrayValue split(Context ctx, String target, Object limit, boolean sensible) throws InterruptedException {
|
||||||
|
var pattern = new RegExpPolyfill(this.source, this.flags() + "g");
|
||||||
|
Object match;
|
||||||
|
int lastEnd = 0;
|
||||||
|
var res = new ArrayValue();
|
||||||
|
var lim = limit == null ? 0 : Values.toNumber(ctx, limit);
|
||||||
|
|
||||||
|
while ((match = pattern.exec(target)) != Values.NULL) {
|
||||||
|
var added = new ArrayList<String>();
|
||||||
|
var arrMatch = (ArrayValue)match;
|
||||||
|
int index = (int)Values.toNumber(ctx, Values.getMember(ctx, match, "index"));
|
||||||
|
var matchVal = (String)arrMatch.get(0);
|
||||||
|
|
||||||
|
if (index >= target.length()) break;
|
||||||
|
|
||||||
|
if (matchVal.length() == 0 || index - lastEnd > 0) {
|
||||||
|
added.add(target.substring(lastEnd, pattern.lastI));
|
||||||
|
if (pattern.lastI < target.length()) {
|
||||||
|
for (var i = 1; i < arrMatch.size(); i++) added.add((String)arrMatch.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (var i = 1; i < arrMatch.size(); i++) added.add((String)arrMatch.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sensible) {
|
||||||
|
if (limit != null && res.size() + added.size() >= lim) break;
|
||||||
|
else for (var i = 0; i < added.size(); i++) res.set(ctx, res.size(), added.get(i));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (var i = 0; i < added.size(); i++) {
|
||||||
|
if (limit != null && res.size() >= lim) return res;
|
||||||
|
else res.set(ctx, res.size(), added.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastEnd = pattern.lastI;
|
||||||
|
}
|
||||||
|
if (lastEnd < target.length()) {
|
||||||
|
res.set(ctx, res.size(), target.substring(lastEnd));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
// [Symbol.replace](target, replacement) {
|
||||||
|
// const pattern = new this.constructor(this, this.flags + "d") as RegExp;
|
||||||
|
// let match: RegExpResult | null;
|
||||||
|
// let lastEnd = 0;
|
||||||
|
// const res: string[] = [];
|
||||||
|
// // log(pattern.toString());
|
||||||
|
// while ((match = pattern.exec(target)) !== null) {
|
||||||
|
// const indices = match.indices![0];
|
||||||
|
// res.push(target.substring(lastEnd, indices[0]));
|
||||||
|
// if (replacement instanceof Function) {
|
||||||
|
// res.push(replacement(target.substring(indices[0], indices[1]), ...match.slice(1), indices[0], target));
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// res.push(replacement);
|
||||||
|
// }
|
||||||
|
// lastEnd = indices[1];
|
||||||
|
// if (!pattern.global) break;
|
||||||
|
// }
|
||||||
|
// if (lastEnd < target.length) {
|
||||||
|
// res.push(target.substring(lastEnd));
|
||||||
|
// }
|
||||||
|
// return res.join('');
|
||||||
|
// },
|
||||||
|
// [Symbol.search](target, reverse, start) {
|
||||||
|
// const pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp;
|
||||||
|
// if (!reverse) {
|
||||||
|
// pattern.lastIndex = (start as any) | 0;
|
||||||
|
// const res = pattern.exec(target);
|
||||||
|
// if (res) return res.index;
|
||||||
|
// else return -1;
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// start ??= target.length;
|
||||||
|
// start |= 0;
|
||||||
|
// let res: RegExpResult | null = null;
|
||||||
|
// while (true) {
|
||||||
|
// const tmp = pattern.exec(target);
|
||||||
|
// if (tmp === null || tmp.index > start) break;
|
||||||
|
// res = tmp;
|
||||||
|
// }
|
||||||
|
// if (res && res.index <= start) return res.index;
|
||||||
|
// else return -1;
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
@Native public RegExpPolyfill(Context ctx, Object pattern, Object flags) throws InterruptedException {
|
||||||
|
this(cleanupPattern(ctx, pattern), cleanupFlags(ctx, flags));
|
||||||
|
}
|
||||||
|
public RegExpPolyfill(String pattern, String flags) {
|
||||||
|
if (pattern == null || pattern.equals("")) pattern = "(?:)";
|
||||||
|
if (flags == null || flags.equals("")) flags = "";
|
||||||
|
|
||||||
|
this.flags = 0;
|
||||||
|
this.hasIndices = flags.contains("d");
|
||||||
|
this.global = flags.contains("g");
|
||||||
|
this.sticky = flags.contains("y");
|
||||||
|
this.source = pattern;
|
||||||
|
|
||||||
|
if (flags.contains("i")) this.flags |= Pattern.CASE_INSENSITIVE;
|
||||||
|
if (flags.contains("m")) this.flags |= Pattern.MULTILINE;
|
||||||
|
if (flags.contains("s")) this.flags |= Pattern.DOTALL;
|
||||||
|
if (flags.contains("u")) this.flags |= Pattern.UNICODE_CHARACTER_CLASS;
|
||||||
|
|
||||||
|
this.pattern = Pattern.compile(pattern.replace("\\d", "[0-9]"), this.flags);
|
||||||
|
|
||||||
|
var matcher = NAMED_PATTERN.matcher(source);
|
||||||
|
var groups = new ArrayList<String>();
|
||||||
|
|
||||||
|
while (matcher.find()) {
|
||||||
|
if (!checkEscaped(source, matcher.start() - 1)) {
|
||||||
|
groups.add(matcher.group(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namedGroups = groups.toArray(String[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RegExpPolyfill(String pattern) { this(pattern, null); }
|
||||||
|
public RegExpPolyfill() { this(null, null); }
|
||||||
|
}
|
@ -13,7 +13,6 @@ import me.topchetoeu.jscript.interop.Native;
|
|||||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
|
|
||||||
public class SetPolyfill {
|
public class SetPolyfill {
|
||||||
@Native("@@Symbol.typeName") public final String name = "Set";
|
|
||||||
private LinkedHashSet<Object> set = new LinkedHashSet<>();
|
private LinkedHashSet<Object> set = new LinkedHashSet<>();
|
||||||
|
|
||||||
@Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) throws InterruptedException {
|
@Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) throws InterruptedException {
|
||||||
|
@ -14,8 +14,6 @@ import me.topchetoeu.jscript.interop.NativeGetter;
|
|||||||
|
|
||||||
// TODO: implement index wrapping properly
|
// TODO: implement index wrapping properly
|
||||||
public class StringPolyfill {
|
public class StringPolyfill {
|
||||||
@Native("@@Symbol.typeName") public final String name = "String";
|
|
||||||
|
|
||||||
public final String value;
|
public final String value;
|
||||||
|
|
||||||
private static String passThis(Context ctx, String funcName, Object val) throws InterruptedException {
|
private static String passThis(Context ctx, String funcName, Object val) throws InterruptedException {
|
||||||
|
@ -14,7 +14,6 @@ import me.topchetoeu.jscript.interop.NativeGetter;
|
|||||||
|
|
||||||
public class SymbolPolyfill {
|
public class SymbolPolyfill {
|
||||||
private static final Map<String, Symbol> symbols = new HashMap<>();
|
private static final Map<String, Symbol> symbols = new HashMap<>();
|
||||||
@Native("@@Symbol.typeName") public final String name = "Symbol";
|
|
||||||
|
|
||||||
@NativeGetter public static Symbol typeName(Context ctx) { return ctx.env.symbol("Symbol.typeName"); }
|
@NativeGetter public static Symbol typeName(Context ctx) { return ctx.env.symbol("Symbol.typeName"); }
|
||||||
@NativeGetter public static Symbol replace(Context ctx) { return ctx.env.symbol("Symbol.replace"); }
|
@NativeGetter public static Symbol replace(Context ctx) { return ctx.env.symbol("Symbol.replace"); }
|
||||||
|
Loading…
Reference in New Issue
Block a user