Core library reprogramming #5

Merged
TopchetoEU merged 23 commits from TopchetoEU/corelib-reprogramming into master 2023-10-04 05:50:26 +00:00
25 changed files with 313 additions and 239 deletions
Showing only changes of commit 22ec95a7b5 - Show all commits

View File

@ -54,7 +54,7 @@ async function compileJava() {
.replace('${AUTHOR}', conf.author) .replace('${AUTHOR}', conf.author)
); );
const args = ['--release', '11', ]; 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'); 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); for await (const path of find('src', undefined, v => v.endsWith('.java') && !v.endsWith('Metadata.java'))) args.push(path);

View File

@ -13,6 +13,10 @@ interface Internals {
number: NumberConstructor; number: NumberConstructor;
string: StringConstructor; string: StringConstructor;
symbol: SymbolConstructor; symbol: SymbolConstructor;
error: ErrorConstructor;
syntax: SyntaxErrorConstructor;
type: TypeErrorConstructor;
range: RangeErrorConstructor;
map: typeof Map; map: typeof Map;
set: typeof Set; set: typeof Set;
@ -57,6 +61,10 @@ try {
const Number = env.global.Number = internals.number; const Number = env.global.Number = internals.number;
const String = env.global.String = internals.string; const String = env.global.String = internals.string;
const Symbol = env.global.Symbol = internals.symbol; 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 Map = env.global.Map = internals.map;
const Set = env.global.Set = internals.set; const Set = env.global.Set = internals.set;
@ -69,12 +77,16 @@ try {
env.setProto('symbol', Symbol.prototype); env.setProto('symbol', Symbol.prototype);
env.setProto('bool', Boolean.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; (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('values/errors');
run('regex'); run('regex');
run('timeout'); run('timeout');

View File

@ -3,7 +3,6 @@
"lib.d.ts", "lib.d.ts",
"modules.ts", "modules.ts",
"utils.ts", "utils.ts",
"values/errors.ts",
"regex.ts", "regex.ts",
"timeout.ts", "timeout.ts",
"core.ts" "core.ts"

View File

@ -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<T1 extends ErrorConstructor>(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');
});

View File

@ -17,14 +17,12 @@ public class Environment {
private HashMap<String, ObjectValue> prototypes = new HashMap<>(); private HashMap<String, ObjectValue> prototypes = new HashMap<>();
public GlobalScope global; public GlobalScope global;
public WrappersProvider wrappersProvider; 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<String, Symbol> symbols = new HashMap<>(); public HashMap<String, Symbol> symbols = new HashMap<>();
@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(ctx, "Regular expressions not supported.");
}); });
@Native public ObjectValue proto(String name) { @Native public ObjectValue proto(String name) {
return prototypes.get(name); return prototypes.get(name);
@ -43,37 +41,20 @@ public class Environment {
} }
} }
// @Native public ObjectValue arrayPrototype = new ObjectValue(); @NativeGetter("global") public ObjectValue getGlobal() {
// @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() {
return global.obj; return global.obj;
} }
@NativeSetter("global") @NativeSetter("global") public void setGlobal(ObjectValue val) {
public void setGlobal(ObjectValue val) {
global = new GlobalScope(val); global = new GlobalScope(val);
} }
@Native @Native public Environment fork() {
public Environment fork() {
var res = new Environment(compile, wrappersProvider, global); var res = new Environment(compile, wrappersProvider, global);
res.regexConstructor = regexConstructor; res.regexConstructor = regexConstructor;
res.prototypes = new HashMap<>(prototypes); res.prototypes = new HashMap<>(prototypes);
return res; return res;
} }
@Native public Environment child() {
@Native
public Environment child() {
var res = fork(); var res = fork();
res.global = res.global.globalChild(); res.global = res.global.globalChild();
return res; return res;

View File

@ -15,9 +15,9 @@ public class MessageContext {
public List<CodeFrame> frames() { return Collections.unmodifiableList(frames); } public List<CodeFrame> frames() { return Collections.unmodifiableList(frames); }
public MessageContext pushFrame(CodeFrame frame) { public MessageContext pushFrame(Context ctx, CodeFrame frame) throws InterruptedException {
this.frames.add(frame); 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; return this;
} }
public boolean popFrame(CodeFrame frame) { public boolean popFrame(CodeFrame frame) {
@ -27,6 +27,25 @@ public class MessageContext {
return true; return true;
} }
public List<String> stackTrace() {
var res = new ArrayList<String>();
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) { public MessageContext(Engine engine) {
this.engine = engine; this.engine = engine;
} }

View File

@ -9,6 +9,7 @@ import me.topchetoeu.jscript.engine.scope.LocalScope;
import me.topchetoeu.jscript.engine.scope.ValueVariable; import me.topchetoeu.jscript.engine.scope.ValueVariable;
import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.CodeFunction; 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.engine.values.Values;
import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.exceptions.EngineException;
@ -93,6 +94,9 @@ public class CodeFrame {
stack[stackPtr++] = Values.normalize(ctx, val); stack[stackPtr++] = Values.normalize(ctx, val);
} }
// TODO: THIS SYSTEM IS SEVERLY BROKEN
// MUST FIX!!!!!
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();
if (codePtr < 0 || codePtr >= function.body.length) return null; 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 { public Object next(Context ctx, Object prevValue, Object prevReturn, Object prevError) throws InterruptedException {
TryCtx tryCtx = null; TryCtx tryCtx = null;
if (prevError != Runners.NO_RETURN) prevReturn = Runners.NO_RETURN; if (prevError != Runners.NO_RETURN) prevReturn = Runners.NO_RETURN;
@ -210,18 +266,8 @@ public class CodeFrame {
else return res; else return res;
} }
catch (EngineException e) { catch (EngineException e) {
if (tryCtx.hasCatch) { throw e;
tryCtx.state = TryCtx.STATE_CATCH; // propagateErr(ctx, e.value);
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;
} }
codePtr = tryCtx.finallyStart; codePtr = tryCtx.finallyStart;
@ -237,7 +283,7 @@ public class CodeFrame {
else return res; else return res;
} }
catch (EngineException e) { catch (EngineException e) {
e.cause = tryCtx.err; setCause(ctx, e.value, tryCtx.err);
if (tryCtx.hasFinally) { if (tryCtx.hasFinally) {
tryCtx.err = e; tryCtx.err = e;
tryCtx.state = TryCtx.STATE_FINALLY_THREW; tryCtx.state = TryCtx.STATE_FINALLY_THREW;
@ -253,7 +299,7 @@ public class CodeFrame {
return nextNoTry(ctx); return nextNoTry(ctx);
} }
catch (EngineException e) { catch (EngineException e) {
e.cause = tryCtx.err; setCause(ctx, e.value, tryCtx.err);
throw e; throw e;
} }
} }
@ -262,16 +308,12 @@ public class CodeFrame {
public Object run(Context ctx) throws InterruptedException { public Object run(Context ctx) throws InterruptedException {
try { try {
ctx.message.pushFrame(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, Runners.NO_RETURN);
if (res != Runners.NO_RETURN) return res; if (res != Runners.NO_RETURN) return res;
} }
} }
catch (Throwable e) {
// e.printStackTrace();
throw e;
}
finally { finally {
ctx.message.popFrame(this); ctx.message.popFrame(this);
} }

View File

@ -26,8 +26,8 @@ public class Runners {
public static Object execThrow(Context ctx, Instruction instr, CodeFrame frame) { public static Object execThrow(Context ctx, Instruction instr, CodeFrame frame) {
throw new EngineException(frame.pop()); throw new EngineException(frame.pop());
} }
public static Object execThrowSyntax(Context ctx, Instruction instr, CodeFrame frame) { public static Object execThrowSyntax(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
throw EngineException.ofSyntax((String)instr.get(0)); throw EngineException.ofSyntax(ctx, (String)instr.get(0));
} }
private static Object call(Context ctx, Object func, Object thisArg, Object ...args) throws InterruptedException { 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 name = frame.pop();
var obj = frame.pop(); var obj = frame.pop();
if (getter != null && !Values.isFunction(getter)) throw EngineException.ofType("Getter must be a function or undefined."); 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("Setter 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("Property apply target must be an object."); 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); Values.object(obj).defineProperty(ctx, name, Values.function(getter), Values.function(setter), false, false);
frame.push(ctx, obj); frame.push(ctx, obj);
@ -214,7 +214,7 @@ public class Runners {
frame.push(ctx, Values.getMember(ctx, obj, key)); frame.push(ctx, Values.getMember(ctx, obj, key));
} }
catch (IllegalArgumentException e) { catch (IllegalArgumentException e) {
throw EngineException.ofType(e.getMessage()); throw EngineException.ofType(ctx, e.getMessage());
} }
frame.codePtr++; frame.codePtr++;
return NO_RETURN; return NO_RETURN;
@ -239,7 +239,7 @@ public class Runners {
var key = frame.pop(); var key = frame.pop();
var obj = 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); if ((boolean)instr.get(0)) frame.push(ctx, val);
frame.codePtr++; frame.codePtr++;
return NO_RETURN; return NO_RETURN;
@ -307,11 +307,11 @@ public class Runners {
frame.codePtr++; frame.codePtr++;
return NO_RETURN; 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")) { if (instr.is(0, "dbg_names")) {
var names = new String[instr.params.length - 1]; var names = new String[instr.params.length - 1];
for (var i = 0; i < instr.params.length - 1; i++) { 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]; names[i] = (String)instr.params[i + 1];
} }
frame.scope.setNames(names); frame.scope.setNames(names);
@ -325,7 +325,7 @@ public class Runners {
var key = frame.pop(); var key = frame.pop();
var val = 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.push(ctx, true);
frame.codePtr++; frame.codePtr++;
return NO_RETURN; return NO_RETURN;
@ -383,7 +383,7 @@ public class Runners {
case OPERATION: return execOperation(ctx, instr, frame); 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() + ".");
} }
} }
} }

View File

@ -60,12 +60,12 @@ public class GlobalScope implements ScopeRecord {
} }
public Object get(Context ctx, String name) throws InterruptedException { 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); else return obj.getMember(ctx, name);
} }
public void set(Context ctx, String name, Object val) throws InterruptedException { 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.hasMember(ctx, name, false)) throw EngineException.ofSyntax(ctx, "The variable '" + name + "' doesn't exist.");
if (!obj.setMember(ctx, name, val, false)) throw EngineException.ofSyntax("The global '" + name + "' is readonly."); if (!obj.setMember(ctx, name, val, false)) throw EngineException.ofSyntax(ctx, "The global '" + name + "' is readonly.");
} }
public Set<String> keys() { public Set<String> keys() {

View File

@ -14,10 +14,6 @@ public class ObjectValue {
OBJECT, OBJECT,
ARRAY, ARRAY,
FUNCTION, FUNCTION,
ERROR,
SYNTAX_ERROR,
TYPE_ERROR,
RANGE_ERROR,
} }
public static enum State { public static enum State {
NORMAL, NORMAL,
@ -39,10 +35,6 @@ public class ObjectValue {
private static final Object OBJ_PROTO = new Object(); private static final Object OBJ_PROTO = new Object();
private static final Object ARR_PROTO = new Object(); private static final Object ARR_PROTO = new Object();
private static final Object FUNC_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; protected Object prototype;
@ -150,10 +142,6 @@ public class ObjectValue {
if (prototype == OBJ_PROTO) return ctx.env.proto("object"); if (prototype == OBJ_PROTO) return ctx.env.proto("object");
if (prototype == ARR_PROTO) return ctx.env.proto("array"); if (prototype == ARR_PROTO) return ctx.env.proto("array");
if (prototype == FUNC_PROTO) return ctx.env.proto("function"); 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) { catch (NullPointerException e) {
return null; return null;
@ -176,10 +164,6 @@ public class ObjectValue {
if (obj == ctx.env.proto("object")) prototype = OBJ_PROTO; 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("array")) prototype = ARR_PROTO;
else if (obj == ctx.env.proto("function")) prototype = FUNC_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;
} }
else prototype = obj; else prototype = obj;
@ -194,10 +178,6 @@ public class ObjectValue {
case OBJECT: prototype = OBJ_PROTO; break; case OBJECT: prototype = OBJ_PROTO; break;
case FUNCTION: prototype = FUNC_PROTO; break; case FUNCTION: prototype = FUNC_PROTO; break;
case ARRAY: prototype = ARR_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; case NONE: prototype = null; break;
} }
return true; return true;

View File

@ -75,7 +75,7 @@ public class Values {
if (isPrimitive(res)) return res; 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) { 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) { public static boolean toBoolean(Object obj) {
if (obj == NULL || obj == null) return false; 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 { 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); 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(ctx, "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 null.");
if (key.equals("__proto__")) return setPrototype(ctx, obj, val); if (key.equals("__proto__")) return setPrototype(ctx, obj, val);
if (isObject(obj)) return object(obj).setMember(ctx, key, val, false); 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 { 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); return function(func).call(ctx, thisArg, args);
} }
public static Object callNew(Context ctx, Object func, Object ...args) throws InterruptedException { public static Object callNew(Context ctx, Object func, Object ...args) throws InterruptedException {

View File

@ -5,9 +5,8 @@ import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.Context; 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.Values;
import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto;
public class EngineException extends RuntimeException { public class EngineException extends RuntimeException {
public final Object value; public final Object value;
@ -41,19 +40,28 @@ public class EngineException extends RuntimeException {
catch (EngineException e) { catch (EngineException e) {
ss.append("[Error while stringifying]\n"); ss.append("[Error while stringifying]\n");
} }
for (var line : stackTrace) { // for (var line : stackTrace) {
ss.append(" ").append(line).append('\n'); // ss.append(" ").append(line).append('\n');
} // }
if (cause != null) ss.append("Caused by ").append(cause.toString(ctx)).append('\n'); // if (cause != null) ss.append("Caused by ").append(cause.toString(ctx)).append('\n');
ss.deleteCharAt(ss.length() - 1); ss.deleteCharAt(ss.length() - 1);
return ss.toString(); return ss.toString();
} }
private static Object err(String name, String msg, PlaceholderProto proto) { private static Object err(Context ctx, String type, String name, String msg) throws InterruptedException {
var res = new ObjectValue(proto); try {
if (name != null) res.defineProperty(null, "name", name); var proto = ctx.env.proto(type);
res.defineProperty(null, "message", msg); var constr = Values.getMember(ctx, proto, "constructor");
return res;
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) { public EngineException(Object error) {
@ -63,22 +71,22 @@ public class EngineException extends RuntimeException {
this.cause = null; this.cause = null;
} }
public static EngineException ofError(String name, String msg) { public static EngineException ofError(Context ctx, String name, String msg) throws InterruptedException {
return new EngineException(err(name, msg, PlaceholderProto.ERROR)); return new EngineException(err(ctx, "error", name, msg));
} }
public static EngineException ofError(String msg) { public static EngineException ofError(Context ctx, String msg) throws InterruptedException {
return new EngineException(err(null, msg, PlaceholderProto.ERROR)); return new EngineException(err(ctx, "error", null, msg));
} }
public static EngineException ofSyntax(SyntaxException e) { public static EngineException ofSyntax(Context ctx, SyntaxException e) throws InterruptedException {
return new EngineException(err(null, e.msg, PlaceholderProto.SYNTAX_ERROR)).add(null, e.loc); return new EngineException(err(ctx, "syntaxErr", null, e.msg)).add(null, e.loc);
} }
public static EngineException ofSyntax(String msg) { public static EngineException ofSyntax(Context ctx, String msg) throws InterruptedException {
return new EngineException(err(null, msg, PlaceholderProto.SYNTAX_ERROR)); return new EngineException(err(ctx, "syntaxErr", null, msg));
} }
public static EngineException ofType(String msg) { public static EngineException ofType(Context ctx, String msg) throws InterruptedException {
return new EngineException(err(null, msg, PlaceholderProto.TYPE_ERROR)); return new EngineException(err(ctx, "typeErr", null, msg));
} }
public static EngineException ofRange(String msg) { public static EngineException ofRange(Context ctx, String msg) throws InterruptedException {
return new EngineException(err(null, msg, PlaceholderProto.RANGE_ERROR)); return new EngineException(err(ctx, "rangeErr", null, msg));
} }
} }

View File

@ -20,10 +20,10 @@ public class NativeWrapperProvider implements WrappersProvider {
var nat = method.getAnnotation(Native.class); var nat = method.getAnnotation(Native.class);
var get = method.getAnnotation(NativeGetter.class); var get = method.getAnnotation(NativeGetter.class);
var set = method.getAnnotation(NativeSetter.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 != null) {
if (nat.thisArg() != member && memberMismatch) continue; if (nat.thisArg() && !member || !nat.thisArg() && !memberMatch) continue;
Object name = nat.value(); Object name = nat.value();
if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2)); if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2));
@ -37,7 +37,7 @@ public class NativeWrapperProvider implements WrappersProvider {
} }
else { else {
if (get != null) { if (get != null) {
if (get.thisArg() != member && memberMismatch) continue; if (get.thisArg() && !member || !get.thisArg() && !memberMatch) continue;
Object name = get.value(); Object name = get.value();
if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2)); 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); target.defineProperty(null, name, getter, setter, true, true);
} }
if (set != null) { if (set != null) {
if (set.thisArg() != member && memberMismatch) continue; if (set.thisArg() && !member || !set.thisArg() && !memberMatch) continue;
Object name = set.value(); Object name = set.value();
if (((String)name).startsWith("@@")) name = env.symbol(((String)name).substring(2)); 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) { public static FunctionValue makeConstructor(Environment ctx, Class<?> clazz) {
FunctionValue func = new OverloadFunction(clazz.getName()); FunctionValue func = new OverloadFunction(clazz.getName());
for (var overload : clazz.getConstructors()) { for (var overload : clazz.getDeclaredConstructors()) {
var nat = overload.getAnnotation(Native.class); var nat = overload.getAnnotation(Native.class);
if (nat == null) continue; if (nat == null) continue;
((OverloadFunction)func).add(Overload.fromConstructor(overload, nat.thisArg())); ((OverloadFunction)func).add(Overload.fromConstructor(overload, nat.thisArg()));
} }
for (var overload : clazz.getMethods()) { for (var overload : clazz.getDeclaredMethods()) {
var constr = overload.getAnnotation(NativeConstructor.class); var constr = overload.getAnnotation(NativeConstructor.class);
if (constr == null) continue; if (constr == null) continue;
((OverloadFunction)func).add(Overload.fromMethod(overload, constr.thisArg())); ((OverloadFunction)func).add(Overload.fromMethod(overload, constr.thisArg()));
} }
if (((OverloadFunction)func).overloads.size() == 0) { 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); applyMethods(ctx, false, func, clazz);

View File

@ -34,7 +34,7 @@ public class OverloadFunction extends FunctionValue {
} }
catch (ConvertException e) { catch (ConvertException e) {
if (overloads.size() > 1) continue loop; 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) { catch (ConvertException e) {
if (overloads.size() > 1) continue loop; 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) { catch (ConvertException e) {
if (overloads.size() > 1) continue loop; 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; if (consumesEngine) newArgs[0] = ctx;
@ -77,7 +77,7 @@ public class OverloadFunction extends FunctionValue {
return Values.normalize(ctx, overload.runner.run(ctx, _this, newArgs)); return Values.normalize(ctx, overload.runner.run(ctx, _this, newArgs));
} }
catch (InstantiationException e) { 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) { catch (IllegalAccessException | IllegalArgumentException e) {
continue; continue;
@ -88,21 +88,21 @@ public class OverloadFunction extends FunctionValue {
throw ((EngineException)e.getTargetException()).add(name, loc); throw ((EngineException)e.getTargetException()).add(name, loc);
} }
else if (e.getTargetException() instanceof NullPointerException) { 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 { else {
throw EngineException.ofError(e.getTargetException().getMessage()).add(name, loc); throw EngineException.ofError(ctx, e.getTargetException().getMessage()).add(name, loc);
} }
} }
catch (ReflectiveOperationException e) { catch (ReflectiveOperationException e) {
throw EngineException.ofError(e.getMessage()).add(name, new Location(0, 0, "<internal>")); throw EngineException.ofError(ctx, e.getMessage()).add(name, new Location(0, 0, "<internal>"));
} }
catch (Exception e) { catch (Exception e) {
throw 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) { public OverloadFunction add(Overload overload) {

View File

@ -21,7 +21,7 @@ public class AsyncFunctionPolyfill extends FunctionValue {
private void next(Context ctx, Object inducedValue, Object inducedError) throws InterruptedException { private void next(Context ctx, Object inducedValue, Object inducedError) throws InterruptedException {
Object res = null; Object res = null;
ctx.message.pushFrame(frame); ctx.message.pushFrame(ctx, frame);
awaiting = false; awaiting = false;
while (!awaiting) { while (!awaiting) {
@ -65,7 +65,7 @@ public class AsyncFunctionPolyfill extends FunctionValue {
public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException { public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
var handler = new AsyncHelper(); var handler = new AsyncHelper();
var func = factory.call(ctx, thisArg, new NativeFunction("await", handler::await)); 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.frame = new CodeFrame(ctx, thisArg, args, (CodeFunction)func);
handler.next(ctx, Runners.NO_RETURN, Runners.NO_RETURN); handler.next(ctx, Runners.NO_RETURN, Runners.NO_RETURN);
return handler.promise; return handler.promise;

View File

@ -34,7 +34,7 @@ public class AsyncGeneratorPolyfill extends FunctionValue {
} }
Object res = null; Object res = null;
ctx.message.pushFrame(frame); ctx.message.pushFrame(ctx, frame);
state = 0; state = 0;
while (state == 0) { while (state == 0) {
@ -122,7 +122,7 @@ public class AsyncGeneratorPolyfill extends FunctionValue {
new NativeFunction("await", handler::await), new NativeFunction("await", handler::await),
new NativeFunction("yield", handler::yield) 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); handler.frame = new CodeFrame(ctx, thisArg, args, (CodeFunction)func);
return handler; return handler;
} }

View File

@ -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);
}
}

View File

@ -14,12 +14,12 @@ public class FunctionPolyfill {
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 {
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); return func.call(ctx, thisArg, args);
} }
@Native(thisArg = true) public static FunctionValue bind(Context ctx, FunctionValue func, Object thisArg, Object... args) { @Native(thisArg = true) public static FunctionValue bind(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 new NativeFunction(func.name + " (bound)", (callCtx, _0, callArgs) -> { return new NativeFunction(func.name + " (bound)", (callCtx, _0, callArgs) -> {
var resArgs = new Object[args.length + callArgs.length]; var resArgs = new Object[args.length + callArgs.length];

View File

@ -30,7 +30,7 @@ public class GeneratorPolyfill extends FunctionValue {
} }
Object res = null; Object res = null;
ctx.message.pushFrame(frame); ctx.message.pushFrame(ctx, frame);
yielding = false; yielding = false;
while (!yielding) { while (!yielding) {
@ -89,7 +89,7 @@ public class GeneratorPolyfill extends FunctionValue {
public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException { public Object call(Context ctx, Object thisArg, Object ...args) throws InterruptedException {
var handler = new Generator(); var handler = new Generator();
var func = factory.call(ctx, thisArg, new NativeFunction("yield", handler::yield)); 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); handler.frame = new CodeFrame(ctx, thisArg, args, (CodeFunction)func);
return handler; return handler;
} }

View File

@ -10,11 +10,18 @@ import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.Symbol; import me.topchetoeu.jscript.engine.values.Symbol;
import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.interop.Native; 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 class Internals {
public final Environment targetEnv; 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) { @Native public void markSpecial(FunctionValue ...funcs) {
for (var func : funcs) { for (var func : funcs) {
@ -163,5 +170,9 @@ public class Internals {
this.symbol = targetEnv.wrappersProvider.getConstr(SymbolPolyfill.class); this.symbol = targetEnv.wrappersProvider.getConstr(SymbolPolyfill.class);
this.map = targetEnv.wrappersProvider.getConstr(MapPolyfill.class); this.map = targetEnv.wrappersProvider.getConstr(MapPolyfill.class);
this.set = targetEnv.wrappersProvider.getConstr(SetPolyfill.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);
} }
} }

View File

@ -75,7 +75,7 @@ public class JSON {
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(ctx, e.msg);
} }
} }
@Native @Native

View File

@ -33,27 +33,27 @@ public class ObjectPolyfill {
var hasSet = attrib.hasMember(ctx, "set", false); var hasSet = attrib.hasMember(ctx, "set", false);
if (hasVal) { 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( if (!obj.defineProperty(
ctx, key, ctx, key,
attrib.getMember(ctx, "value"), attrib.getMember(ctx, "value"),
Values.toBoolean(attrib.getMember(ctx, "writable")), Values.toBoolean(attrib.getMember(ctx, "writable")),
Values.toBoolean(attrib.getMember(ctx, "configurable")), Values.toBoolean(attrib.getMember(ctx, "configurable")),
Values.toBoolean(attrib.getMember(ctx, "enumerable")) Values.toBoolean(attrib.getMember(ctx, "enumerable"))
)) throw EngineException.ofType("Can't define property '" + key + "'."); )) throw EngineException.ofType(ctx, "Can't define property '" + key + "'.");
} }
else { else {
var get = attrib.getMember(ctx, "get"); var get = attrib.getMember(ctx, "get");
var set = attrib.getMember(ctx, "set"); var set = attrib.getMember(ctx, "set");
if (get != null && !(get instanceof FunctionValue)) throw EngineException.ofType("Get 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("Set accessor must be a function."); if (set != null && !(set instanceof FunctionValue)) throw EngineException.ofType(ctx, "Set accessor must be a function.");
if (!obj.defineProperty( if (!obj.defineProperty(
ctx, key, ctx, key,
(FunctionValue)get, (FunctionValue)set, (FunctionValue)get, (FunctionValue)set,
Values.toBoolean(attrib.getMember(ctx, "configurable")), Values.toBoolean(attrib.getMember(ctx, "configurable")),
Values.toBoolean(attrib.getMember(ctx, "enumerable")) 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; return obj;

View File

@ -44,7 +44,7 @@ public class PromisePolyfill {
} }
@Native public static PromisePolyfill any(Context ctx, Object _promises) throws InterruptedException { @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); var promises = Values.array(_promises);
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue()); if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
var n = new int[] { promises.size() }; var n = new int[] { promises.size() };
@ -69,7 +69,7 @@ public class PromisePolyfill {
return res; return res;
} }
@Native public static PromisePolyfill race(Context ctx, Object _promises) throws InterruptedException { @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); var promises = Values.array(_promises);
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue()); if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
var res = new PromisePolyfill(); var res = new PromisePolyfill();
@ -85,7 +85,7 @@ public class PromisePolyfill {
return res; return res;
} }
@Native public static PromisePolyfill all(Context ctx, Object _promises) throws InterruptedException { @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); var promises = Values.array(_promises);
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue()); if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
var n = new int[] { promises.size() }; var n = new int[] { promises.size() };
@ -112,7 +112,7 @@ public class PromisePolyfill {
return res; return res;
} }
@Native public static PromisePolyfill allSettled(Context ctx, Object _promises) throws InterruptedException { @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); var promises = Values.array(_promises);
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue()); if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
var n = new int[] { promises.size() }; var n = new int[] { promises.size() };
@ -314,7 +314,7 @@ public class PromisePolyfill {
* NOT THREAD SAFE - must be called from the engine executor thread * NOT THREAD SAFE - must be called from the engine executor thread
*/ */
@Native public PromisePolyfill(Context ctx, FunctionValue func) throws InterruptedException { @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 { try {
func.call( func.call(
ctx, null, ctx, null,

View File

@ -18,10 +18,10 @@ public class StringPolyfill {
public final String value; 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; if (val instanceof StringPolyfill) return ((StringPolyfill)val).value;
else if (val instanceof String) return (String)val; 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) { private static int normalizeI(int i, int len, boolean clamp) {
if (i < 0) i += len; if (i < 0) i += len;
@ -32,47 +32,47 @@ public class StringPolyfill {
return i; return i;
} }
@NativeGetter(thisArg = true) public static int length(Context ctx, Object thisArg) { @NativeGetter(thisArg = true) public static int length(Context ctx, Object thisArg) throws InterruptedException {
return passThis("substring", thisArg).length(); return passThis(ctx, "substring", thisArg).length();
} }
@Native(thisArg = true) public static String substring(Context ctx, Object thisArg, int start, Object _end) throws InterruptedException { @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); start = normalizeI(start, val.length(), true);
int end = normalizeI(_end == null ? val.length() : (int)Values.toNumber(ctx, _end), val.length(), true); int end = normalizeI(_end == null ? val.length() : (int)Values.toNumber(ctx, _end), val.length(), true);
return val.substring(start, end); return val.substring(start, end);
} }
@Native(thisArg = true) public static String substr(Context ctx, Object thisArg, int start, Object _len) throws InterruptedException { @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); int len = _len == null ? val.length() - start : (int)Values.toNumber(ctx, _len);
return substring(ctx, val, start, start + len); return substring(ctx, val, start, start + len);
} }
@Native(thisArg = true) public static String toLowerCase(Context ctx, Object thisArg) { @Native(thisArg = true) public static String toLowerCase(Context ctx, Object thisArg) throws InterruptedException {
return passThis("toLowerCase", thisArg).toLowerCase(); return passThis(ctx, "toLowerCase", thisArg).toLowerCase();
} }
@Native(thisArg = true) public static String toUpperCase(Context ctx, Object thisArg) { @Native(thisArg = true) public static String toUpperCase(Context ctx, Object thisArg) throws InterruptedException {
return passThis("toUpperCase", thisArg).toUpperCase(); return passThis(ctx, "toUpperCase", thisArg).toUpperCase();
} }
@Native(thisArg = true) public static String charAt(Context ctx, Object thisArg, int i) { @Native(thisArg = true) public static String charAt(Context ctx, Object thisArg, int i) throws InterruptedException {
return passThis("charAt", thisArg).charAt(i) + ""; return passThis(ctx, "charAt", thisArg).charAt(i) + "";
} }
@Native(thisArg = true) public static int charCodeAt(Context ctx, Object thisArg, int i) { @Native(thisArg = true) public static int charCodeAt(Context ctx, Object thisArg, int i) throws InterruptedException {
return passThis("charCodeAt", thisArg).charAt(i); return passThis(ctx, "charCodeAt", thisArg).charAt(i);
} }
@Native(thisArg = true) public static boolean startsWith(Context ctx, Object thisArg, String term, int pos) { @Native(thisArg = true) public static boolean startsWith(Context ctx, Object thisArg, String term, int pos) throws InterruptedException {
return passThis("startsWith", thisArg).startsWith(term, pos); 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 { @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; return val.lastIndexOf(term, pos) >= 0;
} }
@Native(thisArg = true) public static int indexOf(Context ctx, Object thisArg, Object term, int start) throws InterruptedException { @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)) { if (term != null && term != Values.NULL && !(term instanceof String)) {
var search = Values.getMember(ctx, term, ctx.env.symbol("Symbol.search")); 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); 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 { @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)) { if (term != null && term != Values.NULL && !(term instanceof String)) {
var search = Values.getMember(ctx, term, ctx.env.symbol("Symbol.search")); 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 { @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 { @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)) { if (term != null && term != Values.NULL && !(term instanceof String)) {
var replace = Values.getMember(ctx, term, ctx.env.symbol("Symbol.replace")); 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); 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 { @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)) { if (term != null && term != Values.NULL && !(term instanceof String)) {
var replace = Values.getMember(ctx, term, ctx.env.symbol("Symbol.replace")); 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 { @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; FunctionValue match;
@ -137,9 +137,9 @@ public class StringPolyfill {
var regex = Values.callNew(ctx, ctx.env.regexConstructor, Values.toString(ctx, term), ""); var regex = Values.callNew(ctx, ctx.env.regexConstructor, Values.toString(ctx, term), "");
_match = Values.getMember(ctx, regex, ctx.env.symbol("Symbol.match")); _match = Values.getMember(ctx, regex, ctx.env.symbol("Symbol.match"));
if (_match instanceof FunctionValue) match = (FunctionValue)_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, ""); } catch (IllegalArgumentException e) { return new ArrayValue(ctx, ""); }
@ -148,7 +148,7 @@ public class StringPolyfill {
else return new ArrayValue(ctx, ""); else return new ArrayValue(ctx, "");
} }
@Native(thisArg = true) public static Object matchAll(Context ctx, Object thisArg, Object term, String replacement) throws InterruptedException { @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; FunctionValue match = null;
@ -162,15 +162,15 @@ public class StringPolyfill {
var regex = Values.callNew(ctx, ctx.env.regexConstructor, Values.toString(ctx, term), "g"); var regex = Values.callNew(ctx, ctx.env.regexConstructor, Values.toString(ctx, term), "g");
var _match = Values.getMember(ctx, regex, ctx.env.symbol("Symbol.matchAll")); var _match = Values.getMember(ctx, regex, ctx.env.symbol("Symbol.matchAll"));
if (_match instanceof FunctionValue) match = (FunctionValue)_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.");
return match.call(ctx, term, val); 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 { @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); 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 { @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 { @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)); for (var el : args) res.append(Values.toString(ctx, el));
return res.toString(); return res.toString();
} }
@Native(thisArg = true) public static String trim(Context ctx, Object thisArg) throws InterruptedException { @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 { @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; else return val;
} }
@Native(thisArg = true) public static String toString(Context ctx, Object thisArg) throws InterruptedException { @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 { @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) { @Native public static String fromCharCode(int ...val) {

View File

@ -27,28 +27,22 @@ public class SymbolPolyfill {
public final Symbol value; 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; if (val instanceof SymbolPolyfill) return ((SymbolPolyfill)val).value;
else if (val instanceof Symbol) return (Symbol)val; 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 { @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(""); if (val == null) return new Symbol("");
else return new Symbol(Values.toString(ctx, val)); else return new Symbol(Values.toString(ctx, val));
} }
@Native(thisArg = true) public static String toString(Context ctx, Object thisArg) throws InterruptedException { @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 { @Native(thisArg = true) public static Symbol valueOf(Context ctx, Object thisArg) throws InterruptedException {
return passThis("valueOf", thisArg); return passThis(ctx, "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);
} }
@Native("for") public static Symbol _for(String key) { @Native("for") public static Symbol _for(String key) {