Files
swift-mirror/test/1_stdlib/ArrayTraps.swift.gyb
Erik Eckstein 2f971c22cb re-apply r26871: stdlib: Do the Array fast-path check with a single array property call.
Changes compared to the original version:
I fixed the 2 bugs and added a test for the so far undetected missing range check bug.
To keep the SIL simple (4 basic blocks for arr[x]) I extracted the slow path for getElement into a
non-inlinable function.
On the other hand I inlined _typeCheck into the slow-path function.
This speeds up NSArray accesses because now only a single objectAtIndex is required for both
type checking and element retrieving.

Update on performance: DeltaBlue is now only 12% better (and not 25%). I suspect this is because
now Arnold's tail duplication cannot detect the ObjC call in the slow path.



Swift SVN r26935
2015-04-03 06:48:25 +00:00

252 lines
6.4 KiB
Plaintext

// RUN: rm -rf %t
// RUN: mkdir -p %t
// RUN: %S/../../utils/gyb %s -o %t/ArrayTraps.swift
// RUN: %S/../../utils/line-directive %t/ArrayTraps.swift -- %target-build-swift %t/ArrayTraps.swift -o %t/a.out_Debug
// RUN: %S/../../utils/line-directive %t/ArrayTraps.swift -- %target-build-swift %t/ArrayTraps.swift -o %t/a.out_Release -O
//
// RUN: %S/../../utils/line-directive %t/ArrayTraps.swift -- %target-run %t/a.out_Debug
// RUN: %S/../../utils/line-directive %t/ArrayTraps.swift -- %target-run %t/a.out_Release
// XFAIL: linux
import StdlibUnittest
%{
# We test for bounds-checking traps for both reading and writing
# both single elements and slices of all three different array
# types.
array_types = ('Array', 'ContiguousArray', 'ArraySlice', '_UnitTestArray')
def bounds_trap(index, expr_to_write):
global io
return 'expectCrashLater()\n' + (
'let x = a[%s]' % index
if io == 'read' else
'a[%s] = %s' % (index, expr_to_write))
}%
% for ArrayTy in array_types:
var ${ArrayTy}Traps = TestSuite("${ArrayTy}Traps")
% for io in ['read', 'write']:
${ArrayTy}Traps.test("bounds1/${io}") {
var a: ${ArrayTy}<Int> = []
${bounds_trap(index='0', expr_to_write='1')}
}
${ArrayTy}Traps.test("bounds2/${io}") {
var a: ${ArrayTy}<Int> = []
${bounds_trap(index='100', expr_to_write='1')}
}
${ArrayTy}Traps.test("bounds3/${io}") {
var a: ${ArrayTy} = [ 10, 20, 30 ]
${bounds_trap(index='3', expr_to_write='1')}
}
${ArrayTy}Traps.test("sliceBounds0/${io}") {
var a: ${ArrayTy}<Int> = []
${bounds_trap(index='-1..<1', expr_to_write='ArraySlice()')}
}
${ArrayTy}Traps.test("sliceBounds1/${io}") {
var a: ${ArrayTy} = [ 1 ]
${bounds_trap(index='-1..<1', expr_to_write='ArraySlice()')}
}
${ArrayTy}Traps.test("sliceBounds2/${io}") {
var a: ${ArrayTy} = [ 1 ]
${bounds_trap(index='0..<2', expr_to_write='ArraySlice()')}
}
${ArrayTy}Traps.test("sliceBounds3/${io}") {
var a: ${ArrayTy} = [ 1 ]
${bounds_trap(index='1..<2', expr_to_write='ArraySlice()')}
}
% end
${ArrayTy}Traps.test("PopFromEmpty") {
var a: ${ArrayTy}<Int> = []
expectCrashLater()
a.removeLast()
}
% for index in -1, 2:
${ArrayTy}Traps.test("${'insert(_:atIndex:)/%s' % index}") {
var a: ${ArrayTy}<Int> = [42]
expectCrashLater()
a.insert(3, atIndex: ${index})
}
% end
% for index in -1, 1, 2:
${ArrayTy}Traps.test("${'removeAtIndex(_:)/%s' % index}") {
var a: ${ArrayTy}<Int> = [42]
expectCrashLater()
a.removeAtIndex(${index})
}
% end
% end
class Base { }
class Derived : Base { }
class Derived2 : Derived { }
ArrayTraps.test("downcast1") {
let ba: [Base] = [ Derived(), Base() ]
let da = ba as! [Derived]
let d0 = da[0]
expectCrashLater()
da[1]
}
import Foundation
ArrayTraps.test("downcast2") {
let a: [AnyObject] = [ "String", 1 ]
let sa = a as! [NSString]
let s0 = sa[0]
expectCrashLater()
sa[1]
}
ArrayTraps.test("downcast3") {
let ba: [Base] = [ Derived2(), Derived(), Base() ]
let d2a = ba as! [Derived2]
let d2a0 = d2a[0]
let d1a = d2a as [Derived]
let d1a0 = d1a[0]
let d1a1 = d1a[1]
expectCrashLater()
d1a[2]
}
@objc protocol ObjCProto { }
class ObjCBase : NSObject, ObjCProto { }
class ObjCDerived : ObjCBase { }
ArrayTraps.test("downcast4") {
let ba: [ObjCProto] = [ ObjCDerived(), ObjCBase() ]
let da = ba as! [ObjCDerived]
let d0 = da[0]
expectCrashLater()
da[1]
}
ArrayTraps.test("unsafeLength") {
var a = [ 42, 77, 88 ]
expectEqual(42, a.withUnsafeBufferPointer({ $0[0] }))
expectCrashLater()
a.withUnsafeBufferPointer {
UnsafeBufferPointer(start: $0.baseAddress, count: -1)
}
}
ArrayTraps.test("bounds_with_downcast")
.crashOutputMatches(_isDebugAssertConfiguration() ?
"fatal error: Array index out of range" : "")
.code {
let ba: [Base] = [ Derived(), Base() ]
let da = ba as! [Derived]
expectCrashLater()
let x = da[2]
}
var ArraySemanticOptzns = TestSuite("ArraySemanticOptzns")
class BaseClass {
}
class ElementClass : BaseClass {
var val: String
init(_ x: String) {
val = x
}
}
class ViolateInoutSafeySwitchToObjcBuffer {
final var anArray: [ElementClass] = []
let nsArray = NSArray(
objects: ElementClass("a"), ElementClass("b"), ElementClass("c"))
@inline(never)
func accessArrayViaInoutVolation() {
anArray = _convertNSArrayToArray(nsArray)
}
@inline(never)
func runLoop(inout A: [ElementClass]) {
// Simulate what happens if we hoist array properties out of a loop and the
// loop calls a function that violates inout safety and overrides the array.
let isNativeNoDTC = A._getArrayPropertyIsNativeNoDTC()
for i in 0..<A.count {
A._checkSubscript(i, hoistedIsNativeNoDTCBuffer: isNativeNoDTC)
A._getElement(i, hoistedIsNativeNoDTCBuffer: isNativeNoDTC)
accessArrayViaInoutVolation()
}
}
@inline(never)
func inoutViolation() {
anArray = [ ElementClass("1"), ElementClass("2"), ElementClass("3") ]
runLoop(&anArray)
}
}
ArraySemanticOptzns.test("inout_rule_violated_isNativeBuffer")
.crashOutputMatches(_isDebugAssertConfiguration() ?
"fatal error: inout rules were violated: the array was overwritten" : "")
.code {
let v = ViolateInoutSafeySwitchToObjcBuffer()
expectCrashLater()
v.inoutViolation()
}
class ViolateInoutSafeyNeedElementTypeCheck {
final var anArray : [ElementClass] = []
@inline(never)
func accessArrayViaInoutVolation() {
// Overwrite the array with one that needs an element type check.
let ba: [BaseClass] = [ BaseClass(), BaseClass() ]
anArray = ba as! [ElementClass]
}
@inline(never)
func runLoop(inout A : [ElementClass]) {
// Simulate what happens if we hoist array properties out of a loop and the
// loop calls a function that violates inout safety and overrides the array.
let isNativeNoDTC = A._getArrayPropertyIsNativeNoDTC()
for i in 0..<A.count {
A._checkSubscript(i, hoistedIsNativeNoDTCBuffer: isNativeNoDTC)
A._getElement(i, hoistedIsNativeNoDTCBuffer: isNativeNoDTC)
accessArrayViaInoutVolation()
}
}
@inline(never)
func inoutViolation() {
anArray = [ ElementClass("1"), ElementClass("2"), ElementClass("3")]
runLoop(&anArray)
}
}
ArraySemanticOptzns.test("inout_rule_violated_needsElementTypeCheck")
.crashOutputMatches(_isDebugAssertConfiguration() ?
"fatal error: inout rules were violated: the array was overwritten" : "")
.code {
let v = ViolateInoutSafeyNeedElementTypeCheck()
expectCrashLater()
v.inoutViolation()
}
runAllTests()