Current section

26 Versions

Jump to

Compare versions

18 files changed
+403 additions
-107 deletions
  @@ -169,10 +169,6 @@ const Erlang_Binary = {
169 169 const startValue = scopeData.data[0].value;
170 170 const lengthValue = scopeData.data[1].value;
171 171
172 - if (startValue < 0n) {
173 - raiseInvalidOptions();
174 - }
175 -
176 172 scopeStart = Number(startValue);
177 173 scopeLength = Number(lengthValue);
178 174 });
  @@ -470,17 +466,29 @@ const Erlang_Binary = {
470 466 ](options, 3);
471 467
472 468 // match/3 only supports :scope; reject :global, :trim, and :trim_all
473 - // Validate scope start is within subject bounds
474 - if (global || trim || trimAll || scopeStart > subject.bytes.length) {
469 + if (global || trim || trimAll) {
475 470 Interpreter.raiseArgumentError(
476 471 Interpreter.buildArgumentErrorMsg(3, "invalid options"),
477 472 );
478 473 }
479 474
475 + // Validate scope start is within subject bounds
476 + if (scopeStart < 0 || scopeStart > subject.bytes.length) {
477 + Interpreter.raiseArgumentError(
478 + Interpreter.buildArgumentErrorMsg(
479 + 3,
480 + "specified part is not wholly inside binary",
481 + ),
482 + );
483 + }
484 +
480 485 // Validate that if scopeLength is specified, scopeStart + scopeLength >= 0
481 486 if (scopeLength !== null && scopeStart + scopeLength < 0) {
482 487 Interpreter.raiseArgumentError(
483 - Interpreter.buildArgumentErrorMsg(3, "invalid options"),
488 + Interpreter.buildArgumentErrorMsg(
489 + 3,
490 + "specified part is not wholly inside binary",
491 + ),
484 492 );
485 493 }
486 494
  @@ -490,7 +498,10 @@ const Erlang_Binary = {
490 498 // Validate scope doesn't extend beyond subject
491 499 if (scopeStart + effectiveLength > subject.bytes.length) {
492 500 Interpreter.raiseArgumentError(
493 - Interpreter.buildArgumentErrorMsg(3, "invalid options"),
501 + Interpreter.buildArgumentErrorMsg(
502 + 3,
503 + "specified part is not wholly inside binary",
504 + ),
494 505 );
495 506 }
496 507
  @@ -599,15 +610,27 @@ const Erlang_Binary = {
599 610 "_parse_search_opts/2"
600 611 ](options, 3);
601 612
602 - if (global || trim || trimAll || scopeStart > subject.bytes.length) {
613 + if (global || trim || trimAll) {
603 614 Interpreter.raiseArgumentError(
604 615 Interpreter.buildArgumentErrorMsg(3, "invalid options"),
605 616 );
606 617 }
607 618
619 + if (scopeStart < 0 || scopeStart > subject.bytes.length) {
620 + Interpreter.raiseArgumentError(
621 + Interpreter.buildArgumentErrorMsg(
622 + 3,
623 + "specified part is not wholly inside binary",
624 + ),
625 + );
626 + }
627 +
608 628 if (scopeLength !== null && scopeStart + scopeLength < 0) {
609 629 Interpreter.raiseArgumentError(
610 - Interpreter.buildArgumentErrorMsg(3, "invalid options"),
630 + Interpreter.buildArgumentErrorMsg(
631 + 3,
632 + "specified part is not wholly inside binary",
633 + ),
611 634 );
612 635 }
613 636
  @@ -644,7 +667,10 @@ const Erlang_Binary = {
644 667
645 668 if (scopeStart + effectiveLength > subject.bytes.length) {
646 669 Interpreter.raiseArgumentError(
647 - Interpreter.buildArgumentErrorMsg(3, "invalid options"),
670 + Interpreter.buildArgumentErrorMsg(
671 + 3,
672 + "specified part is not wholly inside binary",
673 + ),
648 674 );
649 675 }
650 676
  @@ -1211,7 +1237,7 @@ const Erlang_Binary = {
1211 1237 ](options, 3);
1212 1238
1213 1239 // Validate scope start is within subject bounds
1214 - if (scopeStart > subject.bytes.length) {
1240 + if (scopeStart < 0 || scopeStart > subject.bytes.length) {
1215 1241 Interpreter.raiseArgumentError(
1216 1242 Interpreter.buildArgumentErrorMsg(3, "invalid options"),
1217 1243 );
  @@ -37,7 +37,12 @@ const Erlang_Elixir_Utils = {
37 37 codePoints.push(Number(codepoint.value));
38 38
39 39 if (Type.isImproperList(result)) {
40 - current = result.data[1];
40 + // cp/1 may return a multi-element improper tail, e.g. [codepoint | [rest | tail]];
41 + // reconstruct it rather than dropping everything past the second element.
42 + current =
43 + result.data.length === 2
44 + ? result.data[1]
45 + : Type.improperList(result.data.slice(1));
41 46
42 47 if (Type.isBitstring(current) && Bitstring.isEmpty(current)) {
43 48 break;
  @@ -1645,16 +1645,11 @@ const Erlang = {
1645 1645
1646 1646 // Start div/2
1647 1647 "div/2": (integer1, integer2) => {
1648 - if (!Type.isInteger(integer1) || !Type.isInteger(integer2)) {
1649 - const arg1 = Interpreter.inspect(integer1);
1650 - const arg2 = Interpreter.inspect(integer2);
1651 -
1652 - Interpreter.raiseArgumentError(
1653 - `bad argument in arithmetic expression: div(${arg1}, ${arg2})`,
1654 - );
1655 - }
1656 -
1657 - if (integer2.value === 0n) {
1648 + if (
1649 + !Type.isInteger(integer1) ||
1650 + !Type.isInteger(integer2) ||
1651 + integer2.value === 0n
1652 + ) {
1658 1653 const arg1 = Interpreter.inspect(integer1);
1659 1654 const arg2 = Interpreter.inspect(integer2);
  @@ -1017,6 +1017,37 @@ const Erlang_Lists = {
1017 1017 },
1018 1018 // End sort/2
1019 1019 // Deps: []
1020 +
1021 + // Start suffix/2
1022 + "suffix/2": (list1, list2) => {
1023 + // The Erlang implementation computes length/1 on both arguments, which
1024 + // rejects anything that is not a proper list with an "not a list" error.
1025 + if (!Type.isProperList(list1) || !Type.isProperList(list2)) {
1026 + Interpreter.raiseArgumentError(
1027 + Interpreter.buildArgumentErrorMsg(1, "not a list"),
1028 + );
1029 + }
1030 +
1031 + const length1 = list1.data.length;
1032 + const delta = list2.data.length - length1;
1033 +
1034 + // The first list cannot be a suffix of a shorter second list.
1035 + if (delta < 0) {
1036 + return Type.boolean(false);
1037 + }
1038 +
1039 + // Compare the first list against the trailing part of the second list
1040 + // in place, avoiding any intermediate allocation.
1041 + for (let i = 0; i < length1; i++) {
1042 + if (!Interpreter.isStrictlyEqual(list1.data[i], list2.data[delta + i])) {
1043 + return Type.boolean(false);
1044 + }
1045 + }
1046 +
1047 + return Type.boolean(true);
1048 + },
1049 + // End suffix/2
1050 + // Deps: []
1020 1051 };
1021 1052
1022 1053 export default Erlang_Lists;
  @@ -19,7 +19,7 @@ const Erlang_Sets = {
19 19 Interpreter.buildFunctionClauseErrorMsg(":proplists.get_value/3", [
20 20 Type.atom("version"),
21 21 opts,
22 - Type.integer(1),
22 + Type.integer(2),
23 23 ]),
24 24 );
25 25 }
Loading more files…