ES6 Support Groundwork + Fixes (OLD ONE DON'T LOOK AT ME!!!) #22
@ -1,14 +1,21 @@
|
||||
|
||||
plugins {
|
||||
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 {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2'
|
||||
// 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'
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,13 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0'
|
||||
}
|
||||
|
||||
|
||||
rootProject.name = properties.project_name
|
||||
|
@ -1,7 +1,6 @@
|
||||
package me.topchetoeu.jscript.runtime;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -109,6 +108,7 @@ public final class Frame {
|
||||
public final Stack<TryCtx> tryStack = new Stack<>();
|
||||
public final CodeFunction function;
|
||||
public final Environment env;
|
||||
private final DebugContext dbg;
|
||||
|
||||
public Value[] getVar(int i) {
|
||||
if (i < 0) return captures[~i];
|
||||
@ -132,24 +132,15 @@ public final class Frame {
|
||||
return peek(0);
|
||||
}
|
||||
public Value peek(int offset) {
|
||||
if (stackPtr <= offset) return null;
|
||||
else return stack[stackPtr - 1 - offset];
|
||||
return stack[stackPtr - 1 - offset];
|
||||
}
|
||||
public Value pop() {
|
||||
if (stackPtr == 0) return Value.UNDEFINED;
|
||||
return stack[--stackPtr];
|
||||
}
|
||||
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];
|
||||
Arrays.fill(res, Value.UNDEFINED);
|
||||
System.arraycopy(stack, srcI, res, dstI, copyN);
|
||||
stackPtr -= copyN;
|
||||
System.arraycopy(stack, stackPtr - n, res, 0, n);
|
||||
stackPtr -= n;
|
||||
|
||||
return res;
|
||||
}
|
||||
@ -162,33 +153,45 @@ public final class Frame {
|
||||
|
||||
stack[stackPtr++] = val;
|
||||
}
|
||||
public void replace(Value val) {
|
||||
stack[stackPtr - 1] = val;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
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) {
|
||||
try {
|
||||
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 {
|
||||
DebugContext.get(env).onInstruction(env, this, instr, null, null, false);
|
||||
dbg.onInstruction(env, this, instr);
|
||||
|
||||
try {
|
||||
this.jumpFlag = this.popTryFlag = false;
|
||||
returnValue = InstructionRunner.exec(env, instr, this);
|
||||
}
|
||||
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 (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()) {
|
||||
@ -265,17 +268,17 @@ public final class Frame {
|
||||
if (error != null) {
|
||||
var caught = false;
|
||||
|
||||
for (var frame : DebugContext.get(env).getStackFrames()) {
|
||||
for (var frame : dbg.getStackFrames()) {
|
||||
for (var tryCtx : frame.tryStack) {
|
||||
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;
|
||||
}
|
||||
if (returnValue != null) {
|
||||
DebugContext.get(env).onInstruction(env, this, instr, returnValue, null, false);
|
||||
dbg.onInstruction(env, this, instr, returnValue, null, false);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
@ -285,7 +288,7 @@ public final class Frame {
|
||||
/**
|
||||
* Executes the next instruction in the frame
|
||||
*/
|
||||
public Value next() {
|
||||
public final Value next() {
|
||||
return next(null, null, null);
|
||||
}
|
||||
/**
|
||||
@ -294,7 +297,7 @@ public final class Frame {
|
||||
*
|
||||
* @param value The value to induce
|
||||
*/
|
||||
public Value next(Value value) {
|
||||
public final Value next(Value value) {
|
||||
return next(value, null, null);
|
||||
}
|
||||
/**
|
||||
@ -305,7 +308,7 @@ public final class Frame {
|
||||
*
|
||||
* @param error The error to induce
|
||||
*/
|
||||
public Value induceError(EngineException error) {
|
||||
public final Value induceError(EngineException error) {
|
||||
return next(null, null, error);
|
||||
}
|
||||
/**
|
||||
@ -317,7 +320,7 @@ public final class Frame {
|
||||
*
|
||||
* @param value The retunr value to induce
|
||||
*/
|
||||
public Value induceReturn(Value value) {
|
||||
public final Value induceReturn(Value value) {
|
||||
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) {
|
||||
this.env = env;
|
||||
this.dbg = DebugContext.get(env);
|
||||
this.function = func;
|
||||
this.isNew = isNew;
|
||||
|
||||
|
@ -285,11 +285,105 @@ public class InstructionRunner {
|
||||
|
||||
private static Value execOperation(Environment env, Instruction instr, Frame frame) {
|
||||
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++;
|
||||
return null;
|
||||
}
|
||||
|
@ -86,6 +86,10 @@ public class DebugContext {
|
||||
if (debugger != null) return debugger.onInstruction(env, frame, instruction, returnVal, error, caught);
|
||||
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) {
|
||||
if (debugger != null) debugger.onSourceLoad(filename, source);
|
||||
if (sources != null) sources.put(filename, source);
|
||||
|
@ -87,7 +87,7 @@ public interface Member {
|
||||
|
||||
if (field.configurable != configurable) 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);
|
||||
writable = field.writable;
|
||||
|
@ -10,7 +10,6 @@ import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import me.topchetoeu.jscript.common.Operation;
|
||||
import me.topchetoeu.jscript.common.environment.Environment;
|
||||
import me.topchetoeu.jscript.common.environment.Key;
|
||||
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 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) {
|
||||
for (var val = getPrototype(env); val != null; val = getPrototype(env)) {
|
||||
if (val.equals(proto)) return true;
|
||||
@ -179,44 +119,6 @@ public abstract class Value {
|
||||
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 Map<String, Member> getOwnMembers(Environment env);
|
||||
public abstract Map<SymbolValue, Member> getOwnSymbolMembers(Environment env);
|
||||
@ -475,33 +377,6 @@ public abstract class Value {
|
||||
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) {
|
||||
return () -> {
|
||||
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) {
|
||||
for (var el : it) {
|
||||
this.call(env, Value.UNDEFINED, el);
|
||||
@ -656,6 +518,153 @@ public abstract class Value {
|
||||
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) {
|
||||
prefix = prefix == null ? "Uncaught" : "Uncaught " + prefix;
|
||||
if (err instanceof EngineException) {
|
||||
@ -678,11 +687,4 @@ public abstract class Value {
|
||||
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<>());
|
||||
}
|
||||
}
|
||||
|
@ -10,13 +10,12 @@ public final class CodeFunction extends FunctionValue {
|
||||
public final Value[][] captures;
|
||||
public Environment env;
|
||||
|
||||
@Override public Value onCall(Environment env, boolean isNew, String name, Value thisArg, Value ...args) {
|
||||
var frame = new Frame(env, isNew, thisArg, args, this);
|
||||
private Value onCall(Frame frame) {
|
||||
frame.onPush();
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
var res = frame.next();
|
||||
var res = frame.next(null, null, null);
|
||||
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) {
|
||||
super(name, body.argsN);
|
||||
this.captures = captures;
|
||||
|
@ -70,8 +70,6 @@ public class ObjectValue extends Value {
|
||||
@Override public NumberValue toNumber(Environment env) { return toPrimitive(env).toNumber(env); }
|
||||
@Override public StringValue type() { return typeString; }
|
||||
|
||||
@Override public boolean strictEquals(Environment ext, Value other) { return this == other; }
|
||||
|
||||
public final void preventExtensions() {
|
||||
extensible = false;
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package me.topchetoeu.jscript.runtime.values.primitives;
|
||||
|
||||
import me.topchetoeu.jscript.common.environment.Environment;
|
||||
import me.topchetoeu.jscript.runtime.values.Value;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
|
||||
|
||||
public final class BoolValue extends PrimitiveValue {
|
||||
@ -23,8 +22,8 @@ public final class BoolValue extends PrimitiveValue {
|
||||
return env.get(BOOL_PROTO);
|
||||
}
|
||||
|
||||
@Override public boolean strictEquals(Environment ext, Value other) {
|
||||
if (other instanceof BoolValue) return value == ((BoolValue)other).value;
|
||||
@Override public boolean equals(Object other) {
|
||||
if (other instanceof BoolValue bool) return value == bool.value;
|
||||
else return false;
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,6 @@ import me.topchetoeu.jscript.common.json.JSON;
|
||||
import me.topchetoeu.jscript.common.json.JSONElement;
|
||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.common.parsing.Source;
|
||||
import me.topchetoeu.jscript.runtime.values.Value;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
|
||||
|
||||
public final class NumberValue extends PrimitiveValue {
|
||||
@ -25,13 +24,8 @@ public final class NumberValue extends PrimitiveValue {
|
||||
return env.get(NUMBER_PROTO);
|
||||
}
|
||||
|
||||
@Override public CompareResult compare(Environment env, Value other) {
|
||||
if (other instanceof NumberValue) return CompareResult.from(Double.compare(value, ((NumberValue)other).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;
|
||||
@Override public boolean equals(Object other) {
|
||||
if (other instanceof NumberValue val) return value == val.value;
|
||||
else return false;
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,11 @@
|
||||
package me.topchetoeu.jscript.runtime.values.primitives;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import me.topchetoeu.jscript.common.environment.Environment;
|
||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.common.parsing.Source;
|
||||
import me.topchetoeu.jscript.runtime.values.Member;
|
||||
import me.topchetoeu.jscript.runtime.values.Value;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
|
||||
|
||||
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 Value add(Environment ext, Value other) {
|
||||
return new StringValue(value + other.toString(ext).value);
|
||||
@Override public boolean equals(Object other) {
|
||||
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 Map<String, Member> getOwnMembers(Environment env) {
|
||||
|
@ -27,9 +27,6 @@ public final class SymbolValue extends PrimitiveValue {
|
||||
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 String toString() {
|
||||
|
@ -6,7 +6,6 @@ import me.topchetoeu.jscript.common.environment.Environment;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.jscript.runtime.values.KeyCache;
|
||||
import me.topchetoeu.jscript.runtime.values.Member;
|
||||
import me.topchetoeu.jscript.runtime.values.Value;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
|
||||
|
||||
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 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 Member getOwnMember(Environment env, KeyCache key) {
|
||||
|
Loading…
Reference in New Issue
Block a user