ES6 Support Groundwork + Fixes #26

Merged
TopchetoEU merged 49 commits from ES6 into master 2024-09-05 14:26:07 +00:00
14 changed files with 309 additions and 217 deletions
Showing only changes of commit c39c06b792 - Show all commits

View File

@ -1,14 +1,21 @@
plugins { plugins {
id "application" id "application"
// these idiots don't optimize in the compile-time, but in the runtime
// who let these knuckleheads make a language
// TODO: figure out how to integrate proguard
// id "com.github.xaverkapeller.proguard-annotations"
} }
repositories { repositories {
mavenCentral() mavenCentral()
gradlePluginPortal()
} }
dependencies { dependencies {
annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2'
// Genuinely fuck Java // Genuinely fuck Java
annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2'
compileOnly 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2' compileOnly 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2'
} }

View File

@ -1,5 +1,13 @@
pluginManagement {
repositories {
mavenCentral()
gradlePluginPortal()
}
}
plugins { plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0' id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0'
} }
rootProject.name = properties.project_name rootProject.name = properties.project_name

View File

@ -1,7 +1,6 @@
package me.topchetoeu.jscript.runtime; package me.topchetoeu.jscript.runtime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -109,6 +108,7 @@ public final class Frame {
public final Stack<TryCtx> tryStack = new Stack<>(); public final Stack<TryCtx> tryStack = new Stack<>();
public final CodeFunction function; public final CodeFunction function;
public final Environment env; public final Environment env;
private final DebugContext dbg;
public Value[] getVar(int i) { public Value[] getVar(int i) {
if (i < 0) return captures[~i]; if (i < 0) return captures[~i];
@ -132,24 +132,15 @@ public final class Frame {
return peek(0); return peek(0);
} }
public Value peek(int offset) { public Value peek(int offset) {
if (stackPtr <= offset) return null; return stack[stackPtr - 1 - offset];
else return stack[stackPtr - 1 - offset];
} }
public Value pop() { public Value pop() {
if (stackPtr == 0) return Value.UNDEFINED;
return stack[--stackPtr]; return stack[--stackPtr];
} }
public Value[] take(int n) { public Value[] take(int n) {
int srcI = stackPtr - n;
if (srcI < 0) srcI = 0;
int dstI = n + srcI - stackPtr;
int copyN = stackPtr - srcI;
Value[] res = new Value[n]; Value[] res = new Value[n];
Arrays.fill(res, Value.UNDEFINED); System.arraycopy(stack, stackPtr - n, res, 0, n);
System.arraycopy(stack, srcI, res, dstI, copyN); stackPtr -= n;
stackPtr -= copyN;
return res; return res;
} }
@ -162,33 +153,45 @@ public final class Frame {
stack[stackPtr++] = val; stack[stackPtr++] = val;
} }
public void replace(Value val) {
stack[stackPtr - 1] = val;
}
// for the love of christ don't touch this // for the love of christ don't touch this
private Value next(Value value, Value returnValue, EngineException error) { /**
* This is provided only for optimization-sike. All parameters must be null except at most one, otherwise undefined behavior
*/
public final Value next(Value value, Value returnValue, EngineException error) {
if (value != null) push(value); if (value != null) push(value);
Instruction instr = null; Instruction instr = null;
if (codePtr >= 0 && codePtr < function.body.instructions.length) instr = function.body.instructions[codePtr]; if (codePtr != function.body.instructions.length) instr = function.body.instructions[codePtr];
if (returnValue == null && error == null) { if (returnValue == null && error == null) {
try { try {
if (Thread.interrupted()) throw new InterruptException(); if (Thread.interrupted()) throw new InterruptException();
if (instr == null) returnValue = null; if (instr == null) {
if (stackPtr > 0) returnValue = stack[stackPtr - 1];
else returnValue = Value.UNDEFINED;
}
else { else {
DebugContext.get(env).onInstruction(env, this, instr, null, null, false); dbg.onInstruction(env, this, instr);
try { try {
this.jumpFlag = this.popTryFlag = false; this.jumpFlag = this.popTryFlag = false;
returnValue = InstructionRunner.exec(env, instr, this); returnValue = InstructionRunner.exec(env, instr, this);
} }
catch (EngineException e) { catch (EngineException e) {
error = e.add(env, function.name, DebugContext.get(env).getMapOrEmpty(function).toLocation(codePtr, true)); error = e.add(env, function.name, dbg.getMapOrEmpty(function).toLocation(codePtr, true));
} }
} }
} }
catch (EngineException e) { error = e; } catch (EngineException e) { error = e; }
// catch (RuntimeException e) { error = EngineException.ofError("InternalError", e.getMessage()); } catch (RuntimeException e) {
System.out.println(dbg.getMapOrEmpty(function).toLocation(codePtr, true));
throw e;
}
} }
while (!tryStack.empty()) { while (!tryStack.empty()) {
@ -265,17 +268,17 @@ public final class Frame {
if (error != null) { if (error != null) {
var caught = false; var caught = false;
for (var frame : DebugContext.get(env).getStackFrames()) { for (var frame : dbg.getStackFrames()) {
for (var tryCtx : frame.tryStack) { for (var tryCtx : frame.tryStack) {
if (tryCtx.state == TryState.TRY) caught = true; if (tryCtx.state == TryState.TRY) caught = true;
} }
} }
DebugContext.get(env).onInstruction(env, this, instr, null, error, caught); dbg.onInstruction(env, this, instr, null, error, caught);
throw error; throw error;
} }
if (returnValue != null) { if (returnValue != null) {
DebugContext.get(env).onInstruction(env, this, instr, returnValue, null, false); dbg.onInstruction(env, this, instr, returnValue, null, false);
return returnValue; return returnValue;
} }
@ -285,7 +288,7 @@ public final class Frame {
/** /**
* Executes the next instruction in the frame * Executes the next instruction in the frame
*/ */
public Value next() { public final Value next() {
return next(null, null, null); return next(null, null, null);
} }
/** /**
@ -294,7 +297,7 @@ public final class Frame {
* *
* @param value The value to induce * @param value The value to induce
*/ */
public Value next(Value value) { public final Value next(Value value) {
return next(value, null, null); return next(value, null, null);
} }
/** /**
@ -305,7 +308,7 @@ public final class Frame {
* *
* @param error The error to induce * @param error The error to induce
*/ */
public Value induceError(EngineException error) { public final Value induceError(EngineException error) {
return next(null, null, error); return next(null, null, error);
} }
/** /**
@ -317,7 +320,7 @@ public final class Frame {
* *
* @param value The retunr value to induce * @param value The retunr value to induce
*/ */
public Value induceReturn(Value value) { public final Value induceReturn(Value value) {
return next(null, value, null); return next(null, value, null);
} }
@ -408,6 +411,7 @@ public final class Frame {
public Frame(Environment env, boolean isNew, Value thisArg, Value[] args, CodeFunction func) { public Frame(Environment env, boolean isNew, Value thisArg, Value[] args, CodeFunction func) {
this.env = env; this.env = env;
this.dbg = DebugContext.get(env);
this.function = func; this.function = func;
this.isNew = isNew; this.isNew = isNew;

View File

@ -285,11 +285,105 @@ public class InstructionRunner {
private static Value execOperation(Environment env, Instruction instr, Frame frame) { private static Value execOperation(Environment env, Instruction instr, Frame frame) {
Operation op = instr.get(0); Operation op = instr.get(0);
var args = new Value[op.operands]; Value res;
var stack = frame.stack;
for (var i = op.operands - 1; i >= 0; i--) args[i] = frame.pop(); frame.stackPtr -= 1;
var ptr = frame.stackPtr;
frame.push(Value.operation(env, op, args)); // for (var i = op.operands - 1; i >= 0; i--) args[i] = frame.pop();
switch (op) {
case ADD:
res = Value.add(env, stack[ptr - 1], stack[ptr]);
break;
case SUBTRACT:
res = Value.subtract(env, stack[ptr - 1], stack[ptr]);
break;
case DIVIDE:
res = Value.divide(env, stack[ptr - 1], stack[ptr]);
break;
case MULTIPLY:
res = Value.multiply(env, stack[ptr - 1], stack[ptr]);
break;
case MODULO:
res = Value.modulo(env, stack[ptr - 1], stack[ptr]);
break;
case AND:
res = Value.and(env, stack[ptr - 1], stack[ptr]);
break;
case OR:
res = Value.or(env, stack[ptr - 1], stack[ptr]);
break;
case XOR:
res = Value.xor(env, stack[ptr - 1], stack[ptr]);
break;
case EQUALS:
res = BoolValue.of(stack[ptr - 1].equals(stack[ptr]));
break;
case NOT_EQUALS:
res = BoolValue.of(!stack[ptr - 1].equals(stack[ptr]));
break;
case LOOSE_EQUALS:
res = BoolValue.of(Value.looseEqual(env, stack[ptr - 1], stack[ptr]));
break;
case LOOSE_NOT_EQUALS:
res = BoolValue.of(!Value.looseEqual(env, stack[ptr - 1], stack[ptr]));
break;
case GREATER:
res = BoolValue.of(Value.greater(env, stack[ptr - 1], stack[ptr]));
break;
case GREATER_EQUALS:
res = BoolValue.of(Value.greaterOrEqual(env, stack[ptr - 1], stack[ptr]));
break;
case LESS:
res = BoolValue.of(Value.less(env, stack[ptr - 1], stack[ptr]));
break;
case LESS_EQUALS:
res = BoolValue.of(Value.lessOrEqual(env, stack[ptr - 1], stack[ptr]));
break;
case INVERSE:
res = Value.bitwiseNot(env, stack[ptr++]);
frame.stackPtr++;
break;
case NOT:
res = BoolValue.of(!stack[ptr++].toBoolean());
frame.stackPtr++;
break;
case POS:
res = stack[ptr++].toNumber(env);
frame.stackPtr++;
break;
case NEG:
res = Value.negative(env, stack[ptr++]);
frame.stackPtr++;
break;
case SHIFT_LEFT:
res = Value.shiftLeft(env, stack[ptr], stack[ptr]);
break;
case SHIFT_RIGHT:
res = Value.shiftRight(env, stack[ptr], stack[ptr]);
break;
case USHIFT_RIGHT:
res = Value.unsignedShiftRight(env, stack[ptr], stack[ptr]);
break;
case IN:
res = BoolValue.of(stack[ptr - 1].hasMember(env, stack[ptr], false));
break;
case INSTANCEOF:
res = BoolValue.of(stack[ptr - 1].isInstanceOf(env, stack[ptr].getMember(env, new StringValue("prototype"))));
break;
default: return null;
}
stack[ptr - 1] = res;
frame.codePtr++; frame.codePtr++;
return null; return null;
} }

View File

@ -86,6 +86,10 @@ public class DebugContext {
if (debugger != null) return debugger.onInstruction(env, frame, instruction, returnVal, error, caught); if (debugger != null) return debugger.onInstruction(env, frame, instruction, returnVal, error, caught);
else return false; else return false;
} }
public boolean onInstruction(Environment env, Frame frame, Instruction instruction) {
if (debugger != null) return debugger.onInstruction(env, frame, instruction, null, null, false);
else return false;
}
public void onSource(Filename filename, String source) { public void onSource(Filename filename, String source) {
if (debugger != null) debugger.onSourceLoad(filename, source); if (debugger != null) debugger.onSourceLoad(filename, source);
if (sources != null) sources.put(filename, source); if (sources != null) sources.put(filename, source);

View File

@ -87,7 +87,7 @@ public interface Member {
if (field.configurable != configurable) return false; if (field.configurable != configurable) return false;
if (field.enumerable != enumerable) return false; if (field.enumerable != enumerable) return false;
if (!writable) return field.get(env, self).strictEquals(env, get(env, self)); if (!writable) return field.get(env, self).equals(get(env, self));
set(env, field.get(env, self), self); set(env, field.get(env, self), self);
writable = field.writable; writable = field.writable;

View File

@ -10,7 +10,6 @@ import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import me.topchetoeu.jscript.common.Operation;
import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.environment.Key; import me.topchetoeu.jscript.common.environment.Key;
import me.topchetoeu.jscript.common.json.JSON; import me.topchetoeu.jscript.common.json.JSON;
@ -112,65 +111,6 @@ public abstract class Value {
public final int toInt(Environment env) { return (int)toNumber(env).value; } public final int toInt(Environment env) { return (int)toNumber(env).value; }
public final long toLong(Environment env) { return (long)toNumber(env).value; } public final long toLong(Environment env) { return (long)toNumber(env).value; }
public Value add(Environment env, Value other) {
if (this instanceof StringValue || other instanceof StringValue) {
return new StringValue(this.toString(env).value + other.toString(env).value);
}
else return new NumberValue(this.toNumber(env).value + other.toNumber(env).value);
}
public NumberValue subtract(Environment env, Value other) {
return new NumberValue(toNumber(env).value - other.toNumber(env).value);
}
public NumberValue multiply(Environment env, Value other) {
return new NumberValue(toNumber(env).value - other.toNumber(env).value);
}
public NumberValue divide(Environment env, Value other) {
return new NumberValue(toNumber(env).value / other.toNumber(env).value);
}
public NumberValue modulo(Environment env, Value other) {
return new NumberValue(toNumber(env).value % other.toNumber(env).value);
}
public NumberValue negative(Environment env) {
return new NumberValue(-toNumber(env).value);
}
public NumberValue and(Environment env, Value other) {
return new NumberValue(this.toInt(env) & other.toInt(env));
}
public NumberValue or(Environment env, Value other) {
return new NumberValue(this.toInt(env) | other.toInt(env));
}
public NumberValue xor(Environment env, Value other) {
return new NumberValue(this.toInt(env) ^ other.toInt(env));
}
public NumberValue bitwiseNot(Environment env) {
return new NumberValue(~this.toInt(env));
}
public NumberValue shiftLeft(Environment env, Value other) {
return new NumberValue(this.toInt(env) << other.toInt(env));
}
public NumberValue shiftRight(Environment env, Value other) {
return new NumberValue(this.toInt(env) >> other.toInt(env));
}
public NumberValue unsignedShiftRight(Environment env, Value other) {
long a = this.toInt(env);
long b = other.toInt(env);
if (a < 0) a += 0x100000000l;
if (b < 0) b += 0x100000000l;
return new NumberValue(a >>> b);
}
public CompareResult compare(Environment env, Value other) {
var a = this.toPrimitive(env);
var b = other.toPrimitive(env);
if (a instanceof StringValue && b instanceof StringValue) return a.compare(env, b);
else return a.toNumber(env).compare(env, b.toNumber(env));
}
public final boolean isInstanceOf(Environment env, Value proto) { public final boolean isInstanceOf(Environment env, Value proto) {
for (var val = getPrototype(env); val != null; val = getPrototype(env)) { for (var val = getPrototype(env); val != null; val = getPrototype(env)) {
if (val.equals(proto)) return true; if (val.equals(proto)) return true;
@ -179,44 +119,6 @@ public abstract class Value {
return false; return false;
} }
public static Value operation(Environment env, Operation op, Value ...args) {
switch (op) {
case ADD: return args[0].add(env, args[1]);
case SUBTRACT: return args[0].subtract(env, args[1]);
case DIVIDE: return args[0].divide(env, args[1]);
case MULTIPLY: return args[0].multiply(env, args[1]);
case MODULO: return args[0].modulo(env, args[1]);
case AND: return args[0].and(env, args[1]);
case OR: return args[0].or(env, args[1]);
case XOR: return args[0].xor(env, args[1]);
case EQUALS: return BoolValue.of(args[0].strictEquals(env, args[1]));
case NOT_EQUALS: return BoolValue.of(!args[0].strictEquals(env, args[1]));
case LOOSE_EQUALS: return BoolValue.of(args[0].looseEqual(env, args[1]));
case LOOSE_NOT_EQUALS: return BoolValue.of(!args[0].looseEqual(env, args[1]));
case GREATER: return BoolValue.of(args[0].compare(env, args[1]).greater());
case GREATER_EQUALS: return BoolValue.of(args[0].compare(env, args[1]).greaterOrEqual());
case LESS: return BoolValue.of(args[0].compare(env, args[1]).less());
case LESS_EQUALS: return BoolValue.of(args[0].compare(env, args[1]).lessOrEqual());
case INVERSE: return args[0].bitwiseNot(env);
case NOT: return BoolValue.of(!args[0].toBoolean());
case POS: return args[0].toNumber(env);
case NEG: return args[0].negative(env);
case SHIFT_LEFT: return args[0].shiftLeft(env, args[1]);
case SHIFT_RIGHT: return args[0].shiftRight(env, args[1]);
case USHIFT_RIGHT: return args[0].unsignedShiftRight(env, args[1]);
case IN: return BoolValue.of(args[0].hasMember(env, args[1], false));
case INSTANCEOF: return BoolValue.of(args[0].isInstanceOf(env, args[1].getMember(env, new StringValue("prototype"))));
default: return null;
}
}
public abstract Member getOwnMember(Environment env, KeyCache key); public abstract Member getOwnMember(Environment env, KeyCache key);
public abstract Map<String, Member> getOwnMembers(Environment env); public abstract Map<String, Member> getOwnMembers(Environment env);
public abstract Map<SymbolValue, Member> getOwnSymbolMembers(Environment env); public abstract Map<SymbolValue, Member> getOwnSymbolMembers(Environment env);
@ -475,33 +377,6 @@ public abstract class Value {
else return null; else return null;
} }
public abstract boolean strictEquals(Environment env, Value other);
public final boolean looseEqual(Environment env, Value other) {
var a = this;
var b = other;
// In loose equality, null is equivalent to undefined
if (a instanceof VoidValue || b instanceof VoidValue) return a instanceof VoidValue && b instanceof VoidValue;
// If both are objects, just compare their references
if (!a.isPrimitive() && !b.isPrimitive()) return a.strictEquals(env, b);
// Convert values to primitives
a = a.toPrimitive(env);
b = b.toPrimitive(env);
// Compare symbols by reference
if (a instanceof SymbolValue || b instanceof SymbolValue) return a.strictEquals(env, b);
// Compare booleans as numbers
if (a instanceof BoolValue || b instanceof BoolValue) return a.toNumber(env).strictEquals(env, b.toNumber(env));
// Comparse numbers as numbers
if (a instanceof NumberValue || b instanceof NumberValue) return a.toNumber(env).strictEquals(env, b.toNumber(env));
// Default to strings
return a.toString(env).strictEquals(env, b.toString(env));
}
public Iterable<Object> toIterable(Environment env) { public Iterable<Object> toIterable(Environment env) {
return () -> { return () -> {
if (!(this instanceof FunctionValue)) return Collections.emptyIterator(); if (!(this instanceof FunctionValue)) return Collections.emptyIterator();
@ -541,19 +416,6 @@ public abstract class Value {
}; };
} }
public static FunctionValue fromIterator(Environment ext, Iterable<? extends Value> iterable) {
var it = iterable.iterator();
return new NativeFunction("", args -> {
var obj = new ObjectValue();
if (!it.hasNext()) obj.defineOwnMember(args.env, "done", FieldMember.of(BoolValue.TRUE));
else obj.defineOwnMember(args.env, "value", FieldMember.of(it.next()));
return obj;
});
}
public void callWith(Environment env, Iterable<? extends Value> it) { public void callWith(Environment env, Iterable<? extends Value> it) {
for (var el : it) { for (var el : it) {
this.call(env, Value.UNDEFINED, el); this.call(env, Value.UNDEFINED, el);
@ -656,6 +518,153 @@ public abstract class Value {
return toReadable(ext, new HashSet<>(), 0); return toReadable(ext, new HashSet<>(), 0);
} }
public static final ObjectValue global(Environment env) {
return env.initFrom(GLOBAL, () -> new ObjectValue());
}
public static final Map<String, Value> intrinsics(Environment env) {
return env.initFrom(INTRINSICS, () -> new HashMap<>());
}
public static FunctionValue fromIterator(Environment ext, Iterable<? extends Value> iterable) {
var it = iterable.iterator();
return new NativeFunction("", args -> {
var obj = new ObjectValue();
if (!it.hasNext()) obj.defineOwnMember(args.env, "done", FieldMember.of(BoolValue.TRUE));
else obj.defineOwnMember(args.env, "value", FieldMember.of(it.next()));
return obj;
});
}
public static final boolean lessOrEqual(Environment env, Value a, Value b) {
a = a.toPrimitive(env);
b = b.toPrimitive(env);
if (a instanceof StringValue aStr && b instanceof StringValue bStr) {
return aStr.value.compareTo(bStr.value) <= 0;
}
else {
return a.toNumber(env).value <= b.toNumber(env).value;
}
}
public static final boolean greaterOrEqual(Environment env, Value a, Value b) {
a = a.toPrimitive(env);
b = b.toPrimitive(env);
if (a instanceof StringValue aStr && b instanceof StringValue bStr) {
return aStr.value.compareTo(bStr.value) >= 0;
}
else {
return a.toNumber(env).value >= b.toNumber(env).value;
}
}
public static final boolean less(Environment env, Value a, Value b) {
a = a.toPrimitive(env);
b = b.toPrimitive(env);
if (a instanceof StringValue aStr && b instanceof StringValue bStr) {
return aStr.value.compareTo(bStr.value) >= 0;
}
else {
return a.toNumber(env).value < b.toNumber(env).value;
}
}
public static final boolean greater(Environment env, Value a, Value b) {
a = a.toPrimitive(env);
b = b.toPrimitive(env);
if (a instanceof StringValue aStr && b instanceof StringValue bStr) {
return aStr.value.compareTo(bStr.value) >= 0;
}
else {
return a.toNumber(env).value > b.toNumber(env).value;
}
}
public static final Value add(Environment env, Value a, Value b) {
a = a.toPrimitive(env);
b = b.toPrimitive(env);
if (a instanceof StringValue || b instanceof StringValue) {
return new StringValue(a.toString(env).value + b.toString(env).value);
}
else {
return new NumberValue(a.toNumber(env).value + b.toNumber(env).value);
}
}
public static final NumberValue subtract(Environment env, Value a, Value b) {
return new NumberValue(a.toNumber(env).value - b.toNumber(env).value);
}
public static final NumberValue multiply(Environment env, Value a, Value b) {
return new NumberValue(a.toNumber(env).value - b.toNumber(env).value);
}
public static final NumberValue divide(Environment env, Value a, Value b) {
return new NumberValue(a.toNumber(env).value / b.toNumber(env).value);
}
public static final NumberValue modulo(Environment env, Value a, Value b) {
return new NumberValue(a.toNumber(env).value % b.toNumber(env).value);
}
public static final NumberValue negative(Environment env, Value a) {
return new NumberValue(-a.toNumber(env).value);
}
public static final NumberValue and(Environment env, Value a, Value b) {
return new NumberValue(a.toInt(env) & b.toInt(env));
}
public static final NumberValue or(Environment env, Value a, Value b) {
return new NumberValue(a.toInt(env) | b.toInt(env));
}
public static final NumberValue xor(Environment env, Value a, Value b) {
return new NumberValue(a.toInt(env) ^ b.toInt(env));
}
public static final NumberValue bitwiseNot(Environment env, Value a) {
return new NumberValue(~a.toInt(env));
}
public static final NumberValue shiftLeft(Environment env, Value a, Value b) {
return new NumberValue(a.toInt(env) << b.toInt(env));
}
public static final NumberValue shiftRight(Environment env, Value a, Value b) {
return new NumberValue(a.toInt(env) >> b.toInt(env));
}
public static final NumberValue unsignedShiftRight(Environment env, Value a, Value b) {
long _a = a.toInt(env);
long _b = b.toInt(env);
if (_a < 0) _a += 0x100000000l;
if (_b < 0) _b += 0x100000000l;
return new NumberValue(_a >>> _b);
}
public static final boolean looseEqual(Environment env, Value a, Value b) {
// In loose equality, null is equivalent to undefined
if (a instanceof VoidValue || b instanceof VoidValue) return a instanceof VoidValue && b instanceof VoidValue;
// If both are objects, just compare their references
if (!a.isPrimitive() && !b.isPrimitive()) return a.equals(b);
// Convert values to primitives
a = a.toPrimitive(env);
b = b.toPrimitive(env);
// Compare symbols by reference
if (a instanceof SymbolValue || b instanceof SymbolValue) return a.equals(b);
// Compare booleans as numbers
if (a instanceof BoolValue || b instanceof BoolValue) return a.toNumber(env).equals(b.toNumber(env));
// Comparse numbers as numbers
if (a instanceof NumberValue || b instanceof NumberValue) return a.toNumber(env).equals(b.toNumber(env));
// Default to strings
return a.toString(env).equals(b.toString(env));
}
// public static Value operation(Environment env, Operation op, Value ...args) {
// }
public static final String errorToReadable(RuntimeException err, String prefix) { public static final String errorToReadable(RuntimeException err, String prefix) {
prefix = prefix == null ? "Uncaught" : "Uncaught " + prefix; prefix = prefix == null ? "Uncaught" : "Uncaught " + prefix;
if (err instanceof EngineException) { if (err instanceof EngineException) {
@ -678,11 +687,4 @@ public abstract class Value {
return prefix + " internal error " + str.toString(); return prefix + " internal error " + str.toString();
} }
} }
public static final ObjectValue global(Environment env) {
return env.initFrom(GLOBAL, () -> new ObjectValue());
}
public static final Map<String, Value> intrinsics(Environment env) {
return env.initFrom(INTRINSICS, () -> new HashMap<>());
}
} }

View File

@ -10,13 +10,12 @@ public final class CodeFunction extends FunctionValue {
public final Value[][] captures; public final Value[][] captures;
public Environment env; public Environment env;
@Override public Value onCall(Environment env, boolean isNew, String name, Value thisArg, Value ...args) { private Value onCall(Frame frame) {
var frame = new Frame(env, isNew, thisArg, args, this);
frame.onPush(); frame.onPush();
try { try {
while (true) { while (true) {
var res = frame.next(); var res = frame.next(null, null, null);
if (res != null) return res; if (res != null) return res;
} }
} }
@ -25,6 +24,11 @@ public final class CodeFunction extends FunctionValue {
} }
} }
@Override public Value onCall(Environment env, boolean isNew, String name, Value thisArg, Value ...args) {
var frame = new Frame(env, isNew, thisArg, args, this);
return onCall(frame);
}
public CodeFunction(Environment env, String name, FunctionBody body, Value[][] captures) { public CodeFunction(Environment env, String name, FunctionBody body, Value[][] captures) {
super(name, body.argsN); super(name, body.argsN);
this.captures = captures; this.captures = captures;

View File

@ -70,8 +70,6 @@ public class ObjectValue extends Value {
@Override public NumberValue toNumber(Environment env) { return toPrimitive(env).toNumber(env); } @Override public NumberValue toNumber(Environment env) { return toPrimitive(env).toNumber(env); }
@Override public StringValue type() { return typeString; } @Override public StringValue type() { return typeString; }
@Override public boolean strictEquals(Environment ext, Value other) { return this == other; }
public final void preventExtensions() { public final void preventExtensions() {
extensible = false; extensible = false;
} }

View File

@ -1,7 +1,6 @@
package me.topchetoeu.jscript.runtime.values.primitives; package me.topchetoeu.jscript.runtime.values.primitives;
import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
public final class BoolValue extends PrimitiveValue { public final class BoolValue extends PrimitiveValue {
@ -23,8 +22,8 @@ public final class BoolValue extends PrimitiveValue {
return env.get(BOOL_PROTO); return env.get(BOOL_PROTO);
} }
@Override public boolean strictEquals(Environment ext, Value other) { @Override public boolean equals(Object other) {
if (other instanceof BoolValue) return value == ((BoolValue)other).value; if (other instanceof BoolValue bool) return value == bool.value;
else return false; else return false;
} }

View File

@ -5,7 +5,6 @@ import me.topchetoeu.jscript.common.json.JSON;
import me.topchetoeu.jscript.common.json.JSONElement; import me.topchetoeu.jscript.common.json.JSONElement;
import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
public final class NumberValue extends PrimitiveValue { public final class NumberValue extends PrimitiveValue {
@ -25,13 +24,8 @@ public final class NumberValue extends PrimitiveValue {
return env.get(NUMBER_PROTO); return env.get(NUMBER_PROTO);
} }
@Override public CompareResult compare(Environment env, Value other) { @Override public boolean equals(Object other) {
if (other instanceof NumberValue) return CompareResult.from(Double.compare(value, ((NumberValue)other).value)); if (other instanceof NumberValue val) return value == val.value;
else return super.compare(env, other);
}
@Override public boolean strictEquals(Environment ext, Value other) {
other = other.toPrimitive(ext);
if (other instanceof NumberValue) return value == ((NumberValue)other).value;
else return false; else return false;
} }

View File

@ -1,13 +1,11 @@
package me.topchetoeu.jscript.runtime.values.primitives; package me.topchetoeu.jscript.runtime.values.primitives;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Member;
import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
public final class StringValue extends PrimitiveValue { public final class StringValue extends PrimitiveValue {
@ -27,17 +25,11 @@ public final class StringValue extends PrimitiveValue {
} }
@Override public StringValue toString(Environment ext) { return this; } @Override public StringValue toString(Environment ext) { return this; }
@Override public Value add(Environment ext, Value other) { @Override public boolean equals(Object other) {
return new StringValue(value + other.toString(ext).value); if (other instanceof StringValue val) return value.length() == val.value.length() && value.equals(val.value);
else return false;
} }
@Override public CompareResult compare(Environment env, Value other) {
if (other instanceof StringValue) return CompareResult.from(value.compareTo(((StringValue)other).value));
else return super.compare(env, other);
}
@Override public boolean strictEquals(Environment ext, Value other) {
return (other instanceof StringValue) && Objects.equals(((StringValue)other).value, value);
}
@Override public ObjectValue getPrototype(Environment env) { return env.get(STRING_PROTO); } @Override public ObjectValue getPrototype(Environment env) { return env.get(STRING_PROTO); }
@Override public Map<String, Member> getOwnMembers(Environment env) { @Override public Map<String, Member> getOwnMembers(Environment env) {

View File

@ -27,9 +27,6 @@ public final class SymbolValue extends PrimitiveValue {
throw EngineException.ofType("Cannot convert a Symbol value to a number"); throw EngineException.ofType("Cannot convert a Symbol value to a number");
} }
@Override public boolean strictEquals(Environment ext, Value other) {
return other == this;
}
@Override public ObjectValue getPrototype(Environment env) { return env.get(SYMBOL_PROTO); } @Override public ObjectValue getPrototype(Environment env) { return env.get(SYMBOL_PROTO); }
@Override public String toString() { @Override public String toString() {

View File

@ -6,7 +6,6 @@ import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.KeyCache; import me.topchetoeu.jscript.runtime.values.KeyCache;
import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Member;
import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
public final class VoidValue extends PrimitiveValue { public final class VoidValue extends PrimitiveValue {
@ -20,16 +19,6 @@ public final class VoidValue extends PrimitiveValue {
@Override public NumberValue toNumber(Environment ext) { return NumberValue.NAN; } @Override public NumberValue toNumber(Environment ext) { return NumberValue.NAN; }
@Override public StringValue toString(Environment ext) { return nameString; } @Override public StringValue toString(Environment ext) { return nameString; }
@Override public Value add(Environment ext, Value other) {
if (!other.isPrimitive()) other = other.toPrimitive(ext);
if (other instanceof StringValue) return nameString.add(ext, other);
else return NumberValue.NAN;
}
@Override public boolean strictEquals(Environment ext, Value other) {
return this == other;
}
@Override public ObjectValue getPrototype(Environment env) { return null; } @Override public ObjectValue getPrototype(Environment env) { return null; }
@Override public Member getOwnMember(Environment env, KeyCache key) { @Override public Member getOwnMember(Environment env, KeyCache key) {