// 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. // 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(); } on Error 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('constant 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'); }