blob: 5975a0b302b36cfbda5082a7bb79e22dff5af077 [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// @dart = 2.7
// Test that optimized JSArray indexers generate the same error as dynamically
// dispatched calls.
import 'package:expect/expect.dart';
@pragma('dart2js:noInline')
@pragma('dart2js:assumeDynamic')
confuse(x) => x;
Error getError(action(), name, part) {
try {
action();
} catch (e) {
return e;
}
Expect.fail('must throw: $name: $part');
}
indexErrorContainsIndex() {
makeFault(i) => () => confuse([])[i];
var name = 'index error contains index';
var e1 = getError(makeFault(1234), name, 'small');
var e2 = getError(makeFault(1234000), name, 'medium');
var e3 = getError(makeFault(1234000000000), name, 'large');
Expect.equals('$e1', '$e2'.replaceAll('000', ''));
Expect.equals('$e1', '$e3'.replaceAll('000', ''));
Expect.equals('$e1'.length + 3, '$e2'.length);
Expect.equals('$e1'.length + 9, '$e3'.length);
}
compare(name, fault1(), fault2(), fault3()) {
var e1 = getError(fault1, name, 'fault1');
var e2 = getError(fault2, name, 'fault2');
var e3 = getError(fault3, name, 'fault3');
Expect.equals('$e1', '$e2', '$name: fault1 vs fault2');
Expect.equals('$e1', '$e3', '$name: fault1 vs fault3');
}
// These tests are a bit tedious and avoid common helpers with higher order
// functions to keep the type inference for each test independent from the
// others.
//
// The 'constant' tests have a constant index which might permit different
// optimizations to a variable index. e.g. the compiler might determine HUGE is
// always out of range since the maximum JavaScript Array length is 2^32.
//
// The 'variable' forms take the index as an argument.
const int HUGE = 1000000000000;
constantIndexEmpty() {
// Single dynamic receiver indexing might go via one-shot interceptor that
// might have an accelerated path.
fault1() => confuse([])[0];
fault2() {
var a = [];
while (confuse(false)) a.add(1);
// Easily inferred type and open coded indexer.
return a[0];
}
fault3() {
var a = confuse([]);
// Multiple indexing might go via shared interceptor.
return [a[0], a[1], a[2]];
}
compare('constant index on empty list', fault1, fault2, fault3);
}
constantIndexHugeEmpty() {
// Single dynamic receiver indexing might go via one-shot interceptor that
// might have an accelerated path.
fault1() => confuse([])[HUGE];
fault2() {
var a = [];
while (confuse(false)) a.add(1);
return a[HUGE];
}
fault3() {
var a = confuse([]);
return [a[HUGE], a[1], a[2]];
}
compare(
'constant index on empty list with huge index', fault1, fault2, fault3);
}
constantIndexNonempty() {
// Single dynamic receiver indexing might go via one-shot interceptor that
// might have an accelerated path.
fault1() => confuse([1])[1];
fault2() {
var a = [1];
while (confuse(false)) a.add(1);
// Easily inferred type and open coded indexer.
return a[1];
}
fault3() {
var a = confuse([1]);
// Multiple indexing might go via shared interceptor.
return [a[1], a[2], a[3]];
}
compare('constant index on non-empty list', fault1, fault2, fault3);
}
constantIndexHugeNonempty() {
// Single dynamic receiver indexing might go via one-shot interceptor that
// might have an accelerated path.
fault1() => confuse([1])[HUGE];
fault2() {
var a = [1];
while (confuse(false)) a.add(1);
// Easily inferred type and open coded indexer.
return a[HUGE];
}
fault3() {
var a = confuse([1]);
// Multiple indexing might go via shared interceptor.
return [a[HUGE], a[1], a[2]];
}
compare('constant index on non-empty list with huge index', fault1, fault2,
fault3);
}
constantIndexSetEmpty() {
fault1() {
// Single dynamic receiver indexing might go via one-shot interceptor that
// might have an accelerated path.
confuse([])[0] = 0;
}
fault2() {
var a = [];
while (confuse(false)) a.add(1);
// Easily inferred type and open coded indexer.
a[0] = 0;
return a;
}
fault3() {
var a = confuse([]);
// Multiple indexing might go via shared interceptor.
a[0] = 0;
a[1] = 0;
a[2] = 0;
return a;
}
compare('coinstant index-set on empty list', fault1, fault2, fault3);
}
constantIndexSetNonempty() {
fault1() {
// Single dynamic receiver indexing might go via one-shot interceptor that
// might have an accelerated path.
confuse([1])[1] = 0;
}
fault2() {
var a = [1];
while (confuse(false)) a.add(1);
// Easily inferred type and open coded indexer.
a[1] = 0;
return a;
}
fault3() {
var a = confuse([1]);
// Multiple indexing might go via shared interceptor.
a[0] = 0;
a[1] = 0;
a[2] = 0;
return a;
}
compare('constant index-set on non-empty list', fault1, fault2, fault3);
}
variableIndexEmpty(index, qualifier) {
// Single dynamic receiver indexing might go via one-shot interceptor that
// might have an accelerated path.
fault1() => confuse([])[index];
fault2() {
var a = [];
while (confuse(false)) a.add(1);
// Easily inferred type and open coded indexer.
return a[index];
}
fault3() {
var a = confuse([]);
// Multiple indexing might go via shared interceptor.
return [a[index], a[1], a[2]];
}
compare('general index on empty list $qualifier', fault1, fault2, fault3);
}
variableIndexNonempty(index, qualifier) {
// Single dynamic receiver indexing might go via one-shot interceptor that
// might have an accelerated path.
fault1() => confuse([1])[index];
fault2() {
var a = [1];
while (confuse(false)) a.add(1);
// Easily inferred type and open coded indexer.
return a[index];
}
fault3() {
var a = confuse([1]);
// Multiple indexing might go via shared interceptor.
return [a[index], a[1], a[2]];
}
compare(
'variable index on non-empty list $qualifier', fault1, fault2, fault3);
}
variableIndexSetEmpty(index, qualifier) {
fault1() {
var a = confuse([]);
// Single dynamic receiver indexing might go via one-shot interceptor that
// might have an accelerated path.
a[index] = 1;
return a;
}
fault2() {
var a = [];
while (confuse(false)) a.add(1);
// Easily inferred type and open coded indexer.
a[index] = 1;
return a;
}
fault3() {
var a = confuse([]);
// Multiple indexing might go via shared interceptor.
a[index] = 1;
a[2] = 2;
a[3] = 3;
return a;
}
compare(
'variable index-set on empty list $qualifier', fault1, fault2, fault3);
}
variableIndexSetNonempty(index, qualifier) {
fault1() {
var a = confuse([1]);
// Single dynamic receiver indexing might go via one-shot interceptor that
// might have an accelerated path.
a[index] = 1;
return a;
}
fault2() {
var a = [1];
while (confuse(false)) a.add(1);
// Easily inferred type and open coded indexer.
a[index] = 1;
return a;
}
fault3() {
var a = confuse([1]);
// Multiple indexing might go via shared interceptor.
a[index] = 1;
a[2] = 2;
a[3] = 3;
return a;
}
compare('variable index-set on non-empty list $qualifier', fault1, fault2,
fault3);
}
main() {
indexErrorContainsIndex();
constantIndexEmpty();
constantIndexHugeEmpty();
constantIndexNonempty();
constantIndexHugeNonempty();
constantIndexSetEmpty();
constantIndexSetNonempty();
variableIndexEmpty(0, 'zero index');
variableIndexEmpty(10, 'small index');
variableIndexEmpty(-1, 'negative index');
variableIndexEmpty(HUGE, 'huge index');
variableIndexNonempty(10, 'small index');
variableIndexNonempty(-1, 'negative index');
variableIndexNonempty(HUGE, 'huge index');
variableIndexSetEmpty(0, 'zero index');
variableIndexSetEmpty(10, 'small index');
variableIndexSetEmpty(-1, 'negative index');
variableIndexSetEmpty(HUGE, 'huge index');
variableIndexSetNonempty(10, 'small index');
variableIndexSetNonempty(-1, 'negative index');
variableIndexSetNonempty(HUGE, 'huge index');
}