From 22ec95a7b51baa3a4ca6f9330b0573795899649f Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Tue, 26 Sep 2023 22:54:12 +0300 Subject: [PATCH] feat: implement errors --- build.js | 2 +- lib/core.ts | 14 +++- lib/tsconfig.json | 1 - lib/values/errors.ts | 47 ----------- .../jscript/engine/Environment.java | 31 ++----- .../jscript/engine/MessageContext.java | 23 +++++- .../jscript/engine/frame/CodeFrame.java | 80 ++++++++++++++----- .../jscript/engine/frame/Runners.java | 22 ++--- .../jscript/engine/scope/GlobalScope.java | 6 +- .../jscript/engine/values/ObjectValue.java | 20 ----- .../jscript/engine/values/Values.java | 10 +-- .../jscript/exceptions/EngineException.java | 54 +++++++------ .../interop/NativeWrapperProvider.java | 14 ++-- .../jscript/interop/OverloadFunction.java | 16 ++-- .../polyfills/AsyncFunctionPolyfill.java | 4 +- .../polyfills/AsyncGeneratorPolyfill.java | 4 +- .../jscript/polyfills/ErrorPolyfill.java | 75 +++++++++++++++++ .../jscript/polyfills/FunctionPolyfill.java | 6 +- .../jscript/polyfills/GeneratorPolyfill.java | 4 +- .../jscript/polyfills/Internals.java | 13 ++- src/me/topchetoeu/jscript/polyfills/JSON.java | 2 +- .../jscript/polyfills/ObjectPolyfill.java | 10 +-- .../jscript/polyfills/PromisePolyfill.java | 10 +-- .../jscript/polyfills/StringPolyfill.java | 68 ++++++++-------- .../jscript/polyfills/SymbolPolyfill.java | 16 ++-- 25 files changed, 313 insertions(+), 239 deletions(-) delete mode 100644 lib/values/errors.ts create mode 100644 src/me/topchetoeu/jscript/polyfills/ErrorPolyfill.java diff --git a/build.js b/build.js index 20a1f23..774fabe 100644 --- a/build.js +++ b/build.js @@ -54,7 +54,7 @@ async function compileJava() { .replace('${AUTHOR}', conf.author) ); const args = ['--release', '11', ]; - if (argv[1] === 'debug') args.push('-g'); + if (argv[2] === 'debug') args.push('-g'); args.push('-d', 'dst/classes', 'Metadata.java'); for await (const path of find('src', undefined, v => v.endsWith('.java') && !v.endsWith('Metadata.java'))) args.push(path); diff --git a/lib/core.ts b/lib/core.ts index 61b0cd3..3ce7a25 100644 --- a/lib/core.ts +++ b/lib/core.ts @@ -13,6 +13,10 @@ interface Internals { number: NumberConstructor; string: StringConstructor; symbol: SymbolConstructor; + error: ErrorConstructor; + syntax: SyntaxErrorConstructor; + type: TypeErrorConstructor; + range: RangeErrorConstructor; map: typeof Map; set: typeof Set; @@ -57,6 +61,10 @@ try { const Number = env.global.Number = internals.number; const String = env.global.String = internals.string; const Symbol = env.global.Symbol = internals.symbol; + const Error = env.global.Error = internals.error; + const SyntaxError = env.global.SyntaxError = internals.syntax; + const TypeError = env.global.TypeError = internals.type; + const RangeError = env.global.RangeError = internals.range; const Map = env.global.Map = internals.map; const Set = env.global.Set = internals.set; @@ -69,12 +77,16 @@ try { 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); globalThis.log = (...args) => internals.apply(internals.log, internals, args); - run('values/errors'); run('regex'); run('timeout'); diff --git a/lib/tsconfig.json b/lib/tsconfig.json index cdd853b..f0ee4e3 100644 --- a/lib/tsconfig.json +++ b/lib/tsconfig.json @@ -3,7 +3,6 @@ "lib.d.ts", "modules.ts", "utils.ts", - "values/errors.ts", "regex.ts", "timeout.ts", "core.ts" diff --git a/lib/values/errors.ts b/lib/values/errors.ts deleted file mode 100644 index a8d1ef0..0000000 --- a/lib/values/errors.ts +++ /dev/null @@ -1,47 +0,0 @@ -define("values/errors", () => { - var Error = env.global.Error = function Error(msg: string) { - if (msg === undefined) msg = ''; - else msg += ''; - - return { - message: msg, - stack: [] as string[], - __proto__: Error.prototype, - } as any; - } as ErrorConstructor; - - setConstr(Error.prototype, Error); - setProps(Error.prototype, { - name: 'Error', - toString: internals.setEnv(function(this: Error) { - if (!(this instanceof Error)) return ''; - - if (this.message === '') return this.name; - else return this.name + ': ' + this.message; - }, env) - }); - env.setProto('error', Error.prototype); - internals.markSpecial(Error); - - function makeError(name: string, proto: string): T1 { - function constr (msg: string) { - var res = new Error(msg); - (res as any).__proto__ = constr.prototype; - return res; - } - - (constr as any).__proto__ = Error; - (constr.prototype as any).__proto__ = env.proto('error'); - setConstr(constr.prototype, constr as ErrorConstructor); - setProps(constr.prototype, { name: name }); - - internals.markSpecial(constr); - env.setProto(proto, constr.prototype); - - return constr as T1; - } - - env.global.RangeError = makeError('RangeError', 'rangeErr'); - env.global.TypeError = makeError('TypeError', 'typeErr'); - env.global.SyntaxError = makeError('SyntaxError', 'syntaxErr'); -}); \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/engine/Environment.java b/src/me/topchetoeu/jscript/engine/Environment.java index c9a4cbe..5340774 100644 --- a/src/me/topchetoeu/jscript/engine/Environment.java +++ b/src/me/topchetoeu/jscript/engine/Environment.java @@ -17,14 +17,12 @@ public class Environment { private HashMap prototypes = new HashMap<>(); public GlobalScope global; public WrappersProvider wrappersProvider; - /** - * NOTE: This is not the register for Symbol.for, but for the symbols like Symbol.iterator - */ + /** NOTE: This is not the register for Symbol.for, but for the symbols like Symbol.iterator */ public HashMap symbols = new HashMap<>(); @Native public FunctionValue compile; @Native public FunctionValue regexConstructor = new NativeFunction("RegExp", (ctx, thisArg, args) -> { - throw EngineException.ofError("Regular expressions not supported."); + throw EngineException.ofError(ctx, "Regular expressions not supported."); }); @Native public ObjectValue proto(String name) { return prototypes.get(name); @@ -43,37 +41,20 @@ public class Environment { } } - // @Native public ObjectValue arrayPrototype = new ObjectValue(); - // @Native public ObjectValue boolPrototype = new ObjectValue(); - // @Native public ObjectValue functionPrototype = new ObjectValue(); - // @Native public ObjectValue numberPrototype = new ObjectValue(); - // @Native public ObjectValue objectPrototype = new ObjectValue(PlaceholderProto.NONE); - // @Native public ObjectValue stringPrototype = new ObjectValue(); - // @Native public ObjectValue symbolPrototype = new ObjectValue(); - // @Native public ObjectValue errorPrototype = new ObjectValue(); - // @Native public ObjectValue syntaxErrPrototype = new ObjectValue(PlaceholderProto.ERROR); - // @Native public ObjectValue typeErrPrototype = new ObjectValue(PlaceholderProto.ERROR); - // @Native public ObjectValue rangeErrPrototype = new ObjectValue(PlaceholderProto.ERROR); - - @NativeGetter("global") - public ObjectValue getGlobal() { + @NativeGetter("global") public ObjectValue getGlobal() { return global.obj; } - @NativeSetter("global") - public void setGlobal(ObjectValue val) { + @NativeSetter("global") public void setGlobal(ObjectValue val) { global = new GlobalScope(val); } - @Native - public Environment fork() { + @Native public Environment fork() { var res = new Environment(compile, wrappersProvider, global); res.regexConstructor = regexConstructor; res.prototypes = new HashMap<>(prototypes); return res; } - - @Native - public Environment child() { + @Native public Environment child() { var res = fork(); res.global = res.global.globalChild(); return res; diff --git a/src/me/topchetoeu/jscript/engine/MessageContext.java b/src/me/topchetoeu/jscript/engine/MessageContext.java index 2ac19b4..9125018 100644 --- a/src/me/topchetoeu/jscript/engine/MessageContext.java +++ b/src/me/topchetoeu/jscript/engine/MessageContext.java @@ -15,9 +15,9 @@ public class MessageContext { public List frames() { return Collections.unmodifiableList(frames); } - public MessageContext pushFrame(CodeFrame frame) { + public MessageContext pushFrame(Context ctx, CodeFrame frame) throws InterruptedException { this.frames.add(frame); - if (this.frames.size() > maxStackFrames) throw EngineException.ofRange("Stack overflow!"); + if (this.frames.size() > maxStackFrames) throw EngineException.ofRange(ctx, "Stack overflow!"); return this; } public boolean popFrame(CodeFrame frame) { @@ -27,6 +27,25 @@ public class MessageContext { return true; } + public List stackTrace() { + var res = new ArrayList(); + + for (var el : frames) { + var name = el.function.name; + var loc = el.function.loc(); + var trace = ""; + + if (loc != null) trace += "at " + loc.toString() + " "; + if (name != null && !name.equals("")) trace += "in " + name + " "; + + trace = trace.trim(); + + if (!res.equals("")) res.add(trace); + } + + return res; + } + public MessageContext(Engine engine) { this.engine = engine; } diff --git a/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java b/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java index 818a4ae..7a94fe1 100644 --- a/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java +++ b/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java @@ -9,6 +9,7 @@ import me.topchetoeu.jscript.engine.scope.LocalScope; import me.topchetoeu.jscript.engine.scope.ValueVariable; import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.CodeFunction; +import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.exceptions.EngineException; @@ -93,6 +94,9 @@ public class CodeFrame { stack[stackPtr++] = Values.normalize(ctx, val); } + // TODO: THIS SYSTEM IS SEVERLY BROKEN + // MUST FIX!!!!! + private Object nextNoTry(Context ctx) throws InterruptedException { if (Thread.currentThread().isInterrupted()) throw new InterruptedException(); if (codePtr < 0 || codePtr >= function.body.length) return null; @@ -111,6 +115,58 @@ public class CodeFrame { } } + private void setCause(Context ctx, Object err, Object cause) throws InterruptedException { + if (err instanceof ObjectValue) { + Values.setMember(ctx, err, ctx.env.symbol("Symbol.cause"), cause); + } + } + // private void propagateErr(Context ctx, Object err) { + // while (!tryStack.isEmpty()) { + // var tmp = tryStack.get(tryStack.size() - 1); + + // if (tmp.state == TryCtx.STATE_TRY || tmp.state == TryCtx.STATE_CATCH) { + // tmp.jumpPtr = tmp.end; + + // if (tmp.state == TryCtx.STATE_TRY && tmp.hasCatch) { + // tmp.state = TryCtx.STATE_CATCH; + // scope.catchVars.add(new ValueVariable(false, err)); + // codePtr = tmp.catchStart; + // return; + // } + // else if (tmp.hasFinally) { + // tmp.state = TryCtx.STATE_FINALLY_THREW; + // tmp.err = new EngineException(err); + // codePtr = tmp.finallyStart; + // return; + // } + // else if (tmp.state == TryCtx.STATE_CATCH) scope.catchVars.remove(scope.catchVars.size() - 1); + // } + + // tryStack.remove(tryStack.size() - 1); + // } + // throw new EngineException(err); + // } + // private void propagateRet(Context ctx, Object val) { + // while (!tryStack.isEmpty()) { + // var tmp = tryStack.get(tryStack.size() - 1); + + // if (tmp.state == TryCtx.STATE_TRY || tmp.state == TryCtx.STATE_CATCH) { + // tmp.jumpPtr = tmp.end; + + // if (tmp.hasFinally) { + // tmp.state = TryCtx.STATE_FINALLY_RETURNED; + // tmp.err = new EngineException(err); + // codePtr = tmp.finallyStart; + // return; + // } + // else if (tmp.state == TryCtx.STATE_CATCH) scope.catchVars.remove(scope.catchVars.size() - 1); + // } + + // tryStack.remove(tryStack.size() - 1); + // } + // return val; + // } + public Object next(Context ctx, Object prevValue, Object prevReturn, Object prevError) throws InterruptedException { TryCtx tryCtx = null; if (prevError != Runners.NO_RETURN) prevReturn = Runners.NO_RETURN; @@ -210,18 +266,8 @@ public class CodeFrame { else return res; } catch (EngineException e) { - if (tryCtx.hasCatch) { - tryCtx.state = TryCtx.STATE_CATCH; - tryCtx.err = e; - codePtr = tryCtx.catchStart; - scope.catchVars.add(new ValueVariable(false, e.value)); - return Runners.NO_RETURN; - } - else if (tryCtx.hasFinally) { - tryCtx.err = e; - tryCtx.state = TryCtx.STATE_FINALLY_THREW; - } - else throw e; + throw e; + // propagateErr(ctx, e.value); } codePtr = tryCtx.finallyStart; @@ -237,7 +283,7 @@ public class CodeFrame { else return res; } catch (EngineException e) { - e.cause = tryCtx.err; + setCause(ctx, e.value, tryCtx.err); if (tryCtx.hasFinally) { tryCtx.err = e; tryCtx.state = TryCtx.STATE_FINALLY_THREW; @@ -253,7 +299,7 @@ public class CodeFrame { return nextNoTry(ctx); } catch (EngineException e) { - e.cause = tryCtx.err; + setCause(ctx, e.value, tryCtx.err); throw e; } } @@ -262,16 +308,12 @@ public class CodeFrame { public Object run(Context ctx) throws InterruptedException { try { - ctx.message.pushFrame(this); + ctx.message.pushFrame(ctx, this); while (true) { var res = next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, Runners.NO_RETURN); if (res != Runners.NO_RETURN) return res; } } - catch (Throwable e) { - // e.printStackTrace(); - throw e; - } finally { ctx.message.popFrame(this); } diff --git a/src/me/topchetoeu/jscript/engine/frame/Runners.java b/src/me/topchetoeu/jscript/engine/frame/Runners.java index 08d49e2..3032da1 100644 --- a/src/me/topchetoeu/jscript/engine/frame/Runners.java +++ b/src/me/topchetoeu/jscript/engine/frame/Runners.java @@ -26,8 +26,8 @@ public class Runners { public static Object execThrow(Context ctx, Instruction instr, CodeFrame frame) { throw new EngineException(frame.pop()); } - public static Object execThrowSyntax(Context ctx, Instruction instr, CodeFrame frame) { - throw EngineException.ofSyntax((String)instr.get(0)); + public static Object execThrowSyntax(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { + throw EngineException.ofSyntax(ctx, (String)instr.get(0)); } private static Object call(Context ctx, Object func, Object thisArg, Object ...args) throws InterruptedException { @@ -77,9 +77,9 @@ public class Runners { var name = frame.pop(); var obj = frame.pop(); - if (getter != null && !Values.isFunction(getter)) throw EngineException.ofType("Getter must be a function or undefined."); - if (setter != null && !Values.isFunction(setter)) throw EngineException.ofType("Setter must be a function or undefined."); - if (!Values.isObject(obj)) throw EngineException.ofType("Property apply target must be an object."); + if (getter != null && !Values.isFunction(getter)) throw EngineException.ofType(ctx, "Getter must be a function or undefined."); + if (setter != null && !Values.isFunction(setter)) throw EngineException.ofType(ctx, "Setter must be a function or undefined."); + if (!Values.isObject(obj)) throw EngineException.ofType(ctx, "Property apply target must be an object."); Values.object(obj).defineProperty(ctx, name, Values.function(getter), Values.function(setter), false, false); frame.push(ctx, obj); @@ -214,7 +214,7 @@ public class Runners { frame.push(ctx, Values.getMember(ctx, obj, key)); } catch (IllegalArgumentException e) { - throw EngineException.ofType(e.getMessage()); + throw EngineException.ofType(ctx, e.getMessage()); } frame.codePtr++; return NO_RETURN; @@ -239,7 +239,7 @@ public class Runners { var key = frame.pop(); var obj = frame.pop(); - if (!Values.setMember(ctx, obj, key, val)) throw EngineException.ofSyntax("Can't set member '" + key + "'."); + if (!Values.setMember(ctx, obj, key, val)) throw EngineException.ofSyntax(ctx, "Can't set member '" + key + "'."); if ((boolean)instr.get(0)) frame.push(ctx, val); frame.codePtr++; return NO_RETURN; @@ -307,11 +307,11 @@ public class Runners { frame.codePtr++; return NO_RETURN; } - public static Object execNop(Context ctx, Instruction instr, CodeFrame frame) { + public static Object execNop(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException { if (instr.is(0, "dbg_names")) { var names = new String[instr.params.length - 1]; for (var i = 0; i < instr.params.length - 1; i++) { - if (!(instr.params[i + 1] instanceof String)) throw EngineException.ofSyntax("NOP dbg_names instruction must specify only string parameters."); + if (!(instr.params[i + 1] instanceof String)) throw EngineException.ofSyntax(ctx, "NOP dbg_names instruction must specify only string parameters."); names[i] = (String)instr.params[i + 1]; } frame.scope.setNames(names); @@ -325,7 +325,7 @@ public class Runners { var key = frame.pop(); var val = frame.pop(); - if (!Values.deleteMember(ctx, val, key)) throw EngineException.ofSyntax("Can't delete member '" + key + "'."); + if (!Values.deleteMember(ctx, val, key)) throw EngineException.ofSyntax(ctx, "Can't delete member '" + key + "'."); frame.push(ctx, true); frame.codePtr++; return NO_RETURN; @@ -383,7 +383,7 @@ public class Runners { case OPERATION: return execOperation(ctx, instr, frame); - default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + "."); + default: throw EngineException.ofSyntax(ctx, "Invalid instruction " + instr.type.name() + "."); } } } diff --git a/src/me/topchetoeu/jscript/engine/scope/GlobalScope.java b/src/me/topchetoeu/jscript/engine/scope/GlobalScope.java index e447f2a..d505532 100644 --- a/src/me/topchetoeu/jscript/engine/scope/GlobalScope.java +++ b/src/me/topchetoeu/jscript/engine/scope/GlobalScope.java @@ -60,12 +60,12 @@ public class GlobalScope implements ScopeRecord { } public Object get(Context ctx, String name) throws InterruptedException { - if (!obj.hasMember(ctx, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist."); + if (!obj.hasMember(ctx, name, false)) throw EngineException.ofSyntax(ctx, "The variable '" + name + "' doesn't exist."); else return obj.getMember(ctx, name); } public void set(Context ctx, String name, Object val) throws InterruptedException { - if (!obj.hasMember(ctx, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist."); - if (!obj.setMember(ctx, name, val, false)) throw EngineException.ofSyntax("The global '" + name + "' is readonly."); + if (!obj.hasMember(ctx, name, false)) throw EngineException.ofSyntax(ctx, "The variable '" + name + "' doesn't exist."); + if (!obj.setMember(ctx, name, val, false)) throw EngineException.ofSyntax(ctx, "The global '" + name + "' is readonly."); } public Set keys() { diff --git a/src/me/topchetoeu/jscript/engine/values/ObjectValue.java b/src/me/topchetoeu/jscript/engine/values/ObjectValue.java index eae0dce..4eec36f 100644 --- a/src/me/topchetoeu/jscript/engine/values/ObjectValue.java +++ b/src/me/topchetoeu/jscript/engine/values/ObjectValue.java @@ -14,10 +14,6 @@ public class ObjectValue { OBJECT, ARRAY, FUNCTION, - ERROR, - SYNTAX_ERROR, - TYPE_ERROR, - RANGE_ERROR, } public static enum State { NORMAL, @@ -39,10 +35,6 @@ public class ObjectValue { private static final Object OBJ_PROTO = new Object(); private static final Object ARR_PROTO = new Object(); private static final Object FUNC_PROTO = new Object(); - private static final Object ERR_PROTO = new Object(); - private static final Object SYNTAX_ERR_PROTO = new Object(); - private static final Object TYPE_ERR_PROTO = new Object(); - private static final Object RANGE_ERR_PROTO = new Object(); protected Object prototype; @@ -150,10 +142,6 @@ public class ObjectValue { if (prototype == OBJ_PROTO) return ctx.env.proto("object"); if (prototype == ARR_PROTO) return ctx.env.proto("array"); if (prototype == FUNC_PROTO) return ctx.env.proto("function"); - if (prototype == ERR_PROTO) return ctx.env.proto("error"); - if (prototype == RANGE_ERR_PROTO) return ctx.env.proto("rangeErr"); - if (prototype == SYNTAX_ERR_PROTO) return ctx.env.proto("syntaxErr"); - if (prototype == TYPE_ERR_PROTO) return ctx.env.proto("typeErr"); } catch (NullPointerException e) { return null; @@ -176,10 +164,6 @@ public class ObjectValue { if (obj == ctx.env.proto("object")) prototype = OBJ_PROTO; else if (obj == ctx.env.proto("array")) prototype = ARR_PROTO; else if (obj == ctx.env.proto("function")) prototype = FUNC_PROTO; - else if (obj == ctx.env.proto("error")) prototype = ERR_PROTO; - else if (obj == ctx.env.proto("syntaxErr")) prototype = SYNTAX_ERR_PROTO; - else if (obj == ctx.env.proto("typeErr")) prototype = TYPE_ERR_PROTO; - else if (obj == ctx.env.proto("rangeErr")) prototype = RANGE_ERR_PROTO; else prototype = obj; } else prototype = obj; @@ -194,10 +178,6 @@ public class ObjectValue { case OBJECT: prototype = OBJ_PROTO; break; case FUNCTION: prototype = FUNC_PROTO; break; case ARRAY: prototype = ARR_PROTO; break; - case ERROR: prototype = ERR_PROTO; break; - case SYNTAX_ERROR: prototype = SYNTAX_ERR_PROTO; break; - case TYPE_ERROR: prototype = TYPE_ERR_PROTO; break; - case RANGE_ERROR: prototype = RANGE_ERR_PROTO; break; case NONE: prototype = null; break; } return true; diff --git a/src/me/topchetoeu/jscript/engine/values/Values.java b/src/me/topchetoeu/jscript/engine/values/Values.java index d909ede..ab7a962 100644 --- a/src/me/topchetoeu/jscript/engine/values/Values.java +++ b/src/me/topchetoeu/jscript/engine/values/Values.java @@ -75,7 +75,7 @@ public class Values { if (isPrimitive(res)) return res; } - throw EngineException.ofType("Value couldn't be converted to a primitive."); + throw EngineException.ofType(ctx, "Value couldn't be converted to a primitive."); } public static boolean isPrimitive(Object obj) { @@ -105,7 +105,7 @@ public class Values { } } - throw EngineException.ofType("Value couldn't be converted to a primitive."); + throw EngineException.ofType(ctx, "Value couldn't be converted to a primitive."); } public static boolean toBoolean(Object obj) { if (obj == NULL || obj == null) return false; @@ -284,8 +284,8 @@ public class Values { } public static boolean setMember(Context ctx, Object obj, Object key, Object val) throws InterruptedException { obj = normalize(ctx, obj); key = normalize(ctx, key); val = normalize(ctx, val); - if (obj == null) throw EngineException.ofType("Tried to access member of undefined."); - if (obj == NULL) throw EngineException.ofType("Tried to access member of null."); + if (obj == null) throw EngineException.ofType(ctx, "Tried to access member of undefined."); + if (obj == NULL) throw EngineException.ofType(ctx, "Tried to access member of null."); if (key.equals("__proto__")) return setPrototype(ctx, obj, val); if (isObject(obj)) return object(obj).setMember(ctx, key, val, false); @@ -373,7 +373,7 @@ public class Values { } public static Object call(Context ctx, Object func, Object thisArg, Object ...args) throws InterruptedException { - if (!isFunction(func)) throw EngineException.ofType("Tried to call a non-function value."); + if (!isFunction(func)) throw EngineException.ofType(ctx, "Tried to call a non-function value."); return function(func).call(ctx, thisArg, args); } public static Object callNew(Context ctx, Object func, Object ...args) throws InterruptedException { diff --git a/src/me/topchetoeu/jscript/exceptions/EngineException.java b/src/me/topchetoeu/jscript/exceptions/EngineException.java index e695426..93471cc 100644 --- a/src/me/topchetoeu/jscript/exceptions/EngineException.java +++ b/src/me/topchetoeu/jscript/exceptions/EngineException.java @@ -5,9 +5,8 @@ import java.util.List; import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.engine.Context; -import me.topchetoeu.jscript.engine.values.ObjectValue; +import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.Values; -import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto; public class EngineException extends RuntimeException { public final Object value; @@ -41,19 +40,28 @@ public class EngineException extends RuntimeException { catch (EngineException e) { ss.append("[Error while stringifying]\n"); } - for (var line : stackTrace) { - ss.append(" ").append(line).append('\n'); - } - if (cause != null) ss.append("Caused by ").append(cause.toString(ctx)).append('\n'); + // for (var line : stackTrace) { + // ss.append(" ").append(line).append('\n'); + // } + // if (cause != null) ss.append("Caused by ").append(cause.toString(ctx)).append('\n'); ss.deleteCharAt(ss.length() - 1); return ss.toString(); } - private static Object err(String name, String msg, PlaceholderProto proto) { - var res = new ObjectValue(proto); - if (name != null) res.defineProperty(null, "name", name); - res.defineProperty(null, "message", msg); - return res; + private static Object err(Context ctx, String type, String name, String msg) throws InterruptedException { + try { + var proto = ctx.env.proto(type); + var constr = Values.getMember(ctx, proto, "constructor"); + + if (constr instanceof FunctionValue) { + var res = Values.callNew(ctx, constr, msg); + if (name != null) Values.setMember(ctx, res, "name", name); + return res; + } + } + catch (IllegalArgumentException e) { } + + return name + ": " + msg; } public EngineException(Object error) { @@ -63,22 +71,22 @@ public class EngineException extends RuntimeException { this.cause = null; } - public static EngineException ofError(String name, String msg) { - return new EngineException(err(name, msg, PlaceholderProto.ERROR)); + public static EngineException ofError(Context ctx, String name, String msg) throws InterruptedException { + return new EngineException(err(ctx, "error", name, msg)); } - public static EngineException ofError(String msg) { - return new EngineException(err(null, msg, PlaceholderProto.ERROR)); + public static EngineException ofError(Context ctx, String msg) throws InterruptedException { + return new EngineException(err(ctx, "error", null, msg)); } - public static EngineException ofSyntax(SyntaxException e) { - return new EngineException(err(null, e.msg, PlaceholderProto.SYNTAX_ERROR)).add(null, e.loc); + public static EngineException ofSyntax(Context ctx, SyntaxException e) throws InterruptedException { + return new EngineException(err(ctx, "syntaxErr", null, e.msg)).add(null, e.loc); } - public static EngineException ofSyntax(String msg) { - return new EngineException(err(null, msg, PlaceholderProto.SYNTAX_ERROR)); + public static EngineException ofSyntax(Context ctx, String msg) throws InterruptedException { + return new EngineException(err(ctx, "syntaxErr", null, msg)); } - public static EngineException ofType(String msg) { - return new EngineException(err(null, msg, PlaceholderProto.TYPE_ERROR)); + public static EngineException ofType(Context ctx, String msg) throws InterruptedException { + return new EngineException(err(ctx, "typeErr", null, msg)); } - public static EngineException ofRange(String msg) { - return new EngineException(err(null, msg, PlaceholderProto.RANGE_ERROR)); + public static EngineException ofRange(Context ctx, String msg) throws InterruptedException { + return new EngineException(err(ctx, "rangeErr", null, msg)); } } diff --git a/src/me/topchetoeu/jscript/interop/NativeWrapperProvider.java b/src/me/topchetoeu/jscript/interop/NativeWrapperProvider.java index 018a256..3d6f1e9 100644 --- a/src/me/topchetoeu/jscript/interop/NativeWrapperProvider.java +++ b/src/me/topchetoeu/jscript/interop/NativeWrapperProvider.java @@ -20,10 +20,10 @@ public class NativeWrapperProvider implements WrappersProvider { var nat = method.getAnnotation(Native.class); var get = method.getAnnotation(NativeGetter.class); var set = method.getAnnotation(NativeSetter.class); - var memberMismatch = !Modifier.isStatic(method.getModifiers()) != member; + var memberMatch = !Modifier.isStatic(method.getModifiers()) == member; if (nat != null) { - if (nat.thisArg() != member && memberMismatch) continue; + if (nat.thisArg() && !member || !nat.thisArg() && !memberMatch) continue; Object name = nat.value(); if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2)); @@ -37,7 +37,7 @@ public class NativeWrapperProvider implements WrappersProvider { } else { if (get != null) { - if (get.thisArg() != member && memberMismatch) continue; + if (get.thisArg() && !member || !get.thisArg() && !memberMatch) continue; Object name = get.value(); if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2)); @@ -54,7 +54,7 @@ public class NativeWrapperProvider implements WrappersProvider { target.defineProperty(null, name, getter, setter, true, true); } if (set != null) { - if (set.thisArg() != member && memberMismatch) continue; + if (set.thisArg() && !member || !set.thisArg() && !memberMatch) continue; Object name = set.value(); if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2)); @@ -130,19 +130,19 @@ public class NativeWrapperProvider implements WrappersProvider { public static FunctionValue makeConstructor(Environment ctx, Class clazz) { FunctionValue func = new OverloadFunction(clazz.getName()); - for (var overload : clazz.getConstructors()) { + for (var overload : clazz.getDeclaredConstructors()) { var nat = overload.getAnnotation(Native.class); if (nat == null) continue; ((OverloadFunction)func).add(Overload.fromConstructor(overload, nat.thisArg())); } - for (var overload : clazz.getMethods()) { + for (var overload : clazz.getDeclaredMethods()) { var constr = overload.getAnnotation(NativeConstructor.class); if (constr == null) continue; ((OverloadFunction)func).add(Overload.fromMethod(overload, constr.thisArg())); } if (((OverloadFunction)func).overloads.size() == 0) { - func = new NativeFunction(clazz.getName(), (a, b, c) -> { throw EngineException.ofError("This constructor is not invokable."); }); + func = new NativeFunction(clazz.getName(), (a, b, c) -> { throw EngineException.ofError(a, "This constructor is not invokable."); }); } applyMethods(ctx, false, func, clazz); diff --git a/src/me/topchetoeu/jscript/interop/OverloadFunction.java b/src/me/topchetoeu/jscript/interop/OverloadFunction.java index bf0f20c..daf68ee 100644 --- a/src/me/topchetoeu/jscript/interop/OverloadFunction.java +++ b/src/me/topchetoeu/jscript/interop/OverloadFunction.java @@ -34,7 +34,7 @@ public class OverloadFunction extends FunctionValue { } catch (ConvertException e) { if (overloads.size() > 1) continue loop; - else throw EngineException.ofType(String.format("Argument %d can't be converted from %s to %s", i - start, e.source, e.target)); + else throw EngineException.ofType(ctx, String.format("Argument %d can't be converted from %s to %s", i - start, e.source, e.target)); } } @@ -49,7 +49,7 @@ public class OverloadFunction extends FunctionValue { } catch (ConvertException e) { if (overloads.size() > 1) continue loop; - else throw EngineException.ofType(String.format("Element in variadic argument can't be converted from %s to %s", e.source, e.target)); + else throw EngineException.ofType(ctx, String.format("Element in variadic argument can't be converted from %s to %s", e.source, e.target)); } } @@ -64,7 +64,7 @@ public class OverloadFunction extends FunctionValue { } catch (ConvertException e) { if (overloads.size() > 1) continue loop; - else throw EngineException.ofType(String.format("This argument can't be converted from %s to %s", e.source, e.target)); + else throw EngineException.ofType(ctx, String.format("This argument can't be converted from %s to %s", e.source, e.target)); } if (consumesEngine) newArgs[0] = ctx; @@ -77,7 +77,7 @@ public class OverloadFunction extends FunctionValue { return Values.normalize(ctx, overload.runner.run(ctx, _this, newArgs)); } catch (InstantiationException e) { - throw EngineException.ofError("The class may not be instantiated."); + throw EngineException.ofError(ctx, "The class may not be instantiated."); } catch (IllegalAccessException | IllegalArgumentException e) { continue; @@ -88,21 +88,21 @@ public class OverloadFunction extends FunctionValue { throw ((EngineException)e.getTargetException()).add(name, loc); } else if (e.getTargetException() instanceof NullPointerException) { - throw EngineException.ofType("Unexpected value of 'undefined'.").add(name, loc); + throw EngineException.ofType(ctx, "Unexpected value of 'undefined'.").add(name, loc); } else { - throw EngineException.ofError(e.getTargetException().getMessage()).add(name, loc); + throw EngineException.ofError(ctx, e.getTargetException().getMessage()).add(name, loc); } } catch (ReflectiveOperationException e) { - throw EngineException.ofError(e.getMessage()).add(name, new Location(0, 0, "")); + throw EngineException.ofError(ctx, e.getMessage()).add(name, new Location(0, 0, "")); } catch (Exception e) { throw e; } } - throw EngineException.ofType("No overload found for native method."); + throw EngineException.ofType(ctx, "No overload found for native method."); } public OverloadFunction add(Overload overload) { diff --git a/src/me/topchetoeu/jscript/polyfills/AsyncFunctionPolyfill.java b/src/me/topchetoeu/jscript/polyfills/AsyncFunctionPolyfill.java index 74d4996..46c024b 100644 --- a/src/me/topchetoeu/jscript/polyfills/AsyncFunctionPolyfill.java +++ b/src/me/topchetoeu/jscript/polyfills/AsyncFunctionPolyfill.java @@ -21,7 +21,7 @@ public class AsyncFunctionPolyfill extends FunctionValue { private void next(Context ctx, Object inducedValue, Object inducedError) throws InterruptedException { Object res = null; - ctx.message.pushFrame(frame); + ctx.message.pushFrame(ctx, frame); awaiting = false; while (!awaiting) { @@ -65,7 +65,7 @@ public class AsyncFunctionPolyfill extends FunctionValue { public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException { var handler = new AsyncHelper(); var func = factory.call(ctx, thisArg, new NativeFunction("await", handler::await)); - if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function."); + if (!(func instanceof CodeFunction)) throw EngineException.ofType(ctx, "Return value of argument must be a js function."); handler.frame = new CodeFrame(ctx, thisArg, args, (CodeFunction)func); handler.next(ctx, Runners.NO_RETURN, Runners.NO_RETURN); return handler.promise; diff --git a/src/me/topchetoeu/jscript/polyfills/AsyncGeneratorPolyfill.java b/src/me/topchetoeu/jscript/polyfills/AsyncGeneratorPolyfill.java index b2eb893..acf3d07 100644 --- a/src/me/topchetoeu/jscript/polyfills/AsyncGeneratorPolyfill.java +++ b/src/me/topchetoeu/jscript/polyfills/AsyncGeneratorPolyfill.java @@ -34,7 +34,7 @@ public class AsyncGeneratorPolyfill extends FunctionValue { } Object res = null; - ctx.message.pushFrame(frame); + ctx.message.pushFrame(ctx, frame); state = 0; while (state == 0) { @@ -122,7 +122,7 @@ public class AsyncGeneratorPolyfill extends FunctionValue { new NativeFunction("await", handler::await), new NativeFunction("yield", handler::yield) ); - if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function."); + if (!(func instanceof CodeFunction)) throw EngineException.ofType(ctx, "Return value of argument must be a js function."); handler.frame = new CodeFrame(ctx, thisArg, args, (CodeFunction)func); return handler; } diff --git a/src/me/topchetoeu/jscript/polyfills/ErrorPolyfill.java b/src/me/topchetoeu/jscript/polyfills/ErrorPolyfill.java new file mode 100644 index 0000000..6a530eb --- /dev/null +++ b/src/me/topchetoeu/jscript/polyfills/ErrorPolyfill.java @@ -0,0 +1,75 @@ +package me.topchetoeu.jscript.polyfills; + +import me.topchetoeu.jscript.engine.Context; +import me.topchetoeu.jscript.engine.values.ArrayValue; +import me.topchetoeu.jscript.engine.values.ObjectValue; +import me.topchetoeu.jscript.engine.values.Values; +import me.topchetoeu.jscript.interop.Native; + +public class ErrorPolyfill { + public static class SyntaxErrorPolyfill extends ErrorPolyfill { + @Native public SyntaxErrorPolyfill(Context ctx, Object message) throws InterruptedException { + super(ctx, message); + this.name = "SyntaxError"; + } + } + public static class TypeErrorPolyfill extends ErrorPolyfill { + @Native public TypeErrorPolyfill(Context ctx, Object message) throws InterruptedException { + super(ctx, message); + this.name = "TypeError"; + } + } + public static class RangeErrorPolyfill extends ErrorPolyfill { + @Native public RangeErrorPolyfill(Context ctx, Object message) throws InterruptedException { + super(ctx, message); + this.name = "RangeError"; + } + } + + @Native public final ArrayValue stack; + @Native public String message; + @Native public String name = "Error"; + + private static String toString(Context ctx, Object name, Object message, ArrayValue stack) throws InterruptedException { + if (name == null) name = ""; + else name = Values.toString(ctx, name).trim(); + if (message == null) message = ""; + else message = Values.toString(ctx, message).trim(); + StringBuilder res = new StringBuilder(); + + if (!name.equals("")) res.append(name); + if (!message.equals("") && !name.equals("")) res.append(": "); + if (!name.equals("")) res.append(message); + + if (stack != null) { + for (var el : stack) { + var str = Values.toString(ctx, el).trim(); + if (!str.equals("")) res.append("\n ").append(el); + } + } + + return res.toString(); + } + + @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) throws InterruptedException { + if (thisArg instanceof ErrorPolyfill) { + return toString(ctx, ((ErrorPolyfill)thisArg).name, ((ErrorPolyfill)thisArg).message, ((ErrorPolyfill)thisArg).stack); + } + else if (thisArg instanceof ObjectValue) { + var stack = Values.getMember(ctx, thisArg, "stack"); + if (!(stack instanceof ArrayValue)) stack = null; + return toString(ctx, + Values.getMember(ctx, thisArg, "name"), + Values.getMember(ctx, thisArg, "message"), + (ArrayValue)stack + ); + } + else return "[Invalid error]"; + } + + @Native public ErrorPolyfill(Context ctx, Object message) throws InterruptedException { + this.stack = new ArrayValue(ctx, ctx.message.stackTrace().toArray()); + if (message == null) this.message = ""; + else this.message = Values.toString(ctx, message); + } +} diff --git a/src/me/topchetoeu/jscript/polyfills/FunctionPolyfill.java b/src/me/topchetoeu/jscript/polyfills/FunctionPolyfill.java index d4ba763..9b6f00f 100644 --- a/src/me/topchetoeu/jscript/polyfills/FunctionPolyfill.java +++ b/src/me/topchetoeu/jscript/polyfills/FunctionPolyfill.java @@ -14,12 +14,12 @@ public class FunctionPolyfill { return func.call(ctx, thisArg, args.toArray()); } @Native(thisArg = true) public static Object call(Context ctx, FunctionValue func, Object thisArg, Object... args) throws InterruptedException { - if (!(func instanceof FunctionValue)) throw EngineException.ofError("Expected this to be a function."); + if (!(func instanceof FunctionValue)) throw EngineException.ofError(ctx, "Expected this to be a function."); return func.call(ctx, thisArg, 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."); + @Native(thisArg = true) public static FunctionValue bind(Context ctx, FunctionValue func, Object thisArg, Object... args) throws InterruptedException { + if (!(func instanceof FunctionValue)) throw EngineException.ofError(ctx, "Expected this to be a function."); return new NativeFunction(func.name + " (bound)", (callCtx, _0, callArgs) -> { var resArgs = new Object[args.length + callArgs.length]; diff --git a/src/me/topchetoeu/jscript/polyfills/GeneratorPolyfill.java b/src/me/topchetoeu/jscript/polyfills/GeneratorPolyfill.java index 429a2ca..ca3a35d 100644 --- a/src/me/topchetoeu/jscript/polyfills/GeneratorPolyfill.java +++ b/src/me/topchetoeu/jscript/polyfills/GeneratorPolyfill.java @@ -30,7 +30,7 @@ public class GeneratorPolyfill extends FunctionValue { } Object res = null; - ctx.message.pushFrame(frame); + ctx.message.pushFrame(ctx, frame); yielding = false; while (!yielding) { @@ -89,7 +89,7 @@ public class GeneratorPolyfill extends FunctionValue { public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException { var handler = new Generator(); var func = factory.call(ctx, thisArg, new NativeFunction("yield", handler::yield)); - if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function."); + if (!(func instanceof CodeFunction)) throw EngineException.ofType(ctx, "Return value of argument must be a js function."); handler.frame = new CodeFrame(ctx, thisArg, args, (CodeFunction)func); return handler; } diff --git a/src/me/topchetoeu/jscript/polyfills/Internals.java b/src/me/topchetoeu/jscript/polyfills/Internals.java index 5c2ceed..e53d6f5 100644 --- a/src/me/topchetoeu/jscript/polyfills/Internals.java +++ b/src/me/topchetoeu/jscript/polyfills/Internals.java @@ -10,11 +10,18 @@ import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Symbol; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.interop.Native; +import me.topchetoeu.jscript.polyfills.ErrorPolyfill.RangeErrorPolyfill; +import me.topchetoeu.jscript.polyfills.ErrorPolyfill.SyntaxErrorPolyfill; +import me.topchetoeu.jscript.polyfills.ErrorPolyfill.TypeErrorPolyfill; public class Internals { public final Environment targetEnv; - @Native public final FunctionValue object, function, promise, array, bool, number, string, symbol, map, set; + @Native public final FunctionValue + object, function, array, + bool, number, string, symbol, + promise, map, set, + error, syntax, type, range; @Native public void markSpecial(FunctionValue ...funcs) { for (var func : funcs) { @@ -163,5 +170,9 @@ public class Internals { this.symbol = targetEnv.wrappersProvider.getConstr(SymbolPolyfill.class); this.map = targetEnv.wrappersProvider.getConstr(MapPolyfill.class); this.set = targetEnv.wrappersProvider.getConstr(SetPolyfill.class); + this.error = targetEnv.wrappersProvider.getConstr(ErrorPolyfill.class); + this.syntax = targetEnv.wrappersProvider.getConstr(SyntaxErrorPolyfill.class); + this.type = targetEnv.wrappersProvider.getConstr(TypeErrorPolyfill.class); + this.range = targetEnv.wrappersProvider.getConstr(RangeErrorPolyfill.class); } } diff --git a/src/me/topchetoeu/jscript/polyfills/JSON.java b/src/me/topchetoeu/jscript/polyfills/JSON.java index 53873a5..bcf7697 100644 --- a/src/me/topchetoeu/jscript/polyfills/JSON.java +++ b/src/me/topchetoeu/jscript/polyfills/JSON.java @@ -75,7 +75,7 @@ public class JSON { return toJS(me.topchetoeu.jscript.json.JSON.parse("", val)); } catch (SyntaxException e) { - throw EngineException.ofSyntax(e.msg); + throw EngineException.ofSyntax(ctx, e.msg); } } @Native diff --git a/src/me/topchetoeu/jscript/polyfills/ObjectPolyfill.java b/src/me/topchetoeu/jscript/polyfills/ObjectPolyfill.java index 9875a74..2ce6712 100644 --- a/src/me/topchetoeu/jscript/polyfills/ObjectPolyfill.java +++ b/src/me/topchetoeu/jscript/polyfills/ObjectPolyfill.java @@ -33,27 +33,27 @@ public class ObjectPolyfill { var hasSet = attrib.hasMember(ctx, "set", false); if (hasVal) { - if (hasGet || hasSet) throw EngineException.ofType("Cannot specify a value and accessors for a property."); + if (hasGet || hasSet) throw EngineException.ofType(ctx, "Cannot specify a value and accessors for a property."); if (!obj.defineProperty( ctx, key, attrib.getMember(ctx, "value"), Values.toBoolean(attrib.getMember(ctx, "writable")), Values.toBoolean(attrib.getMember(ctx, "configurable")), Values.toBoolean(attrib.getMember(ctx, "enumerable")) - )) throw EngineException.ofType("Can't define property '" + key + "'."); + )) throw EngineException.ofType(ctx, "Can't define property '" + key + "'."); } else { var get = attrib.getMember(ctx, "get"); var set = attrib.getMember(ctx, "set"); - if (get != null && !(get instanceof FunctionValue)) throw EngineException.ofType("Get accessor must be a function."); - if (set != null && !(set instanceof FunctionValue)) throw EngineException.ofType("Set accessor must be a function."); + if (get != null && !(get instanceof FunctionValue)) throw EngineException.ofType(ctx, "Get accessor must be a function."); + if (set != null && !(set instanceof FunctionValue)) throw EngineException.ofType(ctx, "Set accessor must be a function."); if (!obj.defineProperty( ctx, key, (FunctionValue)get, (FunctionValue)set, Values.toBoolean(attrib.getMember(ctx, "configurable")), Values.toBoolean(attrib.getMember(ctx, "enumerable")) - )) throw EngineException.ofType("Can't define property '" + key + "'."); + )) throw EngineException.ofType(ctx, "Can't define property '" + key + "'."); } return obj; diff --git a/src/me/topchetoeu/jscript/polyfills/PromisePolyfill.java b/src/me/topchetoeu/jscript/polyfills/PromisePolyfill.java index 41e977d..dab4f57 100644 --- a/src/me/topchetoeu/jscript/polyfills/PromisePolyfill.java +++ b/src/me/topchetoeu/jscript/polyfills/PromisePolyfill.java @@ -44,7 +44,7 @@ public class PromisePolyfill { } @Native public static PromisePolyfill any(Context ctx, Object _promises) throws InterruptedException { - if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); + if (!Values.isArray(_promises)) throw EngineException.ofType(ctx, "Expected argument for any to be an array."); var promises = Values.array(_promises); if (promises.size() == 0) return ofResolved(ctx, new ArrayValue()); var n = new int[] { promises.size() }; @@ -69,7 +69,7 @@ public class PromisePolyfill { return res; } @Native public static PromisePolyfill race(Context ctx, Object _promises) throws InterruptedException { - if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); + if (!Values.isArray(_promises)) throw EngineException.ofType(ctx, "Expected argument for any to be an array."); var promises = Values.array(_promises); if (promises.size() == 0) return ofResolved(ctx, new ArrayValue()); var res = new PromisePolyfill(); @@ -85,7 +85,7 @@ public class PromisePolyfill { return res; } @Native public static PromisePolyfill all(Context ctx, Object _promises) throws InterruptedException { - if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); + if (!Values.isArray(_promises)) throw EngineException.ofType(ctx, "Expected argument for any to be an array."); var promises = Values.array(_promises); if (promises.size() == 0) return ofResolved(ctx, new ArrayValue()); var n = new int[] { promises.size() }; @@ -112,7 +112,7 @@ public class PromisePolyfill { return res; } @Native public static PromisePolyfill allSettled(Context ctx, Object _promises) throws InterruptedException { - if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); + if (!Values.isArray(_promises)) throw EngineException.ofType(ctx, "Expected argument for any to be an array."); var promises = Values.array(_promises); if (promises.size() == 0) return ofResolved(ctx, new ArrayValue()); var n = new int[] { promises.size() }; @@ -314,7 +314,7 @@ public class PromisePolyfill { * NOT THREAD SAFE - must be called from the engine executor thread */ @Native public PromisePolyfill(Context ctx, FunctionValue func) throws InterruptedException { - if (!(func instanceof FunctionValue)) throw EngineException.ofType("A function must be passed to the promise constructor."); + if (!(func instanceof FunctionValue)) throw EngineException.ofType(ctx, "A function must be passed to the promise constructor."); try { func.call( ctx, null, diff --git a/src/me/topchetoeu/jscript/polyfills/StringPolyfill.java b/src/me/topchetoeu/jscript/polyfills/StringPolyfill.java index 2068481..51255d6 100644 --- a/src/me/topchetoeu/jscript/polyfills/StringPolyfill.java +++ b/src/me/topchetoeu/jscript/polyfills/StringPolyfill.java @@ -18,10 +18,10 @@ public class StringPolyfill { public final String value; - private static String passThis(String funcName, Object val) { + private static String passThis(Context ctx, String funcName, Object val) throws InterruptedException { if (val instanceof StringPolyfill) return ((StringPolyfill)val).value; else if (val instanceof String) return (String)val; - else throw EngineException.ofType(String.format("'%s' may only be called upon object and primitve strings.", funcName)); + else throw EngineException.ofType(ctx, String.format("'%s' may only be called upon object and primitve strings.", funcName)); } private static int normalizeI(int i, int len, boolean clamp) { if (i < 0) i += len; @@ -32,47 +32,47 @@ public class StringPolyfill { return i; } - @NativeGetter(thisArg = true) public static int length(Context ctx, Object thisArg) { - return passThis("substring", thisArg).length(); + @NativeGetter(thisArg = true) public static int length(Context ctx, Object thisArg) throws InterruptedException { + return passThis(ctx, "substring", thisArg).length(); } @Native(thisArg = true) public static String substring(Context ctx, Object thisArg, int start, Object _end) throws InterruptedException { - var val = passThis("substring", thisArg); + var val = passThis(ctx, "substring", thisArg); start = normalizeI(start, val.length(), true); int end = normalizeI(_end == null ? val.length() : (int)Values.toNumber(ctx, _end), val.length(), true); return val.substring(start, end); } @Native(thisArg = true) public static String substr(Context ctx, Object thisArg, int start, Object _len) throws InterruptedException { - var val = passThis("substr", thisArg); + var val = passThis(ctx, "substr", thisArg); int len = _len == null ? val.length() - start : (int)Values.toNumber(ctx, _len); return substring(ctx, val, start, start + len); } - @Native(thisArg = true) public static String toLowerCase(Context ctx, Object thisArg) { - return passThis("toLowerCase", thisArg).toLowerCase(); + @Native(thisArg = true) public static String toLowerCase(Context ctx, Object thisArg) throws InterruptedException { + return passThis(ctx, "toLowerCase", thisArg).toLowerCase(); } - @Native(thisArg = true) public static String toUpperCase(Context ctx, Object thisArg) { - return passThis("toUpperCase", thisArg).toUpperCase(); + @Native(thisArg = true) public static String toUpperCase(Context ctx, Object thisArg) throws InterruptedException { + return passThis(ctx, "toUpperCase", thisArg).toUpperCase(); } - @Native(thisArg = true) public static String charAt(Context ctx, Object thisArg, int i) { - return passThis("charAt", thisArg).charAt(i) + ""; + @Native(thisArg = true) public static String charAt(Context ctx, Object thisArg, int i) throws InterruptedException { + return passThis(ctx, "charAt", thisArg).charAt(i) + ""; } - @Native(thisArg = true) public static int charCodeAt(Context ctx, Object thisArg, int i) { - return passThis("charCodeAt", thisArg).charAt(i); + @Native(thisArg = true) public static int charCodeAt(Context ctx, Object thisArg, int i) throws InterruptedException { + return passThis(ctx, "charCodeAt", thisArg).charAt(i); } - @Native(thisArg = true) public static boolean startsWith(Context ctx, Object thisArg, String term, int pos) { - return passThis("startsWith", thisArg).startsWith(term, pos); + @Native(thisArg = true) public static boolean startsWith(Context ctx, Object thisArg, String term, int pos) throws InterruptedException { + return passThis(ctx, "startsWith", thisArg).startsWith(term, pos); } @Native(thisArg = true) public static boolean endsWith(Context ctx, Object thisArg, String term, int pos) throws InterruptedException { - var val = passThis("endsWith", thisArg); + var val = passThis(ctx, "endsWith", thisArg); return val.lastIndexOf(term, pos) >= 0; } @Native(thisArg = true) public static int indexOf(Context ctx, Object thisArg, Object term, int start) throws InterruptedException { - var val = passThis("indexOf", thisArg); + var val = passThis(ctx, "indexOf", thisArg); if (term != null && term != Values.NULL && !(term instanceof String)) { var search = Values.getMember(ctx, term, ctx.env.symbol("Symbol.search")); @@ -84,7 +84,7 @@ public class StringPolyfill { 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); + var val = passThis(ctx, "lastIndexOf", thisArg); if (term != null && term != Values.NULL && !(term instanceof String)) { var search = Values.getMember(ctx, term, ctx.env.symbol("Symbol.search")); @@ -97,11 +97,11 @@ public class StringPolyfill { } @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; + return lastIndexOf(ctx, passThis(ctx, "includes", thisArg), term, pos) >= 0; } @Native(thisArg = true) public static String replace(Context ctx, Object thisArg, Object term, String replacement) throws InterruptedException { - var val = passThis("replace", thisArg); + var val = passThis(ctx, "replace", thisArg); if (term != null && term != Values.NULL && !(term instanceof String)) { var replace = Values.getMember(ctx, term, ctx.env.symbol("Symbol.replace")); @@ -113,7 +113,7 @@ public class StringPolyfill { 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); + var val = passThis(ctx, "replaceAll", thisArg); if (term != null && term != Values.NULL && !(term instanceof String)) { var replace = Values.getMember(ctx, term, ctx.env.symbol("Symbol.replace")); @@ -126,7 +126,7 @@ public class StringPolyfill { } @Native(thisArg = true) public static ArrayValue match(Context ctx, Object thisArg, Object term, String replacement) throws InterruptedException { - var val = passThis("match", thisArg); + var val = passThis(ctx, "match", thisArg); FunctionValue match; @@ -137,9 +137,9 @@ public class StringPolyfill { 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(ctx, "Regular expressions don't support matching."); } - else throw EngineException.ofError("Regular expressions not supported."); + else throw EngineException.ofError(ctx, "Regular expressions not supported."); } catch (IllegalArgumentException e) { return new ArrayValue(ctx, ""); } @@ -148,7 +148,7 @@ public class StringPolyfill { 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); + var val = passThis(ctx, "matchAll", thisArg); FunctionValue match = null; @@ -162,15 +162,15 @@ public class StringPolyfill { 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(ctx, "Regular expressions don't support matching."); } - else throw EngineException.ofError("Regular expressions not supported."); + else throw EngineException.ofError(ctx, "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); + var val = passThis(ctx, "split", thisArg); if (lim != null) lim = Values.toNumber(ctx, lim); @@ -217,18 +217,18 @@ public class StringPolyfill { } @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); + return substring(ctx, passThis(ctx, "slice", thisArg), start, _end); } @Native(thisArg = true) public static String concat(Context ctx, Object thisArg, Object... args) throws InterruptedException { - var res = new StringBuilder(passThis("concat", thisArg)); + var res = new StringBuilder(passThis(ctx, "concat", thisArg)); for (var el : args) res.append(Values.toString(ctx, el)); return res.toString(); } @Native(thisArg = true) public static String trim(Context ctx, Object thisArg) throws InterruptedException { - return passThis("trim", thisArg).trim(); + return passThis(ctx, "trim", thisArg).trim(); } @NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object val) throws InterruptedException { @@ -237,10 +237,10 @@ public class StringPolyfill { else return val; } @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) throws InterruptedException { - return passThis("toString", thisArg); + return passThis(ctx, "toString", thisArg); } @Native(thisArg = true) public static String valueOf(Context ctx, Object thisArg) throws InterruptedException { - return passThis("valueOf", thisArg); + return passThis(ctx, "valueOf", thisArg); } @Native public static String fromCharCode(int ...val) { diff --git a/src/me/topchetoeu/jscript/polyfills/SymbolPolyfill.java b/src/me/topchetoeu/jscript/polyfills/SymbolPolyfill.java index 8671c4d..67ad078 100644 --- a/src/me/topchetoeu/jscript/polyfills/SymbolPolyfill.java +++ b/src/me/topchetoeu/jscript/polyfills/SymbolPolyfill.java @@ -27,28 +27,22 @@ public class SymbolPolyfill { public final Symbol value; - private static Symbol passThis(String funcName, Object val) { + private static Symbol passThis(Context ctx, String funcName, Object val) throws InterruptedException { if (val instanceof SymbolPolyfill) return ((SymbolPolyfill)val).value; else if (val instanceof Symbol) return (Symbol)val; - else throw EngineException.ofType(String.format("'%s' may only be called upon object and primitve symbols.", funcName)); + else throw EngineException.ofType(ctx, String.format("'%s' may only be called upon object and primitve symbols.", funcName)); } @NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object val) throws InterruptedException { - if (thisArg instanceof ObjectValue) throw EngineException.ofType("Symbol constructor may not be called with new."); + if (thisArg instanceof ObjectValue) throw EngineException.ofType(ctx, "Symbol constructor may not be called with new."); if (val == null) return new Symbol(""); else return new Symbol(Values.toString(ctx, val)); } @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) throws InterruptedException { - return passThis("toString", thisArg).value; + return passThis(ctx, "toString", thisArg).value; } @Native(thisArg = true) public static Symbol valueOf(Context ctx, Object thisArg) throws InterruptedException { - return passThis("valueOf", thisArg); - } - - @Native public static String fromCharCode(int ...val) { - char[] arr = new char[val.length]; - for (var i = 0; i < val.length; i++) arr[i] = (char)val[i]; - return new String(arr); + return passThis(ctx, "valueOf", thisArg); } @Native("for") public static Symbol _for(String key) {