diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4916cac..b8a09bb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,6 +15,16 @@ jobs: - run: ./som.sh -cp core-lib/Smalltalk core-lib/TestSuite/TestHarness.som - run: ./som.sh -cp core-lib/Smalltalk:core-lib/TestSuite:core-lib/SomSom/src/compiler:core-lib/SomSom/src/vm:core-lib/SomSom/src/vmobjects:core-lib/SomSom/src/interpreter:core-lib/SomSom/src/primitives core-lib/SomSom/tests/SomSomTests.som + - name: Integration Tests + run: | + python -m pip install --upgrade pip + pip install pytest + export VM=./som.sh + export CLASSPATH=Smalltalk + export TEST_EXPECTATIONS=./integration-tests.yml + export AWFY=Examples/AreWeFastYet/Core + pytest core-lib/IntegrationTests + - run: npm ci - run: npm test - run: npm run lint diff --git a/core-lib b/core-lib index 7bf0413..a721b4f 160000 --- a/core-lib +++ b/core-lib @@ -1 +1 @@ -Subproject commit 7bf0413d9e90e0484f5bde24f44b654c3672da24 +Subproject commit a721b4ff6ecb1ade54e0afe0bd15a8e80e41af17 diff --git a/integration-tests.yml b/integration-tests.yml new file mode 100644 index 0000000..5ade639 --- /dev/null +++ b/integration-tests.yml @@ -0,0 +1,136 @@ +known_failures: [] + + +failing_as_unspecified: + - Tests/arbint_double_div_err.som + - Tests/arbint_double_div_zero_err.som + - Tests/arbint_modulus_err.som + - Tests/array_at_idx0_err.som + - Tests/array_at_idx2_err.som + - Tests/array_at_idx_err.som + - Tests/array_at_negative_idx_err.som + - Tests/array_at_put_idx0_err.som + - Tests/array_at_put_idx2_err.som + - Tests/call2.som + + # I think this one is about E vs e, but maybe also double rendering and e+59 or e59 + - Tests/double2.som + - Tests/double_double_div.som + - Tests/int_double_div.som + + # printing is not specified + # 18446744073709552000 + # or 1.8446744073709552e19 are both valid options + - Tests/integer_asdouble.som + + + # I think IEEE allows for infinities here, and we probably want that + - Tests/double3.som + - Tests/double4.som + - Tests/double5.som + - Tests/double6.som + - Tests/double7.som + - Tests/double8.som + - Tests/double9.som + - Tests/double11.som + - Tests/double13.som + + # Java seems to do some rounding in the transition, but it's also requiring bigints + - Tests/double_asinteger.som + + + - Tests/double_double_div_err.som + - Tests/double_double_div_zero_err1.som + - Tests/double_double_div_zero_err2.som + - Tests/double_double_div_zero_err3.som + - Tests/double_double_div_zero_err4.som + # - Tests/double_modulus.som + - Tests/double_modulus_err.som + + - Tests/exit_double.som + - Tests/exit_int_too_big.som + - Tests/exit_string.som + + - Tests/fromstring_double_err.som + - Tests/fromstring_err.som + + - Tests/hashcode2.som + + - Tests/inst_var_at_bad_idx.som + - Tests/inst_var_at_put_bad_idx.som + + - Tests/instance_fields_overlap/test.som + - Tests/instance_fields_overlap2.som + + - Tests/int5.som + - Tests/int8.som + - Tests/int9.som + + - Tests/int10.som + - Tests/int11.som + - Tests/int12.som + - Tests/int13.som + - Tests/int14.som + - Tests/int15.som + - Tests/int16.som + - Tests/int17.som + + # too large shifts would take too much memory to support + # need to specify this some how + - Tests/int20.som + - Tests/int21.som + - Tests/int22.som + - Tests/int23.som + - Tests/int25.som + - Tests/int27.som + - Tests/int28.som + - Tests/int31.som + + - Tests/int_double_div_err.som + - Tests/int_double_div_zero_err.som + - Tests/int_modulus.som + - Tests/int_modulus_err.som + + - Tests/load_string.som + + - Tests/mutate_methods.som + - Tests/mutate_superclass_method/test.som + + - Tests/nested_backtrace1.som + - Tests/nested_backtrace2.som + + - Tests/obj2.som + + - Tests/perform_string.som + - Tests/perform_witharguments_wrong.som + + - Tests/remainder_zero.som + - Tests/round.som + + - Tests/shift_right.som + - Tests/shift_right_too_big.som + - Tests/shift_right_type_err.som + + - Tests/str_escape_unknown.som + + - Tests/system2.som + - Tests/system_global_lookup_string.som + - Tests/system_global_put_string.som + + - Tests/test_literals_limit_1.som + - Tests/test_literals_limit_2.som + - Tests/unknown_field_write.som + + # This should be specified so that the "set" of fields cannot be changed reflectively + # they obtained array can be changed, but it is expected to have no effect. + - Tests/mutate_fields.som + + +unsupported: + # JS doesn't have a sqrt implementation for BigIntegers + - Tests/int26.som + - Tests/int29.som + + +do_not_run: + - Tests/case_insensitive.som diff --git a/src/som/interpreter/DispatchNode.js b/src/som/interpreter/DispatchNode.js index 7bf1208..48a9f64 100644 --- a/src/som/interpreter/DispatchNode.js +++ b/src/som/interpreter/DispatchNode.js @@ -21,8 +21,6 @@ */ // @ts-check -import { RuntimeException } from '../../lib/exceptions.js'; - import { assert } from '../../lib/assert.js'; import { Node } from './Node.js'; @@ -78,16 +76,16 @@ export class UninitializedSuperDispatchNode extends Node { class CachedSuperDispatchNode extends Node { constructor(selector, lookupClass) { super(null); + this.selector = selector; assert(lookupClass instanceof SClass); - const method = lookupClass.lookupInvokable(selector); - - if (method == null) { - throw new RuntimeException('Currently #dnu with super sent is not yet implemented. '); - } - this.method = method; + this.method = lookupClass.lookupInvokable(selector); } executeDispatch(frame, args) { + if (this.method === null) { + const rcvr = args[0]; + return rcvr.sendDoesNotUnderstand(this.selector, frame, args); + } return this.method.invoke(frame, args); } } diff --git a/src/som/primitives/ArrayPrimitives.js b/src/som/primitives/ArrayPrimitives.js index 26711cc..cad8907 100644 --- a/src/som/primitives/ArrayPrimitives.js +++ b/src/som/primitives/ArrayPrimitives.js @@ -34,7 +34,7 @@ function _atPut(_frame, args) { const index = args[1]; args[0].setIndexableField(index.getEmbeddedInteger() - 1, value); - return value; + return args[0]; } function _length(_frame, args) { diff --git a/src/som/primitives/IntegerPrimitives.js b/src/som/primitives/IntegerPrimitives.js index db4915a..030e401 100644 --- a/src/som/primitives/IntegerPrimitives.js +++ b/src/som/primitives/IntegerPrimitives.js @@ -23,11 +23,12 @@ import { intOrBigInt, isInIntRange } from '../../lib/platform.js'; -import { SBigInteger } from '../vmobjects/numbers.js'; +import { SBigInteger, SInteger } from '../vmobjects/numbers.js'; import { Primitives } from './Primitives.js'; import { universe } from '../vm/Universe.js'; import { SString } from '../vmobjects/SString.js'; +import { notYetImplemented } from '../../lib/assert.js'; function toNumber(val) { if (val instanceof SBigInteger) { @@ -41,7 +42,16 @@ function _asString(_frame, args) { } function _sqrt(_frame, args) { - const res = Math.sqrt(args[0].getEmbeddedInteger()); + const rcvr = args[0]; + if (rcvr instanceof SBigInteger) { + const res = Math.sqrt(rcvr.getEmbeddedBigInteger()); + if (res === BigInt(Math.floor(Number(res)))) { + return universe.newBigInteger(res); + } + return universe.newDouble(Number(res)); + } + + const res = Math.sqrt(rcvr.getEmbeddedInteger()); if (res === Math.floor(res)) { return universe.newInteger(Math.floor(res)); } @@ -117,10 +127,28 @@ function _leftShift(_frame, args) { } function _bitXor(_frame, args) { - const right = toNumber(args[1]); - const left = toNumber(args[0]); + const right = args[1]; + const left = args[0]; + + if (left instanceof SInteger && right instanceof SInteger) { + return universe.newInteger(left.getEmbeddedInteger() ^ right.getEmbeddedInteger()); + } + + if (left instanceof SInteger && right instanceof SBigInteger) { + return intOrBigInt(BigInt(left.getEmbeddedInteger()) ^ right.getEmbeddedBigInteger(), universe); + } + + if (left instanceof SBigInteger && right instanceof SBigInteger) { + return universe.newBigInteger(left.getEmbeddedBigInteger() + ^ right.getEmbeddedBigInteger()); + } + + if (left instanceof SBigInteger && right instanceof SInteger) { + return intOrBigInt(left.getEmbeddedBigInteger() + ^ BigInt(right.getEmbeddedInteger()), universe); + } - return universe.newInteger(left ^ right); + return notYetImplemented(); } function _rem(_frame, args) { diff --git a/src/som/primitives/ObjectPrimitives.js b/src/som/primitives/ObjectPrimitives.js index 07f1704..bc8811d 100644 --- a/src/som/primitives/ObjectPrimitives.js +++ b/src/som/primitives/ObjectPrimitives.js @@ -60,6 +60,9 @@ function _perform(frame, args) { const selector = args[1]; const rcvr = args[0]; const invokable = rcvr.getClass().lookupInvokable(selector); + if (invokable === null) { + return rcvr.sendDoesNotUnderstand(selector, frame, [rcvr]); + } return invokable.invoke(frame, [rcvr]); } @@ -69,19 +72,39 @@ function _performInSuperclass(frame, args) { const rcvr = args[0]; const invokable = clazz.lookupInvokable(selector); + if (invokable === null) { + return rcvr.sendDoesNotUnderstand(selector, frame, [rcvr]); + } return invokable.invoke(frame, [rcvr]); } -function _performWithArguments(_frame, args) { +function _performWithArguments(frame, args) { const directArgs = args[2].getIndexableFields(); const selector = args[1]; const rcvr = args[0]; const invokable = rcvr.getClass().lookupInvokable(selector); const newArgs = [rcvr].concat(directArgs); + if (invokable === null) { + return rcvr.sendDoesNotUnderstand(selector, frame, newArgs); + } return invokable.invoke(rcvr, newArgs); } +function _performWithArgumentsInSuperclass(frame, args) { + const clazz = args[3]; + const directArgs = args[2].getIndexableFields(); + const selector = args[1]; + const rcvr = args[0]; + + const invokable = clazz.lookupInvokable(selector); + const newArgs = [rcvr].concat(directArgs); + if (invokable === null) { + return rcvr.sendDoesNotUnderstand(selector, frame, newArgs); + } + return invokable.invoke(frame, newArgs); +} + function _instVarAt(_frame, args) { const idx = args[1]; return args[0].getField(idx.getEmbeddedInteger() - 1); @@ -91,7 +114,7 @@ function _instVarAtPut(_frame, args) { const val = args[2]; const idx = args[1]; args[0].setField(idx.getEmbeddedInteger() - 1, val); - return val; + return args[0]; } function _instVarNamed(_frame, args) { @@ -118,7 +141,7 @@ class ObjectPrimitives extends Primitives { this.installInstancePrimitive('perform:', _perform); this.installInstancePrimitive('perform:inSuperclass:', _performInSuperclass); this.installInstancePrimitive('perform:withArguments:', _performWithArguments); - this.installInstancePrimitive('perform:withArguments:inSuperclass:', null); // TODO: primitive implementation missing, also in RTruffleSOM... + this.installInstancePrimitive('perform:withArguments:inSuperclass:', _performWithArgumentsInSuperclass); this.installInstancePrimitive('instVarAt:', _instVarAt); this.installInstancePrimitive('instVarAt:put:', _instVarAtPut); this.installInstancePrimitive('instVarNamed:', _instVarNamed); diff --git a/src/som/primitives/SystemPrimitives.js b/src/som/primitives/SystemPrimitives.js index 9932edc..d62938f 100644 --- a/src/som/primitives/SystemPrimitives.js +++ b/src/som/primitives/SystemPrimitives.js @@ -54,7 +54,7 @@ function _globalPut(_frame, args) { const value = args[2]; const symbol = args[1]; universe.setGlobal(symbol, value); - return value; + return args[0]; } function _printString(_frame, args) { diff --git a/src/som/vm/Universe.js b/src/som/vm/Universe.js index 21b2d99..ca0c743 100644 --- a/src/som/vm/Universe.js +++ b/src/som/vm/Universe.js @@ -500,8 +500,8 @@ class Universe { return new SInteger(intVal); } - newBigInteger(BigIntVal) { - return new SBigInteger(BigIntVal); + newBigInteger(bigIntVal) { + return new SBigInteger(bigIntVal); } newBlock(blockMethod, contextFrame) { diff --git a/src/som/vmobjects/numbers.js b/src/som/vmobjects/numbers.js index 2068963..b9fb81b 100644 --- a/src/som/vmobjects/numbers.js +++ b/src/som/vmobjects/numbers.js @@ -136,6 +136,12 @@ export class SInteger extends SAbstractObject { if (right instanceof SInteger) { return universe.newInteger(this.intVal & right.getEmbeddedInteger()); } + + if (right instanceof SBigInteger) { + const r = Number(BigInt.asUintN(53, right.getEmbeddedBigInteger())); + return universe.newInteger(this.intVal & r); + } + notYetImplemented(); // not supported by the library and, not sure what semantics should be return null; } @@ -204,7 +210,11 @@ export class SDouble extends SAbstractObject { } primAsString() { - return universe.newString(this.doubleVal.toString()); + let val = this.doubleVal.toString(); + if (!val.includes('.') && !Number.isNaN(this.doubleVal)) { + val += '.0'; // ensure that we have a decimal point + } + return universe.newString(val); } primSubtract(right) { @@ -317,7 +327,7 @@ export class SBigInteger extends SAbstractObject { } else if (right instanceof SDouble) { return universe.newDouble(Number(this.bigIntVal)).primIntDiv(right); } else { - result = this.bigIntVal / right.getEmbeddedInteger(); + result = this.bigIntVal / BigInt(right.getEmbeddedInteger()); } return universe.newBigInteger(result); } @@ -334,8 +344,17 @@ export class SBigInteger extends SAbstractObject { return universe.newBigInteger(result); } - primAnd(_right) { + primAnd(right) { + if (right instanceof SBigInteger) { + return intOrBigInt(this.bigIntVal & right.getEmbeddedBigInteger(), universe); + } + + if (right instanceof SInteger) { + return intOrBigInt(this.bigIntVal & BigInt(right.getEmbeddedInteger()), universe); + } + notYetImplemented(); // not supported by the library and, not sure what semantics should be + return null; } primEquals(right) {