diff --git a/src/core/NativeTypes.nqp b/src/core/NativeTypes.nqp index a0af8595d9..e46828d85e 100644 --- a/src/core/NativeTypes.nqp +++ b/src/core/NativeTypes.nqp @@ -18,13 +18,23 @@ my module EXPORTHOW { nqp::scwbenable(); } +#?if js +my native int is repr('P6int') is nativesize(32) { } +#?endif +#?if !js my native int is repr('P6int') { } +#?endif my native int64 is repr('P6int') is nativesize(64) { } my native int32 is repr('P6int') is nativesize(32) { } my native int16 is repr('P6int') is nativesize(16) { } my native int8 is repr('P6int') is nativesize( 8) { } +#?if js +my native uint is repr('P6int') is nativesize(32) is unsigned { } +#?endif +#?if !js my native uint is repr('P6int') is unsigned { } +#?endif my native uint64 is repr('P6int') is nativesize(64) is unsigned { } my native uint32 is repr('P6int') is nativesize(32) is unsigned { } my native uint16 is repr('P6int') is nativesize(16) is unsigned { } diff --git a/src/vm/js/Chunk.nqp b/src/vm/js/Chunk.nqp index 5b88c504a2..6d675f3f23 100644 --- a/src/vm/js/Chunk.nqp +++ b/src/vm/js/Chunk.nqp @@ -15,6 +15,9 @@ my $T_UINT16 := 9; # We use a javascript number but always treat it as a 16bit i my $T_UINT8 := 10; # We use a javascript number but always treat it as a 8bit integer my $T_UINT32 := 11; # We use a javascript number but always treat it as a unsigned 32bit integer +my $T_INT64 := 12; # We use a BigInt but treat it as a signed 64bit integer +my $T_UINT64 := 13; # We use a BigInt but treat it as a unsigned 64bit integer + my $T_VOID := -1; # Value of this type shouldn't exist, we use a "" as the expr my $T_NONVAL := -2; # something that is not a nqp value diff --git a/src/vm/js/Compiler.nqp b/src/vm/js/Compiler.nqp index b9b29b4c32..7d26517830 100644 --- a/src/vm/js/Compiler.nqp +++ b/src/vm/js/Compiler.nqp @@ -677,7 +677,7 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce { } if $desired == $T_STR { - if $got_int || $got == $T_UINT32 { + if $got_int || $got == $T_UINT32 || $got == $T_INT64 || $got == $T_UINT64 { return Chunk.new($T_STR, $chunk.expr ~ '.toString()', $chunk); } elsif $got == $T_NUM { @@ -714,9 +714,11 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce { %convert{$T_INT} := 'intToObj'; %convert{$T_INT8} := 'intToObj'; %convert{$T_INT16} := 'intToObj'; + %convert{$T_INT64} := 'int64ToObj'; %convert{$T_UINT8} := 'intToObj'; %convert{$T_UINT16} := 'intToObj'; %convert{$T_UINT32} := 'intToObj'; + %convert{$T_UINT64} := 'int64ToObj'; %convert{$T_NUM} := 'numToObj'; %convert{$T_STR} := 'strToObj'; %convert{$T_RETVAL} := 'retval'; @@ -735,6 +737,18 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce { } } + if $desired == $T_UINT64 { + if $got == $T_INT64 { + return Chunk.new($T_UINT64, "BigInt.asUintN(64, {$chunk.expr})", $chunk); + } + } + + if $desired == $T_INT64 { + if $got == $T_UINT64 { + return Chunk.new($T_INT64, "BigInt.asIntN(64, {$chunk.expr})", $chunk); + } + } + if self.is_fancy_int($desired) { my $int_chunk := $got == $T_INT ?? $chunk !! self.coerce($chunk, $T_INT); return Chunk.new($desired, self.int_to_fancy_int($desired, $int_chunk.expr) , $int_chunk); @@ -1399,7 +1413,12 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce { multi method as_js(QAST::IVal $node, :$want) { - Chunk.new($T_INT,'('~$node.value()~')', :$node); + if $want == $T_INT64 || $want == $T_UINT64 { + Chunk.new($want,'('~$node.value()~'n)', :$node); + } else { + # TODO think about 64bit values + Chunk.new($T_INT,'('~$node.value()~')', :$node); + } } multi method as_js(QAST::NVal $node, :$want) { @@ -1614,7 +1633,7 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce { } - my @types := [$T_OBJ, $T_INT, $T_NUM, $T_STR]; + my @types := [$T_OBJ, $T_INT, $T_NUM, $T_STR, $T_INT64, $T_UINT64]; method type_from_typeobj($typeobj) { my int $type := nqp::objprimspec($typeobj); if $type == 1 { @@ -1632,12 +1651,15 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce { else { $T_INT; } - } else { + } + else { @types[$type]; } } my @suffix := ['', '_i', '_n', '_s']; + @suffix[$T_INT64] := '_i64'; + @suffix[$T_UINT64] := '_u64'; method suffix_from_type($type) { self.is_fancy_int($type) ?? '_i' !! @suffix[$type]; @@ -1747,7 +1769,7 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce { Chunk.void($check, "if (!{$check.expr}) return nqp.paramcheckfailed(HLL, $*CTX, Array.prototype.slice.call(arguments));\n"); } - my %default_value := nqp::hash($T_OBJ, 'nqp.Null', $T_INT, '0', $T_NUM, '0', $T_STR, 'nqp.null_s', $T_INT16, '0', $T_INT8, '0', $T_UINT8, '0', $T_UINT16, '0', $T_UINT32, '0'); + my %default_value := nqp::hash($T_OBJ, 'nqp.Null', $T_INT, '0', $T_NUM, '0', $T_STR, 'nqp.null_s', $T_INT16, '0', $T_INT8, '0', $T_UINT8, '0', $T_UINT16, '0', $T_UINT32, '0', $T_UINT64, '0n', $T_INT64, '0n'); method declare_var(QAST::Var $node) { my int $type := self.type_from_typeobj($node.returns); diff --git a/src/vm/js/Operations.nqp b/src/vm/js/Operations.nqp index 6791136760..2ed99032dd 100644 --- a/src/vm/js/Operations.nqp +++ b/src/vm/js/Operations.nqp @@ -147,6 +147,8 @@ class QAST::OperationsJS { method OBJ() { $T_OBJ } method INT() { $T_INT } + method INT64() { $T_INT64 } + method UINT64() { $T_UINT64 } method STR() { $T_STR } method NUM() { $T_NUM } method BOOL() { $T_BOOL } @@ -182,6 +184,8 @@ class QAST::OperationsJS { add_assign_op('assign_n', $T_NUM); add_assign_op('assign_s', $T_STR); + add_assign_op('assign_i64', $T_INT64); + add_assign_op('assign_u64', $T_UINT64); add_simple_op('decont', $T_OBJ, [$T_OBJ], :method_call, :ctx, :await); @@ -256,6 +260,11 @@ class QAST::OperationsJS { add_simple_op('gcd_i', $T_INT, [$T_INT, $T_INT]); add_simple_op('lcm_i', $T_INT, [$T_INT, $T_INT]); + + # 64 bit integer arithmetic + add_simple_op('add_i64', $T_INT64, [$T_INT64, $T_INT64], sub ($a, $b) {"BigInt.asIntN(64, $a + $b)"}); + add_simple_op('sub_i64', $T_INT64, [$T_INT64, $T_INT64], sub ($a, $b) {"BigInt.asIntN(64, $a - $b)"}); + add_op('chain', sub ($comp, $node, :$want) { my str $ret := $*BLOCK.add_tmp; diff --git a/src/vm/js/nqp-runtime/container-specs.js b/src/vm/js/nqp-runtime/container-specs.js index f55e6b3da1..283be2d363 100644 --- a/src/vm/js/nqp-runtime/container-specs.js +++ b/src/vm/js/nqp-runtime/container-specs.js @@ -248,6 +248,54 @@ class NativeRef { this.set(value.$$getStr()); } }); + } else if (primitiveType == 4) { // int64 + this.STable.addInternalMethods(class { + $$iscont_s() { + return 1; + } + + $$iscont() { + return 1; + } + + $$isrwcont() { + return 1; + } + + $$assign_i64(ctx, value) { + this.set(value); + } + + $$assign_u64(ctx, value) { + this.set(value); + } + + $$getStr() { + return this.get().toString(); + } + + $$getInt64() { + return this.get(); + } + + $$getUint64() { + return BigInt.asUintN(64, this.get()); + } + + $$decont(ctx) { + let hll = STable.hllOwner; + if (hll === undefined) { + hll = ctx.codeRef().staticCode.hll; + } + + const type = hll.get('int_box'); + + const repr = type._STable.REPR; + const obj = repr.allocate(type._STable); + obj.$$setInt64(this.get()); + return obj; + } + }); } else { throw 'incorrect type of NativeRef: ' + primitiveType; } diff --git a/src/vm/js/nqp-runtime/core.js b/src/vm/js/nqp-runtime/core.js index 5ba008f1ff..8faf43edbf 100644 --- a/src/vm/js/nqp-runtime/core.js +++ b/src/vm/js/nqp-runtime/core.js @@ -206,6 +206,18 @@ const intToObj = exports.intToObj = function(currentHLL, i) { } }; +exports.int64ToObj = function(currentHLL, i) { + const type = currentHLL.get('int_box'); + if (!type) { + return new NQPInt(i); + } else { + const repr = type._STable.REPR; + const obj = repr.allocate(type._STable); + obj.$$setInt64(i); + return obj; + } +}; + const numToObj = exports.numToObj = function(currentHLL, n) { const type = currentHLL.get('num_box'); if (!type) { @@ -1159,7 +1171,7 @@ op.objprimspec = function(obj) { } else if (obj instanceof NQPStr) { return 3; } else { - return (obj._STable && obj._STable.REPR.boxedPrimitive ? obj._STable.REPR.boxedPrimitive : 0); + return (obj._STable && obj._STable.REPR.boxedPrimitive ? obj._STable.REPR.boxedPrimitive() : 0); } } else { throw new NQPException(`objprimspec can't handle things of type: ${typeof obj}`); diff --git a/src/vm/js/nqp-runtime/refs.js b/src/vm/js/nqp-runtime/refs.js index 4cfe5361f1..c2ccda5194 100644 --- a/src/vm/js/nqp-runtime/refs.js +++ b/src/vm/js/nqp-runtime/refs.js @@ -106,6 +106,20 @@ helpers.lexRef_i = function(currentHLL, get, set) { return ref; }; +helpers.lexRef_i64 = function(currentHLL, get, set) { + const refType = currentHLL.get('int64_lex_ref'); + if (refType === undefined) { + throw 'No int64 lexical reference type registered for current HLL'; + } + const STable = refType._STable; + const ref = STable.REPR.allocate(STable); + ref.get = get; + ref.set = set; + return ref; +}; + +helpers.lexRef_u64 = helpers.lexRef_i64; + helpers.lexRef_s = function(currentHLL, get, set) { const refType = currentHLL.get('str_lex_ref'); if (refType === undefined) { diff --git a/src/vm/js/nqp-runtime/reprs.js b/src/vm/js/nqp-runtime/reprs.js index 744922868e..f085b3f11a 100644 --- a/src/vm/js/nqp-runtime/reprs.js +++ b/src/vm/js/nqp-runtime/reprs.js @@ -131,6 +131,9 @@ function slotToAttr(slot) { } class REPR { + boxedPrimitive() { + return 0; + } }; REPR.prototype.allocate = basicAllocate; REPR.prototype.typeObjectFor = basicTypeObjectFor; @@ -833,10 +836,13 @@ class P6int extends REPR { this.bits = cursor.varint(); this.isUnsigned = cursor.varint(); } + + boxedPrimitive() { + return this.bits == 64 ? (this.isUnsigned ? 5 : 4) : 1; + } }; P6int.prototype.flattenedDefault = '0'; -P6int.prototype.boxedPrimitive = 1; P6int.prototype.flattenSTable = true; @@ -902,9 +908,12 @@ class P6num extends REPR { return obj; }); } + + boxedPrimitive() { + return 2; + } }; -P6num.prototype.boxedPrimitive = 2; P6num.prototype.flattenSTable = true; P6num.prototype.flattenedDefault = '0'; @@ -968,9 +977,12 @@ class P6str extends REPR { return obj; }); } + + boxedPrimitive() { + return 3; + } }; -P6str.prototype.boxedPrimitive = 3; P6str.prototype.flattenSTable = true; P6str.prototype.flattenedDefault = 'nullStr'; @@ -1091,7 +1103,7 @@ class NFA extends REPR { reprs.NFA = NFA; function primType(type) { - return type !== Null ? (type._STable.REPR.boxedPrimitive || 0): 0; + return type !== Null ? (type._STable.REPR.boxedPrimitive()): 0; } // TODO rework VMArray to be more correct @@ -1601,13 +1613,30 @@ function getBI(obj) { function getIntFromBI(n) { if (n < -(2n**63n) || 2n**63n <= n) { - // TODO - put exact number of bits into exception throw new NQPException(`Cannot unbox ${bignum.bitSize(n)} bit wide bigint into native integer`); } else { return Number(n) | 0; } } +function getInt64FromBI(n) { + if (n < -(2n**63n) || 2n**63n <= n) { + throw new NQPException(`Cannot unbox ${bignum.bitSize(n)} bit wide bigint into native 64bit integer`); + } else { + return n; + } +} + +function getUint64FromBI(n) { + if (n < 0n) { + throw new NQPException(`Cannot unbox negative bigint into unsigned native 64bit integer`); + } else if (2n**64n <= n) { + throw new NQPException(`Cannot unbox ${bignum.bitSize(n)} bit wide bigint into unsigned native 64bit integer`); + } else { + return n; + } +} + class P6bigint extends REPR { setupSTable(STable) { STable.addInternalMethods(class { @@ -1615,10 +1644,22 @@ class P6bigint extends REPR { this.value = BigInt(value); } + $$setInt64(value) { + this.value = value; + } + $$getInt() { return getIntFromBI(this.value); } + $$getInt64() { + return getInt64FromBI(this.value); + } + + $$getUint64() { + return getUint64FromBI(this.value); + } + $$setBignum(value) { this.value = value; } @@ -1691,6 +1732,10 @@ class P6bigint extends REPR { this[name] = BigInt(value); } + $$setInt64(value) { + this[name] = value; + } + $$getInt() { return getIntFromBI(this[name]); } @@ -1699,6 +1744,14 @@ class P6bigint extends REPR { return getIntFromBI(this[name]); } + $$getInt64() { + return getInt64FromBI(this[name]); + } + + $$getUint64() { + return getUint64FromBI(this[name]); + } + $$getBignum() { return this[name]; } @@ -2281,7 +2334,7 @@ class NativeRef extends REPR { compose(STable, reprInfoHash) { const nativeref = reprInfoHash.content.get('nativeref').content; const type = nativeref.get('type'); - this.primitiveType = type._STable.REPR.boxedPrimitive; + this.primitiveType = type._STable.REPR.boxedPrimitive(); this.refkind = nativeref.get('refkind'); } diff --git a/src/vm/js/nqp-runtime/runtime.js b/src/vm/js/nqp-runtime/runtime.js index 5051a4d466..0a6de1db74 100644 --- a/src/vm/js/nqp-runtime/runtime.js +++ b/src/vm/js/nqp-runtime/runtime.js @@ -60,6 +60,7 @@ exports.arg = core.arg; exports.intToObj = core.intToObj; exports.numToObj = core.numToObj; exports.strToObj = core.strToObj; +exports.int64ToObj = core.int64ToObj; exports.EvalResult = core.EvalResult; @@ -595,6 +596,8 @@ const chunkNamesToTypes = { T_UINT16: 9, T_UINT8: 10, T_UINT32: 11, + T_INT64: 12, + T_UINT64: 13, T_VOID: -1, T_NONVAL: -2,