| 1 | """ |
|---|
| 2 | This module contains BoxMember on down, except Property and Indexer which have their own files. |
|---|
| 3 | """ |
|---|
| 4 | |
|---|
| 5 | interface IBoxMember inherits IMember is partial |
|---|
| 6 | """ |
|---|
| 7 | Anything that can be a member of a box should implement IBoxMember. |
|---|
| 8 | This includes methods, properties, enums and more. |
|---|
| 9 | It even includes Box as nested boxes are an anticipated feature. |
|---|
| 10 | """ |
|---|
| 11 | get docString as String? |
|---|
| 12 | get idToken as IToken |
|---|
| 13 | get isNames as String* |
|---|
| 14 | get parentBox as Box |
|---|
| 15 | get token as IToken |
|---|
| 16 | |
|---|
| 17 | def cobraSourceSignature as String |
|---|
| 18 | def mangleName(name as String) as String |
|---|
| 19 | # TODO require .compiler.curBoxMember is this |
|---|
| 20 | def mergedIntoPartialBox(newBox as Box) |
|---|
| 21 | require |
|---|
| 22 | newBox is not .parentBox |
|---|
| 23 | newBox.name == .parentBox.name |
|---|
| 24 | |
|---|
| 25 | |
|---|
| 26 | interface IBoxCodeMember inherits IBoxMember |
|---|
| 27 | |
|---|
| 28 | get hasParams as bool |
|---|
| 29 | get isExtensionMember as bool |
|---|
| 30 | get params as IList<of Param> |
|---|
| 31 | get testMethods as IList<of TestMethod> |
|---|
| 32 | |
|---|
| 33 | def matchesSignature(member as IBoxCodeMember) as bool |
|---|
| 34 | |
|---|
| 35 | |
|---|
| 36 | interface IOverloadable inherits IBoxCodeMember |
|---|
| 37 | """ |
|---|
| 38 | Implemented by AbstractMethod and Indexer. |
|---|
| 39 | """ |
|---|
| 40 | |
|---|
| 41 | pro overloadGroup as MemberOverload? |
|---|
| 42 | def cobraSourceSignature(includeShared as bool) as String |
|---|
| 43 | |
|---|
| 44 | |
|---|
| 45 | class BoxMember |
|---|
| 46 | is abstract, partial |
|---|
| 47 | inherits NamedNode |
|---|
| 48 | implements IBoxMember |
|---|
| 49 | |
|---|
| 50 | shared |
|---|
| 51 | var _accessLevels = ['public', 'protected', 'internal', 'protected internal', 'private'] |
|---|
| 52 | """ |
|---|
| 53 | The access level names are the same in both Cobra and C#. |
|---|
| 54 | """ |
|---|
| 55 | |
|---|
| 56 | var _idToken as IToken |
|---|
| 57 | var _parentBox as Box |
|---|
| 58 | var _docString as String? |
|---|
| 59 | var _isNames as IList<of String>? |
|---|
| 60 | var _attribs as AttributeList |
|---|
| 61 | var _overloadGroup as MemberOverload? |
|---|
| 62 | var _testMethods as List<of TestMethod>? |
|---|
| 63 | var _matchingBaseMember as IBoxCodeMember? |
|---|
| 64 | var _sharedMethodBacking as String? |
|---|
| 65 | var _sharedMethodBackingIsAlias as bool |
|---|
| 66 | var _aliasedMethodBacking as String? |
|---|
| 67 | var _binaryName as String? |
|---|
| 68 | |
|---|
| 69 | cue init(token as IToken, idToken as IToken, parentBox as Box, name as String, isNames as String*, attribs as AttributeList) |
|---|
| 70 | .init(token, idToken, parentBox, name, isNames, attribs, nil) |
|---|
| 71 | |
|---|
| 72 | cue init(token as IToken, idToken as IToken, parentBox as Box, name as String, isNames as String*, attribs as AttributeList, docString as String?) |
|---|
| 73 | base.init(idToken, name) |
|---|
| 74 | _idToken = idToken |
|---|
| 75 | _isNames = isNames.toList |
|---|
| 76 | _attribs = attribs |
|---|
| 77 | _parentBox = parentBox |
|---|
| 78 | _docString = docString |
|---|
| 79 | |
|---|
| 80 | ## Properties |
|---|
| 81 | |
|---|
| 82 | get attributes from _attribs |
|---|
| 83 | |
|---|
| 84 | get idToken from var |
|---|
| 85 | |
|---|
| 86 | get parentBox from var |
|---|
| 87 | |
|---|
| 88 | get canHaveMatchingBaseMember as bool |
|---|
| 89 | """ |
|---|
| 90 | Used by _computeMatchingBaseMembers to skip over members that cannot have base members. |
|---|
| 91 | Typically, explicit interface implementations like 'ICollection.IsSynchronized' |
|---|
| 92 | """ |
|---|
| 93 | return '.' not in _name |
|---|
| 94 | |
|---|
| 95 | pro binaryName from var |
|---|
| 96 | """ |
|---|
| 97 | If this BoxMember was scanned from an assembly (usually a DLL), this property holds the assembly-based name. |
|---|
| 98 | For example, you might say "writeLine" in Cobra, but the binary name is probably "WriteLine" and *could be* "writeLine". |
|---|
| 99 | """ |
|---|
| 100 | |
|---|
| 101 | pro docString from var |
|---|
| 102 | |
|---|
| 103 | get isNames as String* |
|---|
| 104 | for name in _isNames, yield name |
|---|
| 105 | |
|---|
| 106 | pro overloadGroup from var |
|---|
| 107 | |
|---|
| 108 | pro sharedMethodBacking from var |
|---|
| 109 | |
|---|
| 110 | pro sharedMethodBackingIsAlias from var |
|---|
| 111 | |
|---|
| 112 | pro aliasedMethodBacking from var |
|---|
| 113 | |
|---|
| 114 | get testMethods as IList<of TestMethod> |
|---|
| 115 | if _testMethods is nil, _testMethods = List<of TestMethod>() |
|---|
| 116 | return _testMethods to ! |
|---|
| 117 | |
|---|
| 118 | get isAbstract as bool |
|---|
| 119 | return 'abstract' in _isNames |
|---|
| 120 | |
|---|
| 121 | get isCallable as bool |
|---|
| 122 | return false |
|---|
| 123 | |
|---|
| 124 | get isInternal as bool |
|---|
| 125 | return 'internal' in _isNames |
|---|
| 126 | |
|---|
| 127 | get isOverride as bool |
|---|
| 128 | return 'override' in _isNames |
|---|
| 129 | |
|---|
| 130 | get isProtected as bool |
|---|
| 131 | return 'protected' in _isNames |
|---|
| 132 | |
|---|
| 133 | get isPrivate as bool |
|---|
| 134 | return 'private' in _isNames |
|---|
| 135 | |
|---|
| 136 | get isPublic as bool |
|---|
| 137 | return 'public' in _isNames |
|---|
| 138 | |
|---|
| 139 | get isReadOnly as bool |
|---|
| 140 | return 'readonly' in _isNames |
|---|
| 141 | |
|---|
| 142 | get isShared as bool |
|---|
| 143 | return 'shared' in _isNames |
|---|
| 144 | |
|---|
| 145 | pro isUsed as bool |
|---|
| 146 | get |
|---|
| 147 | return base.isUsed |
|---|
| 148 | set |
|---|
| 149 | base.isUsed = value |
|---|
| 150 | .resultType.isUsed = true |
|---|
| 151 | |
|---|
| 152 | get englishName as String is abstract |
|---|
| 153 | |
|---|
| 154 | get defaultAccessLevel as String |
|---|
| 155 | if .isInterfaceMember and not .isExtensionMember |
|---|
| 156 | return '' |
|---|
| 157 | if .name.startsWith('__') |
|---|
| 158 | return 'private' |
|---|
| 159 | if .name.startsWith('_') |
|---|
| 160 | return if(.isStructMember, 'private', 'protected') |
|---|
| 161 | return 'public' |
|---|
| 162 | |
|---|
| 163 | get hasVariParams as bool |
|---|
| 164 | """ |
|---|
| 165 | Retuns true if the class member takes variable number of arguments. |
|---|
| 166 | """ |
|---|
| 167 | return false |
|---|
| 168 | |
|---|
| 169 | get hasParams as bool |
|---|
| 170 | """ |
|---|
| 171 | Returns true if the class member has one or more parameters defined. |
|---|
| 172 | """ |
|---|
| 173 | return .params.count > 0 |
|---|
| 174 | |
|---|
| 175 | get params as IList<of Param> |
|---|
| 176 | """ |
|---|
| 177 | Returns the list of params of this method, possibly empty. |
|---|
| 178 | Checking hasParams first is recommended since it's more efficient. |
|---|
| 179 | """ |
|---|
| 180 | return List<of Param>() |
|---|
| 181 | |
|---|
| 182 | get isClassMember as bool |
|---|
| 183 | return _parentBox inherits Class or (_parentBox inherits Extension and (_parentBox to Extension).extendedBox inherits Class) |
|---|
| 184 | |
|---|
| 185 | get isInterfaceMember as bool |
|---|
| 186 | return _parentBox inherits Interface or (_parentBox inherits Extension and (_parentBox to Extension).extendedBox inherits Interface) |
|---|
| 187 | |
|---|
| 188 | get isStructMember as bool |
|---|
| 189 | return _parentBox inherits Struct or (_parentBox inherits Extension and (_parentBox to Extension).extendedBox inherits Struct) |
|---|
| 190 | |
|---|
| 191 | get isExtensionMember as bool |
|---|
| 192 | return _parentBox inherits Extension |
|---|
| 193 | |
|---|
| 194 | pro matchingBaseMember as IBoxCodeMember? |
|---|
| 195 | get |
|---|
| 196 | return _matchingBaseMember |
|---|
| 197 | set |
|---|
| 198 | _matchingBaseMember = value |
|---|
| 199 | |
|---|
| 200 | pro parentNameSpace as NameSpace? |
|---|
| 201 | get |
|---|
| 202 | return nil |
|---|
| 203 | set |
|---|
| 204 | throw NotSupportedException() |
|---|
| 205 | |
|---|
| 206 | get requiresThis as bool |
|---|
| 207 | return not _name.startsWith('_') |
|---|
| 208 | |
|---|
| 209 | get resultType as IType is abstract |
|---|
| 210 | """ |
|---|
| 211 | The result type of the var, method or property. |
|---|
| 212 | """ |
|---|
| 213 | |
|---|
| 214 | get shouldBeVirtual as bool |
|---|
| 215 | return false |
|---|
| 216 | |
|---|
| 217 | |
|---|
| 218 | ## Other |
|---|
| 219 | |
|---|
| 220 | def cobraSourceSignature as String |
|---|
| 221 | return .idString |
|---|
| 222 | |
|---|
| 223 | def unNilReturnType |
|---|
| 224 | """ |
|---|
| 225 | Subclasses should override to change their "return type node" to non-nilable. |
|---|
| 226 | No need to invoke `base`. |
|---|
| 227 | This is invoked to fix up the CLR library which does not indicate, for example, that StringBuilder.toString returns String not String? |
|---|
| 228 | """ |
|---|
| 229 | pass |
|---|
| 230 | |
|---|
| 231 | def mangleName(name as String) as String |
|---|
| 232 | require .compiler.curBoxMember is this |
|---|
| 233 | return name |
|---|
| 234 | |
|---|
| 235 | def mergedIntoPartialBox(newBox as Box) |
|---|
| 236 | require |
|---|
| 237 | newBox is not .parentBox |
|---|
| 238 | newBox.name == .parentBox.name |
|---|
| 239 | body |
|---|
| 240 | _parentBox = newBox |
|---|
| 241 | |
|---|
| 242 | def removeIsName(name as String) |
|---|
| 243 | require name in .isNames |
|---|
| 244 | _isNames.remove(name) |
|---|
| 245 | |
|---|
| 246 | |
|---|
| 247 | ## INamedNode |
|---|
| 248 | |
|---|
| 249 | get typeForIdentifier as IType is override |
|---|
| 250 | return .resultType |
|---|
| 251 | |
|---|
| 252 | get typeForReceiver as IType is override |
|---|
| 253 | return .resultType |
|---|
| 254 | |
|---|
| 255 | |
|---|
| 256 | ## Binding |
|---|
| 257 | |
|---|
| 258 | def _bindInt is override |
|---|
| 259 | base._bindInt |
|---|
| 260 | isNames = Set<of String>() |
|---|
| 261 | for name in _isNames |
|---|
| 262 | if name in isNames and not .parentBox.isFromBinaryLibrary |
|---|
| 263 | .compiler.warning(this, 'Duplicate modifier "[name]". You can remove it.') |
|---|
| 264 | isNames.add(name) |
|---|
| 265 | _isNames = List<of String>(isNames) |
|---|
| 266 | if .defaultAccessLevel.length |
|---|
| 267 | # CC: if not find level in _accessLevel where level in _isNames |
|---|
| 268 | found = false |
|---|
| 269 | for level in _accessLevels |
|---|
| 270 | if level in _isNames |
|---|
| 271 | found = true |
|---|
| 272 | break |
|---|
| 273 | if not found |
|---|
| 274 | _isNames.insert(0, .defaultAccessLevel) |
|---|
| 275 | if .shouldBeVirtual |
|---|
| 276 | found = false |
|---|
| 277 | for name in ['nonvirtual', 'virtual', 'shared', 'override', 'abstract'] |
|---|
| 278 | if name in _isNames |
|---|
| 279 | found = true |
|---|
| 280 | break |
|---|
| 281 | if not found |
|---|
| 282 | _isNames.add('virtual') |
|---|
| 283 | for attrib in _attribs |
|---|
| 284 | attrib.bindInt |
|---|
| 285 | if _testMethods and _testMethods.count |
|---|
| 286 | for tm in _testMethods, tm.bindInt |
|---|
| 287 | |
|---|
| 288 | def bindImp as dynamic |
|---|
| 289 | assert not .parentBox.isConstructed |
|---|
| 290 | return base.bindImp |
|---|
| 291 | |
|---|
| 292 | def _bindImp is override |
|---|
| 293 | base._bindImp |
|---|
| 294 | assert .didBindInt # class members should have always received a bindInt first |
|---|
| 295 | assert not .parentBox.isConstructed # not expecting to bindImp on a constructed type |
|---|
| 296 | for attrib in _attribs, attrib.bindImp |
|---|
| 297 | if _testMethods and _testMethods.count |
|---|
| 298 | for tm in _testMethods, tm.bindImp |
|---|
| 299 | |
|---|
| 300 | def usesBase |
|---|
| 301 | # Needed by AbstractMethod and ProperDexer |
|---|
| 302 | if 'new' in _isNames or 'override' in _isNames |
|---|
| 303 | return |
|---|
| 304 | # using base implies override, but only if the method sig in the base class is the same |
|---|
| 305 | # (otherwise it's just an overload) |
|---|
| 306 | if (parentBox = .parentBox) inherits Class |
|---|
| 307 | assert parentBox.baseClass |
|---|
| 308 | # Why not use .matchingBaseMember? Because currently that's only method-to-method. Does not work for properties. |
|---|
| 309 | baseMember = parentBox.baseClass.memberForName(_name) |
|---|
| 310 | if baseMember |
|---|
| 311 | if baseMember inherits MemberOverload |
|---|
| 312 | for member in baseMember.members |
|---|
| 313 | if .matchesSignature(member) |
|---|
| 314 | baseMember = member |
|---|
| 315 | break |
|---|
| 316 | if baseMember inherits IBoxCodeMember |
|---|
| 317 | if .matchesSignature(baseMember) |
|---|
| 318 | if 'nonvirtual' in (baseMember).isNames |
|---|
| 319 | _isNames.add('new') |
|---|
| 320 | else |
|---|
| 321 | _isNames.add('override') |
|---|
| 322 | _isNames.remove('virtual') # virtual is the default for properties and methods |
|---|
| 323 | else |
|---|
| 324 | throw FallThroughException(baseMember) |
|---|
| 325 | |
|---|
| 326 | def matchesSignature(member as IBoxCodeMember) as bool |
|---|
| 327 | if .name <> member.name, return false |
|---|
| 328 | params, otherParams = .params, member.params |
|---|
| 329 | if params.count <> otherParams.count, return false |
|---|
| 330 | for i in params.count |
|---|
| 331 | if not _matchesForOverride(params[i].type, otherParams[i].type) |
|---|
| 332 | return false |
|---|
| 333 | return true |
|---|
| 334 | |
|---|
| 335 | def _matchesForOverride(a as IType, b as IType) as bool |
|---|
| 336 | if a == b |
|---|
| 337 | return true |
|---|
| 338 | if (a inherits NilableType) <> (b inherits NilableType) |
|---|
| 339 | return a.nonNil == b.nonNil |
|---|
| 340 | return false |
|---|
| 341 | |
|---|
| 342 | |
|---|
| 343 | ## Fields |
|---|
| 344 | |
|---|
| 345 | def addMinFields is override |
|---|
| 346 | base.addMinFields |
|---|
| 347 | .addField('name', _name) |
|---|
| 348 | .addField('isNames', _isNames) |
|---|
| 349 | |
|---|
| 350 | def addRefFields is override |
|---|
| 351 | base.addRefFields |
|---|
| 352 | .addField('parentBox', .parentBox) |
|---|
| 353 | |
|---|
| 354 | def addSubFields is override |
|---|
| 355 | base.addSubFields |
|---|
| 356 | if _isNames.count |
|---|
| 357 | .addField('isNames', _isNames) |
|---|
| 358 | .addField('docString', _docString) |
|---|
| 359 | |
|---|
| 360 | |
|---|
| 361 | ## Generics |
|---|
| 362 | |
|---|
| 363 | def constructedFor(box as Box, gpToType as Dictionary<of GenericParam, IType>) as BoxMember # CC: as same |
|---|
| 364 | require .didBindInt |
|---|
| 365 | newMember = .memberwiseClone to BoxMember |
|---|
| 366 | newMember._parentBox = box |
|---|
| 367 | newMember._overloadGroup = nil |
|---|
| 368 | return newMember |
|---|
| 369 | |
|---|
| 370 | |
|---|
| 371 | class BoxEvent |
|---|
| 372 | is partial |
|---|
| 373 | inherits BoxMember |
|---|
| 374 | |
|---|
| 375 | var _handlerType as IType? |
|---|
| 376 | var _handlerTypeProxy as ITypeProxy? |
|---|
| 377 | |
|---|
| 378 | cue init(token as IToken, idToken as IToken, box as Box, name as String, isNames as String*, attribs as AttributeList, docString as String?, handlerType as ITypeProxy) |
|---|
| 379 | base.init(token, idToken, box, name, isNames, attribs, docString) |
|---|
| 380 | _handlerTypeProxy = handlerType |
|---|
| 381 | |
|---|
| 382 | get englishName as String is override |
|---|
| 383 | return 'event' |
|---|
| 384 | |
|---|
| 385 | get handlerType from var |
|---|
| 386 | """ |
|---|
| 387 | Returns the underlying handler type for the event. |
|---|
| 388 | """ |
|---|
| 389 | |
|---|
| 390 | get handlerTypeProxy from var |
|---|
| 391 | |
|---|
| 392 | get isCallable as bool is override |
|---|
| 393 | return true |
|---|
| 394 | |
|---|
| 395 | get params as IList<of Param> is override |
|---|
| 396 | assert .didBindInt |
|---|
| 397 | if _handlerType inherits MethodSig |
|---|
| 398 | return _handlerType.params |
|---|
| 399 | else if _handlerType inherits Class |
|---|
| 400 | method = _handlerType.memberForName('invoke') |
|---|
| 401 | if method inherits AbstractMethod |
|---|
| 402 | return method.params |
|---|
| 403 | else |
|---|
| 404 | .compiler.warning(this, 'Cannot locate a single invoke method of "[_handlerType.name]".') |
|---|
| 405 | return List<of Param>() |
|---|
| 406 | else |
|---|
| 407 | if not .hasError |
|---|
| 408 | .compiler.warning(this, 'Cannot determine parameters of event "[.name]".') |
|---|
| 409 | return List<of Param>() |
|---|
| 410 | |
|---|
| 411 | get resultType as IType is override |
|---|
| 412 | return .handlerType to ! |
|---|
| 413 | |
|---|
| 414 | def addSubFields |
|---|
| 415 | base.addSubFields |
|---|
| 416 | .addField('handlerType', _handlerType) |
|---|
| 417 | .addField('handlerTypeProxy', _handlerTypeProxy) |
|---|
| 418 | |
|---|
| 419 | def _bindInt |
|---|
| 420 | base._bindInt |
|---|
| 421 | if not _handlerType |
|---|
| 422 | _handlerType = _handlerTypeProxy.realType |
|---|
| 423 | # TODO: error check that _handlerType is a delegate |
|---|
| 424 | # _handlerType.nonNil.isDescendantOf(.compiler.delegateType) |
|---|
| 425 | |
|---|
| 426 | |
|---|
| 427 | class BoxField inherits BoxMember is abstract, partial |
|---|
| 428 | """ |
|---|
| 429 | The abstract base class for BoxConst and BoxVar. |
|---|
| 430 | """ |
|---|
| 431 | |
|---|
| 432 | var _typeNode as ITypeProxy? |
|---|
| 433 | var _type as IType? |
|---|
| 434 | var _initExpr as Expr? |
|---|
| 435 | var _isAssignedTo as bool |
|---|
| 436 | |
|---|
| 437 | cue init(token as IToken, idToken as IToken, box as Box, name as String, typeNode as ITypeProxy?, isNames as String*, initExpr as Expr?, attribs as AttributeList?, docString as String) |
|---|
| 438 | base.init(token, idToken, box, name, isNames, attribs ? AttributeList(), docString) |
|---|
| 439 | _typeNode = typeNode |
|---|
| 440 | _initExpr = initExpr |
|---|
| 441 | |
|---|
| 442 | def addRefFields is override |
|---|
| 443 | base.addRefFields |
|---|
| 444 | if _type, .addField('type', _type) |
|---|
| 445 | else, .addField('typeNode', _typeNode) |
|---|
| 446 | |
|---|
| 447 | def addSubFields is override |
|---|
| 448 | base.addSubFields |
|---|
| 449 | .addField('initExpr', _initExpr) |
|---|
| 450 | |
|---|
| 451 | pro isAssignedTo from var |
|---|
| 452 | |
|---|
| 453 | get requiresDotPrefix as bool |
|---|
| 454 | return not _name.startsWith('_') |
|---|
| 455 | |
|---|
| 456 | get resultType as IType is override |
|---|
| 457 | """ |
|---|
| 458 | The result type of the var, method or property. |
|---|
| 459 | """ |
|---|
| 460 | t = .type |
|---|
| 461 | if t, return t |
|---|
| 462 | else, throw FallThroughException('_type is nil. this=[this]') |
|---|
| 463 | |
|---|
| 464 | pro type as IType? |
|---|
| 465 | get |
|---|
| 466 | return _type |
|---|
| 467 | set |
|---|
| 468 | assert false, 'Cannot set the type of a box field.' |
|---|
| 469 | |
|---|
| 470 | get initExpr from var |
|---|
| 471 | |
|---|
| 472 | def _bindInt is override |
|---|
| 473 | base._bindInt |
|---|
| 474 | assert _typeNode or _initExpr |
|---|
| 475 | if _type is nil and _typeNode |
|---|
| 476 | _type = _typeNode.realType |
|---|
| 477 | if _initExpr |
|---|
| 478 | _initExpr.bindImp # that's bindImp intentionally |
|---|
| 479 | if _type is nil |
|---|
| 480 | _type = _initExpr.type |
|---|
| 481 | else if not _initExpr.canBeAssignedTo(_type to !) |
|---|
| 482 | .throwError('Incompatible types. Cannot assign value of type [_initExpr.type.name] on the right to [_type.name] on the left.') |
|---|
| 483 | _checkForBaseDuplicate |
|---|
| 484 | |
|---|
| 485 | def _checkForBaseDuplicate |
|---|
| 486 | if not .parentBox.isFromBinaryLibrary and .parentBox.baseClass and .parentBox.baseClass.memberForName(.name) |
|---|
| 487 | ancestor = (.parentBox.baseClass.memberForName(.name) to IBoxMember).parentBox |
|---|
| 488 | .recordError('Cannot declare a [.englishName] named "[.name]" because an inherited member with that name already exists in "[ancestor.name]".') |
|---|
| 489 | |
|---|
| 490 | def constructedFor(box as Box, gpToType as Dictionary<of GenericParam, IType>) as BoxMember # CC: as same |
|---|
| 491 | assert .type |
|---|
| 492 | newMember = base.constructedFor(box, gpToType) to BoxField |
|---|
| 493 | assert newMember._type |
|---|
| 494 | newMember._type = newMember._type.secondaryConstructedTypeFor(box, gpToType) |
|---|
| 495 | return newMember |
|---|
| 496 | |
|---|
| 497 | |
|---|
| 498 | class BoxConst |
|---|
| 499 | is partial |
|---|
| 500 | inherits BoxField |
|---|
| 501 | """ |
|---|
| 502 | A constant such as: |
|---|
| 503 | const PI = 3.1415926535897931 |
|---|
| 504 | |
|---|
| 505 | In practice, when developing libraries, a read-only var is more practical than a constant, |
|---|
| 506 | because if you update/correct its value, the programs that depend on it will see the update. |
|---|
| 507 | Constants, on the other hand, are inlined in code and can only be updated in programs by |
|---|
| 508 | recompiling against a newer library. |
|---|
| 509 | |
|---|
| 510 | Also, in practice, properties may be more practical than constants or read-only variables. |
|---|
| 511 | Their implementation can be revamped later to retrieve values from a config file, database, |
|---|
| 512 | test harness, etc. |
|---|
| 513 | """ |
|---|
| 514 | |
|---|
| 515 | cue init(token as IToken, idToken as IToken, box as Box, name as String, typeNode as ITypeProxy?, isNames as String*, initExpr as Expr?, attribs as AttributeList?, docString as String) |
|---|
| 516 | base.init(token, idToken, box, name, typeNode, isNames, initExpr, attribs, docString) |
|---|
| 517 | |
|---|
| 518 | get isShared as bool is override |
|---|
| 519 | return true |
|---|
| 520 | |
|---|
| 521 | get englishName as String is override |
|---|
| 522 | return 'constant member' |
|---|
| 523 | |
|---|
| 524 | def _bindInt is override |
|---|
| 525 | base._bindInt |
|---|
| 526 | if not _initExpr and not .parentBox.isFromBinaryLibrary |
|---|
| 527 | .throwError('A constant must be explicitly initialized on declaration') |
|---|
| 528 | |
|---|
| 529 | |
|---|
| 530 | class BoxVar |
|---|
| 531 | is partial |
|---|
| 532 | inherits BoxField |
|---|
| 533 | implements IVar |
|---|
| 534 | """ |
|---|
| 535 | A BoxVar is a variable declared for a box, whether instance or shared. For example: |
|---|
| 536 | var _x as int |
|---|
| 537 | var _y as int |
|---|
| 538 | var _name = '' |
|---|
| 539 | var _nextSerialNum as int is shared |
|---|
| 540 | |
|---|
| 541 | Classes and structs can have vars, but interfaces cannot. |
|---|
| 542 | """ |
|---|
| 543 | |
|---|
| 544 | var _ifInheritsStack as Stack<of IType> |
|---|
| 545 | |
|---|
| 546 | cue init(token as IToken, idToken as IToken, box as Box, name as String, typeNode as ITypeProxy?, isNames as String*, initExpr as Expr?, attribs as AttributeList?, docString as String) |
|---|
| 547 | base.init(token, idToken, box, name, typeNode, isNames, initExpr, attribs, docString) |
|---|
| 548 | _ifInheritsStack = Stack<of IType>() |
|---|
| 549 | _useBackEndNameStack = Stack<of String>() # for if-inherits |
|---|
| 550 | _useBackEndNameStack.push(name) |
|---|
| 551 | |
|---|
| 552 | get englishName as String is override |
|---|
| 553 | return 'variable member' |
|---|
| 554 | |
|---|
| 555 | pro ifInheritsStack from var |
|---|
| 556 | |
|---|
| 557 | def attemptAssignmentOf(type as IType) as bool |
|---|
| 558 | # TODO: dup'ed with AbstractLocalVar |
|---|
| 559 | if _ifInheritsStack.count == 0 |
|---|
| 560 | return false |
|---|
| 561 | stack = Stack<of IType>(Stack<of IType>(_ifInheritsStack)) |
|---|
| 562 | assert stack.peek == _ifInheritsStack.peek |
|---|
| 563 | count = 0 |
|---|
| 564 | post while stack.count and not type.isAssignableTo(stack.peek) |
|---|
| 565 | stack.pop |
|---|
| 566 | count += 1 |
|---|
| 567 | if count or type.isAssignableTo(_type to !) |
|---|
| 568 | _ifInheritsStack = stack |
|---|
| 569 | # tell each IfStmt not to pop the if-inherits stack since the assignment did so |
|---|
| 570 | for node in .compiler.nodeStack |
|---|
| 571 | if node inherits IfStmt |
|---|
| 572 | if node.ifInheritsVar is this |
|---|
| 573 | node.doNotPopIfInheritsStack |
|---|
| 574 | count -= 1 |
|---|
| 575 | if count == 0, break |
|---|
| 576 | return true |
|---|
| 577 | else |
|---|
| 578 | return false |
|---|
| 579 | |
|---|
| 580 | def setInitExpr(initExpr as Expr) as bool |
|---|
| 581 | """ Used in property declaration when providing an initial value for an existing backing var. """ |
|---|
| 582 | if _initExpr, return false |
|---|
| 583 | _initExpr = initExpr |
|---|
| 584 | return true |
|---|
| 585 | |
|---|
| 586 | ## INamedNode |
|---|
| 587 | |
|---|
| 588 | get typeForReceiver as IType is override |
|---|
| 589 | return if(_ifInheritsStack.count, _ifInheritsStack.peek, .resultType) |
|---|
| 590 | |
|---|
| 591 | ## IVar |
|---|
| 592 | |
|---|
| 593 | pro type as IType? is override |
|---|
| 594 | get |
|---|
| 595 | return if(_ifInheritsStack.count, _ifInheritsStack.peek, _type) |
|---|
| 596 | set |
|---|
| 597 | assert false, 'Cannot set the type of a ClassVar.' |
|---|
| 598 | |
|---|
| 599 | |
|---|
| 600 | interface HasAddStmt |
|---|
| 601 | def addStmt(stmt as Stmt) |
|---|
| 602 | |
|---|
| 603 | |
|---|
| 604 | class AnonymousMethod |
|---|
| 605 | is partial |
|---|
| 606 | inherits AbstractMethod |
|---|
| 607 | |
|---|
| 608 | cue init(token as IToken, parentMethod as AbstractMethod, paramsList as List<of Param>, returnTypeNode as ITypeProxy?, isNames as String*) |
|---|
| 609 | base.init(token, token, parentMethod.parentBox, parentMethod, '(anonymous method)', paramsList, isNames, AttributeList(), '') |
|---|
| 610 | _name = '(anonymous method [.serialNum])' |
|---|
| 611 | _returnTypeNode = returnTypeNode |
|---|
| 612 | for param in .params, param.isAnonymousParam = true |
|---|
| 613 | |
|---|
| 614 | get englishName as String is override |
|---|
| 615 | return 'anonymous method' |
|---|
| 616 | |
|---|
| 617 | get hasReturnStmt as bool is override |
|---|
| 618 | # the second boolean term is to prevent an extra error in _bindImp for lambdas, |
|---|
| 619 | # although it's not clear to me why this extra guard is needed |
|---|
| 620 | return base.hasReturnStmt or (_stmts.count > 0 and _stmts[0] inherits ReturnStmt) |
|---|
| 621 | |
|---|
| 622 | get hasVariArgs as bool |
|---|
| 623 | return false |
|---|
| 624 | |
|---|
| 625 | get isCallable as bool is override |
|---|
| 626 | return true |
|---|
| 627 | |
|---|
| 628 | get isMethod as bool is override |
|---|
| 629 | return true |
|---|
| 630 | |
|---|
| 631 | get shouldBeVirtual as bool is override |
|---|
| 632 | return false |
|---|
| 633 | |
|---|
| 634 | def lambdaReturnType as IType |
|---|
| 635 | """ This is a service method for LambdaExpr. """ |
|---|
| 636 | require .didBindInt and .didBindImp |
|---|
| 637 | stmt = _stmts[0] |
|---|
| 638 | if stmt inherits ReturnStmt |
|---|
| 639 | return stmt.expr.type to ! |
|---|
| 640 | else |
|---|
| 641 | throw FallThroughException(stmt) |
|---|
| 642 | |
|---|
| 643 | def _bindInt is override |
|---|
| 644 | base._bindInt |
|---|
| 645 | if _returnTypeNode is nil |
|---|
| 646 | _returnType = .compiler.passThroughType # TODO: infer return type from return statement like C# does |
|---|
| 647 | |
|---|
| 648 | def _bindImp is override |
|---|
| 649 | base._bindImp |
|---|
| 650 | numErrors = .compiler.errors.count |
|---|
| 651 | base._bindImp |
|---|
| 652 | if .compiler.errors.count == numErrors and _ |
|---|
| 653 | _returnType not in [.compiler.voidType to IType, .compiler.passThroughType to IType] and _ |
|---|
| 654 | not .hasReturnStmt and not .hasYieldStmt and not .hasThrowStmt and _ |
|---|
| 655 | .bodyExclusion is nil |
|---|
| 656 | # then |
|---|
| 657 | .throwError('Missing return statement for method "[_name]" which returns [_returnType.name].') |
|---|
| 658 | hasYield = false |
|---|
| 659 | for stmt in _stmts |
|---|
| 660 | if stmt inherits YieldStmt |
|---|
| 661 | hasYield = true |
|---|
| 662 | break |
|---|
| 663 | if hasYield, .throwError('Cannot yield inside an anonymous method.') |
|---|
| 664 | |
|---|
| 665 | |
|---|
| 666 | class AbstractMethod |
|---|
| 667 | is abstract, partial |
|---|
| 668 | inherits BoxMember |
|---|
| 669 | implements HasAddStmt, IOverloadable |
|---|
| 670 | """ |
|---|
| 671 | The abstract ancestor class for |
|---|
| 672 | Initializer |
|---|
| 673 | Method |
|---|
| 674 | PropertyXetter |
|---|
| 675 | |
|---|
| 676 | But not for Property (which has two code blocks "get" and "set"), |
|---|
| 677 | or other class decls like variables. |
|---|
| 678 | |
|---|
| 679 | Subclasses must set _params. |
|---|
| 680 | """ |
|---|
| 681 | |
|---|
| 682 | var _parentMethod as AbstractMethod? |
|---|
| 683 | var _returnTypeNode as ITypeProxy? |
|---|
| 684 | var _returnType as IType? |
|---|
| 685 | var _requirePart as RequirePart? |
|---|
| 686 | var _ensurePart as EnsurePart? |
|---|
| 687 | var _oldExprs as List<of OldExpr> |
|---|
| 688 | var _params as IList<of Param> |
|---|
| 689 | var _locals as List<of LocalVar> |
|---|
| 690 | var _stmts as List<of Stmt> |
|---|
| 691 | var _hasReturnStmt as bool |
|---|
| 692 | var _hasThrowStmt as bool |
|---|
| 693 | var _hasYieldStmt as bool |
|---|
| 694 | var _curStmtIndex as int # to implement .replaceChild |
|---|
| 695 | var _isCompilerGenerated as bool |
|---|
| 696 | |
|---|
| 697 | cue init(token as IToken, idToken as IToken, box as Box, parentMethod as AbstractMethod?, name as String, paramsList as IList<of Param>, isNames as String*, attribs as AttributeList, docString as String) |
|---|
| 698 | base.init(token, idToken, box, name, isNames, attribs, docString) |
|---|
| 699 | _parentMethod = parentMethod |
|---|
| 700 | _params = paramsList.clone |
|---|
| 701 | _oldExprs = List<of OldExpr>() |
|---|
| 702 | _locals = List<of LocalVar>() |
|---|
| 703 | _stmts = List<of Stmt>() |
|---|
| 704 | |
|---|
| 705 | get params as IList<of Param> is override |
|---|
| 706 | return _params |
|---|
| 707 | |
|---|
| 708 | get locals from var |
|---|
| 709 | |
|---|
| 710 | get isCallable as bool is override |
|---|
| 711 | return true |
|---|
| 712 | |
|---|
| 713 | pro hasReturnStmt from var |
|---|
| 714 | |
|---|
| 715 | pro hasThrowStmt from var |
|---|
| 716 | |
|---|
| 717 | pro hasYieldStmt from var |
|---|
| 718 | |
|---|
| 719 | pro isCompilerGenerated from var |
|---|
| 720 | |
|---|
| 721 | def bodyExclusion as String? |
|---|
| 722 | ensure result in [nil, 'abstract', 'interface', 'extern box', 'dll import'] |
|---|
| 723 | if 'abstract' in .isNames, return 'abstract' |
|---|
| 724 | if .parentBox inherits Interface and not .parentBox inherits Mixin, return 'interface' |
|---|
| 725 | if .parentBox.isExtern, return 'extern box' |
|---|
| 726 | for attrib in .attributes, if attrib.name in ['DllImport', 'DllImportAttribute'], return 'dll import' |
|---|
| 727 | return nil |
|---|
| 728 | |
|---|
| 729 | def addStmt(stmt as Stmt) |
|---|
| 730 | _stmts.add(stmt) |
|---|
| 731 | |
|---|
| 732 | pro requirePart from var |
|---|
| 733 | |
|---|
| 734 | pro ensurePart from var |
|---|
| 735 | |
|---|
| 736 | get parentMethod from var |
|---|
| 737 | """ |
|---|
| 738 | Anonymous methods have a .parentMethod. |
|---|
| 739 | In the future, there may be nested method declarations which will also then have a .parentMethod. |
|---|
| 740 | """ |
|---|
| 741 | |
|---|
| 742 | get resultType as IType is override |
|---|
| 743 | or require |
|---|
| 744 | .didBindInt |
|---|
| 745 | _returnType |
|---|
| 746 | body |
|---|
| 747 | return _returnType to ! |
|---|
| 748 | |
|---|
| 749 | get statements from _stmts |
|---|
| 750 | |
|---|
| 751 | def findLocal(name as String) as AbstractLocalVar? |
|---|
| 752 | # TODO: should this use a dictionary lookup? |
|---|
| 753 | for local in _locals, if local.name==name, return local |
|---|
| 754 | for param in _params, if param.name==name, return param |
|---|
| 755 | if .parentMethod, return .parentMethod.findLocal(name) |
|---|
| 756 | else, return nil |
|---|
| 757 | |
|---|
| 758 | def findLocalCI(name as String) as AbstractLocalVar? |
|---|
| 759 | name = name.toLower |
|---|
| 760 | # TODO: should this use a dictionary lookup? |
|---|
| 761 | for local in _locals, if local.name.toLower==name, return local |
|---|
| 762 | for param in _params, if param.name.toLower==name, return param |
|---|
| 763 | if .parentMethod, return .parentMethod.findLocalCI(name) |
|---|
| 764 | else, return nil |
|---|
| 765 | |
|---|
| 766 | def addLocal(local as LocalVar) |
|---|
| 767 | require |
|---|
| 768 | .findLocal(local.name) is nil |
|---|
| 769 | body |
|---|
| 770 | other = .findLocalCI(local.name) |
|---|
| 771 | if other, .throwError('Parameters and locals must vary by more than just their case. Change "[local.name]" or "[other.name]" to match, or rename one of them to something different.') |
|---|
| 772 | local.isTracked = true |
|---|
| 773 | _locals.add(local) |
|---|
| 774 | |
|---|
| 775 | def replaceLocal(local as LocalVar) |
|---|
| 776 | """ |
|---|
| 777 | A utility method for EnsurePart to slip in the `result`. |
|---|
| 778 | """ |
|---|
| 779 | require |
|---|
| 780 | .findLocal(local.name) |
|---|
| 781 | body |
|---|
| 782 | _locals.remove(.findLocal(local.name) to LocalVar) |
|---|
| 783 | _locals.add(local) |
|---|
| 784 | |
|---|
| 785 | def removeLocal(name as String) |
|---|
| 786 | require |
|---|
| 787 | name.length |
|---|
| 788 | .findLocal(name) |
|---|
| 789 | body |
|---|
| 790 | for i = 0 .. _locals.count |
|---|
| 791 | if _locals[i].name==name |
|---|
| 792 | _locals.removeAt(i) |
|---|
| 793 | break |
|---|
| 794 | |
|---|
| 795 | def addOldExpr(oldExpr as OldExpr) |
|---|
| 796 | _oldExprs.add(oldExpr) |
|---|
| 797 | oldExpr.sharpVarName = '_lh_old_' + _oldExprs.count.toString |
|---|
| 798 | |
|---|
| 799 | get oldExprs from var |
|---|
| 800 | """ |
|---|
| 801 | Use .addOldExpr(e) instead of method.oldExprs.add(). |
|---|
| 802 | """ |
|---|
| 803 | |
|---|
| 804 | def changeToPublic |
|---|
| 805 | ensure |
|---|
| 806 | .isPublic |
|---|
| 807 | not .isInternal and not .isProtected and not .isPrivate |
|---|
| 808 | .isShared == old .isShared |
|---|
| 809 | body |
|---|
| 810 | for name in ['internal', 'private', 'protected'], _isNames.remove(name) |
|---|
| 811 | if 'public' not in _isNames, _isNames.add('public') |
|---|
| 812 | |
|---|
| 813 | def changeToNonVirtual |
|---|
| 814 | ensure |
|---|
| 815 | 'virtual' not in .isNames |
|---|
| 816 | 'nonvirtual' in .isNames |
|---|
| 817 | .isShared == old .isShared |
|---|
| 818 | body |
|---|
| 819 | _isNames.remove('virtual') |
|---|
| 820 | if 'nonvirtual' not in _isNames, _isNames.add('nonvirtual') |
|---|
| 821 | |
|---|
| 822 | def unNilReturnType is override |
|---|
| 823 | if _returnType |
|---|
| 824 | if _returnType inherits NilableType |
|---|
| 825 | _returnType = _returnType.nonNil |
|---|
| 826 | else if _returnTypeNode inherits NilableTypeProxy |
|---|
| 827 | _returnTypeNode = _returnTypeNode.innerTypeProxy |
|---|
| 828 | |
|---|
| 829 | def addRefFields is override |
|---|
| 830 | base.addRefFields |
|---|
| 831 | .addField('returnTypeNode', _returnTypeNode) |
|---|
| 832 | .addField('returnType', _returnType) |
|---|
| 833 | |
|---|
| 834 | def addSubFields is override |
|---|
| 835 | base.addSubFields |
|---|
| 836 | .addField('params', _params) |
|---|
| 837 | .addField('requirePart', _requirePart) |
|---|
| 838 | .addField('ensurePart', _ensurePart) |
|---|
| 839 | .addField('locals', _locals) |
|---|
| 840 | .addField('stmts', _stmts) |
|---|
| 841 | |
|---|
| 842 | def cobraSourceSignature as String is override |
|---|
| 843 | return .cobraSourceSignature(true) |
|---|
| 844 | |
|---|
| 845 | def cobraSourceSignature(includeShared as bool) as String |
|---|
| 846 | sb = StringBuilder('def [.name]') |
|---|
| 847 | if .params.count > 0 |
|---|
| 848 | sb.append('(') |
|---|
| 849 | sep = '' |
|---|
| 850 | for param in .params |
|---|
| 851 | sb.append('[sep][param.name] as ') |
|---|
| 852 | branch param.direction |
|---|
| 853 | on Direction.In, pass |
|---|
| 854 | on Direction.InOut, sb.append('inout ') |
|---|
| 855 | on Direction.Out, sb.append('out ') |
|---|
| 856 | sb.append('[param.type.name]') |
|---|
| 857 | sep = ', ' |
|---|
| 858 | sb.append(')') |
|---|
| 859 | if not .resultType inherits VoidType |
|---|
| 860 | sb.append(' as [.resultType.name]') |
|---|
| 861 | if includeShared and .isShared |
|---|
| 862 | # TODO: other is names |
|---|
| 863 | sb.append(' is shared') |
|---|
| 864 | return sb.toString |
|---|
| 865 | |
|---|
| 866 | get canHaveDetailedStackTrace as bool |
|---|
| 867 | return .parentBox.canHaveDetailedStackTrace and not .hasYieldStmt |
|---|
| 868 | |
|---|
| 869 | get willRequire as bool |
|---|
| 870 | # in the future, this will also depend on a command line option to include/exclude preconditions |
|---|
| 871 | # classes that are sensitive with respect to the DST skip on contracts |
|---|
| 872 | return .canHaveDetailedStackTrace |
|---|
| 873 | |
|---|
| 874 | get willEnsure as bool |
|---|
| 875 | # in the future, this will also depend on a command line option to include/exclude preconditions |
|---|
| 876 | return .canHaveDetailedStackTrace |
|---|
| 877 | |
|---|
| 878 | def replaceChild(find as INode, replace as INode) as bool |
|---|
| 879 | if _curStmtIndex < _stmts.count and _stmts[_curStmtIndex] is find |
|---|
| 880 | _stmts[_curStmtIndex] = replace to Stmt |
|---|
| 881 | return true |
|---|
| 882 | else |
|---|
| 883 | return base.replaceChild(find, replace) |
|---|
| 884 | |
|---|
| 885 | def _stackPush |
|---|
| 886 | base._stackPush |
|---|
| 887 | .compiler.codeMemberStack.push(this) |
|---|
| 888 | |
|---|
| 889 | def _stackPop |
|---|
| 890 | base._stackPop |
|---|
| 891 | .compiler.codeMemberStack.pop |
|---|
| 892 | |
|---|
| 893 | def _bindInt is override |
|---|
| 894 | assert .compiler.codeMemberStack.peek is this |
|---|
| 895 | base._bindInt |
|---|
| 896 | assert .parentBox.didUnNilReturnTypes |
|---|
| 897 | for param in _params, param.bindInt # TODO: add error recovery |
|---|
| 898 | if _returnType is nil |
|---|
| 899 | if _returnTypeNode, _returnType = _returnTypeNode.realType |
|---|
| 900 | else, _returnType = .compiler.voidType |
|---|
| 901 | assert _returnType, _returnTypeNode |
|---|
| 902 | if _requirePart is nil and .willRequire, _requirePart = RequirePart(this) |
|---|
| 903 | if _requirePart, _requirePart.bindInt |
|---|
| 904 | if _ensurePart is nil and .willEnsure, _ensurePart = EnsurePart(this) |
|---|
| 905 | if _ensurePart, _ensurePart.bindInt |
|---|
| 906 | |
|---|
| 907 | def _bindImp is override |
|---|
| 908 | assert .compiler.codeMemberStack.peek is this |
|---|
| 909 | base._bindImp |
|---|
| 910 | params = .params |
|---|
| 911 | if .matchingBaseMember |
|---|
| 912 | matchingBaseMember = .matchingBaseMember |
|---|
| 913 | if 'override' in .isNames and 'private' in matchingBaseMember.isNames |
|---|
| 914 | .recordError('Cannot override the base member which is declared "private".') |
|---|
| 915 | for i in params.count |
|---|
| 916 | param = params[i] |
|---|
| 917 | if matchingBaseMember.params[i].type inherits NilableType and not param.type inherits NilableType |
|---|
| 918 | .recordError('Cannot make param "[param.name]" non-nil because the base method declares it is nilable. Change "[param.name]" to "[matchingBaseMember.params[i].type.name]", or change the base method.') |
|---|
| 919 | for param in params |
|---|
| 920 | param.bindImp |
|---|
| 921 | for local in _locals |
|---|
| 922 | local.bindImp |
|---|
| 923 | if _requirePart |
|---|
| 924 | _requirePart.bindImp |
|---|
| 925 | if _ensurePart and _returnType and _returnType is not .compiler.voidType |
|---|
| 926 | _backEndResultVarName = '_lh_result' |
|---|
| 927 | else |
|---|
| 928 | _backEndResultVarName = '' |
|---|
| 929 | _curStmtIndex = 0 |
|---|
| 930 | for stmt in _stmts.toArray |
|---|
| 931 | try |
|---|
| 932 | stmt.bindImp |
|---|
| 933 | stmt.afterStatementBindImp # to let expressions know when they are used as statements |
|---|
| 934 | catch ne as NodeException |
|---|
| 935 | .compiler.recordError(ne) |
|---|
| 936 | _curStmtIndex += 1 |
|---|
| 937 | for param in _params, assert param.ifInheritsStack.count == 0 |
|---|
| 938 | if _ensurePart, _ensurePart.bindImp |
|---|
| 939 | if _requirePart, _requirePart.checkConnectToken |
|---|
| 940 | if _ensurePart, _ensurePart.checkConnectToken |
|---|
| 941 | .checkForUnmarkedOverrideOrNew # have to do this after statements bindImp since using base can add `override` |
|---|
| 942 | .checkForUnusedParams |
|---|
| 943 | .checkForUnusedVariables |
|---|
| 944 | |
|---|
| 945 | var _didComputeMatchingBaseMember = true |
|---|
| 946 | |
|---|
| 947 | get didComputeMatchingBaseMember from var |
|---|
| 948 | |
|---|
| 949 | def _computeMatchingBaseMembers |
|---|
| 950 | and ensure .matchingBaseMember is not this |
|---|
| 951 | base._computeMatchingBaseMembers |
|---|
| 952 | if not .canHaveMatchingBaseMember, return |
|---|
| 953 | _didComputeMatchingBaseMember = true |
|---|
| 954 | if _matchingBaseMember, return |
|---|
| 955 | if not .canHaveMatchingBaseMember, return |
|---|
| 956 | if (parentBox = .parentBox) inherits Class |
|---|
| 957 | if parentBox.baseClass |
|---|
| 958 | baseMember = parentBox.baseClass.memberForName(_name) |
|---|
| 959 | if baseMember is nil |
|---|
| 960 | pass |
|---|
| 961 | else if baseMember inherits AbstractMethod |
|---|
| 962 | if .matchesSignature(baseMember) |
|---|
| 963 | _matchingBaseMember = baseMember |
|---|
| 964 | else if baseMember inherits MemberOverload |
|---|
| 965 | if baseMember.members[0] inherits AbstractMethod |
|---|
| 966 | for member in baseMember.members |
|---|
| 967 | if .matchesSignature(member) |
|---|
| 968 | _matchingBaseMember = member |
|---|
| 969 | else |
|---|
| 970 | cannotRedeclare = true |
|---|
| 971 | else |
|---|
| 972 | cannotRedeclare = true |
|---|
| 973 | if cannotRedeclare |
|---|
| 974 | .recordError('Cannot declare a [.englishName] named "[.name]" because the base member "[.name]" is a [baseMember.englishName].') |
|---|
| 975 | if _matchingBaseMember and _matchingBaseMember.parentBox inherits Interface |
|---|
| 976 | # not really a base member in the "base class" sense of the word. |
|---|
| 977 | # would not require marking as "is override" for example |
|---|
| 978 | _matchingBaseMember = nil |
|---|
| 979 | |
|---|
| 980 | def checkForUnmarkedOverrideOrNew |
|---|
| 981 | require .didComputeMatchingBaseMember |
|---|
| 982 | if 'new' not in .isNames and 'override' not in .isNames and _matchingBaseMember and not _matchingBaseMember.isExtensionMember |
|---|
| 983 | .throwError('Member "[_name]" also exists in the base class. You must specify "is override" or "is new", or change the name.') |
|---|
| 984 | |
|---|
| 985 | def checkForUnusedParams |
|---|
| 986 | pass |
|---|
| 987 | /# |
|---|
| 988 | not yet. see http://cobra-language.com/trac/cobra/ticket/279 |
|---|
| 989 | if not .isAbstract and .statements.count |
|---|
| 990 | for param in _params |
|---|
| 991 | if not param.isUsed and not param.isOut and not param.isDeclaredAsUnused |
|---|
| 992 | .compiler.warning(param, 'The value of parameter "[param.name]" is never used in [.parentBox.name].[.name].') |
|---|
| 993 | #/ |
|---|
| 994 | |
|---|
| 995 | def checkForUnusedVariables |
|---|
| 996 | for local in _locals |
|---|
| 997 | if not local.isUsed |
|---|
| 998 | .compiler.warning(local, 'The value of variable "[local.name]" is never used.') |
|---|
| 999 | |
|---|
| 1000 | |
|---|
| 1001 | ## Generics |
|---|
| 1002 | |
|---|
| 1003 | def constructedFor(box as Box, gpToType as Dictionary<of GenericParam, IType>) as BoxMember # CC: as same |
|---|
| 1004 | newMember = base.constructedFor(box, gpToType) to AbstractMethod # CC: this = base. ... |
|---|
| 1005 | assert newMember._returnType |
|---|
| 1006 | newMember._returnType = newMember._returnType.secondaryConstructedTypeFor(box, gpToType) |
|---|
| 1007 | newMember._params = for p in _params get p.constructedFor(box, gpToType) |
|---|
| 1008 | return newMember |
|---|
| 1009 | |
|---|
| 1010 | |
|---|
| 1011 | class Initializer inherits AbstractMethod is partial |
|---|
| 1012 | |
|---|
| 1013 | cue init(token as IToken, idToken as IToken, box as Box, paramsList as IList<of Param>, isNames as String*, attribs as AttributeList, docString as String) |
|---|
| 1014 | base.init(token, idToken, box, nil, 'cue.init', paramsList, isNames, attribs, docString) |
|---|
| 1015 | _isNames = isNames.toList |
|---|
| 1016 | _stmts = List<of Stmt>() |
|---|
| 1017 | |
|---|
| 1018 | get englishName as String is override |
|---|
| 1019 | return 'initializer' |
|---|
| 1020 | |
|---|
| 1021 | get hasVariArgs as bool |
|---|
| 1022 | assert false, 'TODO: complete Initializer.hasVariArgs' |
|---|
| 1023 | return false |
|---|
| 1024 | |
|---|
| 1025 | get isCallable as bool is override |
|---|
| 1026 | return true |
|---|
| 1027 | |
|---|
| 1028 | get isMethod as bool is override |
|---|
| 1029 | return true # because this is needed for "base.init" without ()s |
|---|
| 1030 | |
|---|
| 1031 | def usesBase is override |
|---|
| 1032 | # initializers cannot be virtual and don't require "is new" |
|---|
| 1033 | pass |
|---|
| 1034 | |
|---|
| 1035 | def checkForUnmarkedOverrideOrNew is override |
|---|
| 1036 | # initializers don't say override or new |
|---|
| 1037 | pass |
|---|
| 1038 | |
|---|
| 1039 | get lastToken as IToken |
|---|
| 1040 | if _stmts.count |
|---|
| 1041 | return _stmts[_stmts.count-1].lastToken # CC: use .last |
|---|
| 1042 | else |
|---|
| 1043 | return .token |
|---|
| 1044 | |
|---|
| 1045 | def mangleName(name as String) as String is override |
|---|
| 1046 | if name == 'init', return 'cue.init' |
|---|
| 1047 | else, return name |
|---|
| 1048 | |
|---|
| 1049 | def _bindImp |
|---|
| 1050 | if .canHaveDetailedStackTrace # no extra checks for classes that are sensitive with respect to the DST |
|---|
| 1051 | # add asserts at the end for non-nilable class vars |
|---|
| 1052 | if .compiler.options.boolValue('include-nil-checks') |
|---|
| 1053 | token = .lastToken.copy |
|---|
| 1054 | token.incLineNum |
|---|
| 1055 | stmts = List<of Stmt>() |
|---|
| 1056 | for decl in .parentBox.declsInOrder |
|---|
| 1057 | if decl inherits BoxVar |
|---|
| 1058 | assert decl.type |
|---|
| 1059 | if not decl.type inherits NilableType and decl.type.isReference and not decl.isShared |
|---|
| 1060 | if decl.name.startsWith('_') |
|---|
| 1061 | expr = IdentifierExpr(token, decl) to Expr |
|---|
| 1062 | else |
|---|
| 1063 | expr = DotExpr(token, 'DOT', ThisLit(token, isImplicit=true), MemberExpr(token, decl.name), isImplicit=true) to Expr |
|---|
| 1064 | stmts.add(AssertStmt(token, IsNotNilExpr(token, expr), nil)) |
|---|
| 1065 | if stmts.count |
|---|
| 1066 | cond = BinaryOpExpr.make( _ |
|---|
| 1067 | .lastToken.copy('DOT', '.'), 'DOT', _ |
|---|
| 1068 | IdentifierExpr(.lastToken.copy('ID', 'CobraCore')), _ |
|---|
| 1069 | MemberExpr(.lastToken.copy('ID', '_willCheckNil'))) |
|---|
| 1070 | block = BlockStmt(token, stmts) |
|---|
| 1071 | ifStmt = IfStmt(token, cond, block, nil) |
|---|
| 1072 | .addStmt(ifStmt) |
|---|
| 1073 | |
|---|
| 1074 | base._bindImp |
|---|
| 1075 | |
|---|
| 1076 | first = true |
|---|
| 1077 | for stmt in _stmts |
|---|
| 1078 | isBase = isThisInit = false |
|---|
| 1079 | if stmt inherits DotExpr |
|---|
| 1080 | if stmt.left inherits BaseLit |
|---|
| 1081 | isBase = true |
|---|
| 1082 | else if stmt.left inherits ThisLit and ((stmt.right inherits MemberExpr and (stmt.right to MemberExpr).name == 'init') or (stmt.right inherits CallExpr and (stmt.right to CallExpr).name == 'init')) # CC: axe casts |
|---|
| 1083 | isThisInit = true |
|---|
| 1084 | if first |
|---|
| 1085 | if .isClassMember and not isBase and not isThisInit and not .isShared |
|---|
| 1086 | # structs cannot call base constructors |
|---|
| 1087 | # static initializers cannot call base constructors |
|---|
| 1088 | .compiler.warning(stmt, 'The first statement of an "init" cue should be a call to another "init" in this class or the base class.') |
|---|
| 1089 | else |
|---|
| 1090 | if isBase |
|---|
| 1091 | # TODO: after switching to byte code gen, it would be nice to relax this a bit, by say allowing statements that use args and locals, but can't set class state by, for example, setting class vars or calling members |
|---|
| 1092 | stmt.recordError('The base initializer can only be invoked in the first statement.') |
|---|
| 1093 | else if isThisInit |
|---|
| 1094 | stmt.recordError('Another initializer can only be invoked in the first statement.') |
|---|
| 1095 | first = false |
|---|
| 1096 | |
|---|
| 1097 | |
|---|
| 1098 | class Method inherits AbstractMethod is partial |
|---|
| 1099 | |
|---|
| 1100 | var _implementsTypeNode as ITypeProxy? |
|---|
| 1101 | var _implementsType as IType? |
|---|
| 1102 | |
|---|
| 1103 | cue init(token as IToken, idToken as IToken, box as Box, name as String, paramsList as IList<of Param>, returnTypeNode as ITypeProxy, implementsTypeNode as ITypeProxy?, isNames as String*, attribs as AttributeList, docString as String) |
|---|
| 1104 | .init(token, idToken, box, name, List<of IType>(), paramsList, returnTypeNode, implementsTypeNode, isNames, attribs, docString) |
|---|
| 1105 | |
|---|
| 1106 | cue init(token as IToken, idToken as IToken, box as Box, name as String, genericParams as List<of IType>, paramsList as IList<of Param>, returnTypeNode as ITypeProxy, implementsTypeNode as ITypeProxy?, isNames as String*, attribs as AttributeList, docString as String) |
|---|
| 1107 | base.init(token, idToken, box, nil, name, paramsList, isNames, attribs, docString) |
|---|
| 1108 | _genericParams = genericParams |
|---|
| 1109 | for gp in _genericParams, if gp inherits GenericParam, gp.parentDefinition = this |
|---|
| 1110 | _returnTypeNode = returnTypeNode |
|---|
| 1111 | _implementsTypeNode = implementsTypeNode |
|---|
| 1112 | if 'virtual' in _isNames and _implementsTypeNode |
|---|
| 1113 | _isNames.remove('virtual') |
|---|
| 1114 | _stmts = List<of Stmt>() |
|---|
| 1115 | |
|---|
| 1116 | get englishName as String is override |
|---|
| 1117 | return 'method' |
|---|
| 1118 | |
|---|
| 1119 | get hasVariArgs as bool |
|---|
| 1120 | assert false, 'TODO: complete Method.hasVariArgs' |
|---|
| 1121 | return false |
|---|
| 1122 | |
|---|
| 1123 | get isCallable as bool is override |
|---|
| 1124 | return true |
|---|
| 1125 | |
|---|
| 1126 | pro isMain from var as bool |
|---|
| 1127 | |
|---|
| 1128 | pro hasMainParams from var as bool |
|---|
| 1129 | """ Flag that this method needs main entry point params inserted. Used by BackEnd codeGen""" |
|---|
| 1130 | |
|---|
| 1131 | get isMethod as bool is override |
|---|
| 1132 | return true |
|---|
| 1133 | |
|---|
| 1134 | get implementsType from var |
|---|
| 1135 | |
|---|
| 1136 | get shouldBeVirtual as bool is override |
|---|
| 1137 | return .isClassMember and not .isPrivate and not _implementsTypeNode and not _implementsType |
|---|
| 1138 | |
|---|
| 1139 | def addRefFields is override |
|---|
| 1140 | base.addRefFields |
|---|
| 1141 | .addField('implementsType', _implementsType) |
|---|
| 1142 | |
|---|
| 1143 | def addSubFields is override |
|---|
| 1144 | base.addSubFields |
|---|
| 1145 | .addField('genericParams', _genericParams) |
|---|
| 1146 | if _implementsTypeNode |
|---|
| 1147 | .addField('implementsTypeNode', _implementsTypeNode) |
|---|
| 1148 | |
|---|
| 1149 | get defaultAccessLevel as String is override |
|---|
| 1150 | dal = base.defaultAccessLevel |
|---|
| 1151 | if dal.length and not _implementsType and not _implementsTypeNode |
|---|
| 1152 | return dal |
|---|
| 1153 | else |
|---|
| 1154 | return '' |
|---|
| 1155 | |
|---|
| 1156 | |
|---|
| 1157 | ## Generics |
|---|
| 1158 | |
|---|
| 1159 | var _genericParams as List<of IType> |
|---|
| 1160 | var _genericDef as Method? # non-nil for a method constructed from a generic method |
|---|
| 1161 | var _constructions as Dictionary<of String, Method>? |
|---|
| 1162 | |
|---|
| 1163 | get genericDef from var |
|---|
| 1164 | |
|---|
| 1165 | get genericParams from var |
|---|
| 1166 | |
|---|
| 1167 | get isConstructed as bool |
|---|
| 1168 | return _genericDef is not nil |
|---|
| 1169 | |
|---|
| 1170 | get isGenericDef as bool |
|---|
| 1171 | """ |
|---|
| 1172 | Returns true if this type is a definition of a generic type, and |
|---|
| 1173 | therefore capable of making subsequent generic types via the |
|---|
| 1174 | `constructedTypeFor` method. A generic type will have generic |
|---|
| 1175 | parameters which are all GenericParam (as opposed to other types |
|---|
| 1176 | such as `int` or a given class). |
|---|
| 1177 | """ |
|---|
| 1178 | # TODO: make this more efficient, maybe by computing in the initializer |
|---|
| 1179 | if .genericDef or .genericParams.count == 0 |
|---|
| 1180 | return false |
|---|
| 1181 | else if .genericParams.count |
|---|
| 1182 | for param in .genericParams |
|---|
| 1183 | if not param inherits GenericParam |
|---|
| 1184 | return false |
|---|
| 1185 | return true |
|---|
| 1186 | else |
|---|
| 1187 | return false |
|---|
| 1188 | |
|---|
| 1189 | get containsGenericParameters as bool |
|---|
| 1190 | for param in .genericParams |
|---|
| 1191 | if param inherits GenericParam |
|---|
| 1192 | return true |
|---|
| 1193 | if param inherits Box # CC: and <next if condition> |
|---|
| 1194 | if param.containsGenericParameters |
|---|
| 1195 | return true |
|---|
| 1196 | return false |
|---|
| 1197 | |
|---|
| 1198 | def constructedMethodWith(typeArgs as List<of IType>) as Method |
|---|
| 1199 | """ |
|---|
| 1200 | This is for methods that are generic themselves due to having their own generic parameters. |
|---|
| 1201 | For methods that simply belong to a generic class, see .constructedFor. |
|---|
| 1202 | |
|---|
| 1203 | TODO: Consider the case of a method that implements a specific interface. Can that have generic parameters? |
|---|
| 1204 | """ |
|---|
| 1205 | require |
|---|
| 1206 | .didBindInt |
|---|
| 1207 | .isGenericDef |
|---|
| 1208 | .containsGenericParameters |
|---|
| 1209 | typeArgs.count == .genericParams.count |
|---|
| 1210 | ensure |
|---|
| 1211 | typeArgs <> .genericParams implies this is not result |
|---|
| 1212 | result.genericParams == typeArgs |
|---|
| 1213 | body |
|---|
| 1214 | if typeArgs == _genericParams |
|---|
| 1215 | return this |
|---|
| 1216 | |
|---|
| 1217 | # the cache of constructed types has to come from the root generic type def or you end up with duplicate |
|---|
| 1218 | # constructed types which is not only wasteful, but causes problems with inheritance tests |
|---|
| 1219 | genericDef = this |
|---|
| 1220 | while genericDef.genericDef |
|---|
| 1221 | genericDef = genericDef.genericDef to ! |
|---|
| 1222 | |
|---|
| 1223 | if genericDef._constructions is nil |
|---|
| 1224 | genericDef._constructions = Dictionary<of String, Method>() |
|---|
| 1225 | |
|---|
| 1226 | key = TypeUtil.keyForTypeArgs(typeArgs) |
|---|
| 1227 | |
|---|
| 1228 | if genericDef._constructions.containsKey(key) |
|---|
| 1229 | return genericDef._constructions[key] |
|---|
| 1230 | |
|---|
| 1231 | c = .memberwiseClone to Method |
|---|
| 1232 | assert c is not this |
|---|
| 1233 | genericDef._constructions[key] = c |
|---|
| 1234 | c._genericDef = this |
|---|
| 1235 | c._constructions = nil # only the generic def tracks the constructions |
|---|
| 1236 | c._genericParams = List<of IType>(typeArgs) |
|---|
| 1237 | c._params = List<of Param>() |
|---|
| 1238 | typeForParam = TypeUtil.dictionaryOf(.genericParams, typeArgs) |
|---|
| 1239 | for param in .params |
|---|
| 1240 | c._params.add(param.constructedFor(.parentBox, typeForParam)) |
|---|
| 1241 | c._returnType = c._returnType.secondaryConstructedTypeFor(.parentBox, typeForParam) |
|---|
| 1242 | c.bindInt |
|---|
| 1243 | return c |
|---|
| 1244 | |
|---|
| 1245 | def genericParamForName(name as String) as IType? |
|---|
| 1246 | # idea: return find param in _genericParams where param.name == name |
|---|
| 1247 | for param in _genericParams, if param.name == name, return param |
|---|
| 1248 | return nil |
|---|
| 1249 | |
|---|
| 1250 | |
|---|
| 1251 | ## Binding |
|---|
| 1252 | |
|---|
| 1253 | def _bindInt is override |
|---|
| 1254 | assert .compiler.codeMemberStack.peek is this |
|---|
| 1255 | for param in _genericParams, param.bindInt |
|---|
| 1256 | base._bindInt |
|---|
| 1257 | if .name == '__init__', .compiler.warning(this, 'Initializers are named "init" with no surrounding underscores.') |
|---|
| 1258 | if not 'extern' in .isNames and .bodyExclusion == 'dll import', _isNames.add('extern') |
|---|
| 1259 | |
|---|
| 1260 | def _bindImp is override |
|---|
| 1261 | numErrors = .compiler.errors.count |
|---|
| 1262 | base._bindImp |
|---|
| 1263 | for param in _genericParams |
|---|
| 1264 | param.bindImp |
|---|
| 1265 | if _implementsTypeNode |
|---|
| 1266 | _implementsType = _implementsTypeNode.realType |
|---|
| 1267 | # TODO: make sure the type is among the interfaces of the box |
|---|
| 1268 | if .compiler.errors.count == numErrors and _ |
|---|
| 1269 | _returnType not in [.compiler.voidType to IType, .compiler.passThroughType to IType] and _ |
|---|
| 1270 | not .hasReturnStmt and not .hasYieldStmt and not .hasThrowStmt and _ |
|---|
| 1271 | .bodyExclusion is nil |
|---|
| 1272 | # nil |
|---|
| 1273 | .throwError('Missing return statement for method "[_name]" which returns [_returnType.name].') |
|---|
| 1274 | # check for `return` and `yield` in the same method |
|---|
| 1275 | if .resultType is not .compiler.voidType |
|---|
| 1276 | returnStmts = List<of Stmt>() |
|---|
| 1277 | hasYield = false |
|---|
| 1278 | for stmt in _stmts |
|---|
| 1279 | if stmt inherits ReturnStmt |
|---|
| 1280 | returnStmts.add(stmt) |
|---|
| 1281 | else if stmt inherits YieldStmt |
|---|
| 1282 | hasYield = true |
|---|
| 1283 | if hasYield and returnStmts.count |
|---|
| 1284 | for stmt in returnStmts |
|---|
| 1285 | stmt.recordError('Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration.') |
|---|
| 1286 | |
|---|
| 1287 | |
|---|
| 1288 | class ProperDexer |
|---|
| 1289 | is abstract, partial |
|---|
| 1290 | inherits BoxMember |
|---|
| 1291 | """ |
|---|
| 1292 | The abstract base class for Property and Indexer. |
|---|
| 1293 | |
|---|
| 1294 | This captures some of the common interface between them--whatever happens to be needed. |
|---|
| 1295 | |
|---|
| 1296 | Why not do an IProperDexer interface? Because it wouldn't be compatible with a BoxMember |
|---|
| 1297 | unless we also defined an IBoxMember. |
|---|
| 1298 | """ |
|---|
| 1299 | |
|---|
| 1300 | var _getPart as ProperDexerXetter? |
|---|
| 1301 | var _setPart as ProperDexerXetter? |
|---|
| 1302 | var _coverVar as BoxVar? |
|---|
| 1303 | var _coverAccess as String? |
|---|
| 1304 | var _returnType as IType? |
|---|
| 1305 | var _returnTypeNode as ITypeProxy? |
|---|
| 1306 | |
|---|
| 1307 | cue init(token as IToken, idToken as IToken, box as Box, name as String, isNames as String*, attribs as AttributeList, docString as String?) |
|---|
| 1308 | base.init(token, idToken, box, name, isNames, attribs, docString) |
|---|
| 1309 | |
|---|
| 1310 | cue init(token as IToken, idToken as IToken, box as Box, name as String, returnTypeOrNode as INode, isNames as String*, attribs as AttributeList, docString as String) |
|---|
| 1311 | .init(token, idToken, box, name, isNames, attribs, docString) |
|---|
| 1312 | if returnTypeOrNode inherits IType |
|---|
| 1313 | _returnType = returnTypeOrNode |
|---|
| 1314 | _returnTypeNode = nil |
|---|
| 1315 | else if returnTypeOrNode inherits ITypeProxy |
|---|
| 1316 | _returnType = nil |
|---|
| 1317 | _returnTypeNode = returnTypeOrNode |
|---|
| 1318 | else |
|---|
| 1319 | throw FallThroughException(returnTypeOrNode) |
|---|
| 1320 | |
|---|
| 1321 | def makeGetPart(token as IToken) as AbstractMethod is abstract |
|---|
| 1322 | require .getPart is nil |
|---|
| 1323 | ensure .getPart |
|---|
| 1324 | |
|---|
| 1325 | def makeSetPart(token as IToken) as AbstractMethod is abstract |
|---|
| 1326 | require .setPart is nil |
|---|
| 1327 | ensure .setPart |
|---|
| 1328 | |
|---|
| 1329 | def addRefFields is override |
|---|
| 1330 | base.addRefFields |
|---|
| 1331 | if _coverVar, .addField('coverVar', _coverVar) |
|---|
| 1332 | if _returnType, .addField('returnType', _returnType) |
|---|
| 1333 | else, .addField('returnTypeNode', _returnTypeNode) |
|---|
| 1334 | |
|---|
| 1335 | def addSubFields is override |
|---|
| 1336 | base.addSubFields |
|---|
| 1337 | if _getPart, .addField('getPart', _getPart) |
|---|
| 1338 | if _setPart, .addField('setPart', _setPart) |
|---|
| 1339 | |
|---|
| 1340 | get returnType from var |
|---|
| 1341 | |
|---|
| 1342 | get returnTypeNode from var |
|---|
| 1343 | |
|---|
| 1344 | get resultType as IType is override |
|---|
| 1345 | if not .didBindInt, .bindInt # to call a method from a var/const initializer |
|---|
| 1346 | assert .didBindInt |
|---|
| 1347 | assert _returnType |
|---|
| 1348 | return _returnType to ! |
|---|
| 1349 | |
|---|
| 1350 | get getPart from var |
|---|
| 1351 | |
|---|
| 1352 | get setPart from var |
|---|
| 1353 | |
|---|
| 1354 | get shouldBeVirtual as bool is override |
|---|
| 1355 | return .isClassMember and not .isPrivate # TODO: and not _implementsTypeNode and not _implementsType |
|---|
| 1356 | |
|---|
| 1357 | def mergedIntoPartialBox(newBox as Box) |
|---|
| 1358 | base.mergedIntoPartialBox(newBox) |
|---|
| 1359 | if .getPart, .getPart.mergedIntoPartialBox(newBox) |
|---|
| 1360 | if .setPart, .setPart.mergedIntoPartialBox(newBox) |
|---|
| 1361 | |
|---|
| 1362 | def unNilReturnType is override |
|---|
| 1363 | if _returnType |
|---|
| 1364 | if _returnType inherits NilableType |
|---|
| 1365 | _returnType = _returnType.nonNil |
|---|
| 1366 | else if _returnTypeNode inherits NilableTypeProxy |
|---|
| 1367 | _returnTypeNode = _returnTypeNode.innerTypeProxy |
|---|
| 1368 | |
|---|
| 1369 | def _bindInt is override |
|---|
| 1370 | base._bindInt |
|---|
| 1371 | if _returnType is nil |
|---|
| 1372 | if _coverVar |
|---|
| 1373 | _coverVar.bindInt |
|---|
| 1374 | _returnType = _coverVar.resultType |
|---|
| 1375 | else |
|---|
| 1376 | assert _returnTypeNode |
|---|
| 1377 | _returnType = _returnTypeNode.realType |
|---|
| 1378 | assert _returnType # properties always have a return type |
|---|
| 1379 | |
|---|
| 1380 | if _coverVar |
|---|
| 1381 | if _coverAccess in ['getset', 'get'] |
|---|
| 1382 | # return VARNAME |
|---|
| 1383 | .makeGetPart(.token).statements.add(ReturnStmt(.token, IdentifierExpr(.token, _coverVar))) |
|---|
| 1384 | if _coverAccess in ['getset', 'set'] |
|---|
| 1385 | # VARNAME = value |
|---|
| 1386 | .makeSetPart(.token).statements.add(AssignExpr(.token, 'ASSIGN', IdentifierExpr(.token, _coverVar), IdentifierExpr(.token, 'value'))) |
|---|
| 1387 | |
|---|
| 1388 | if _getPart, _getPart.bindInt |
|---|
| 1389 | if _setPart, _setPart.bindInt |
|---|
| 1390 | |
|---|
| 1391 | def _computeMatchingBaseMembers |
|---|
| 1392 | base._computeMatchingBaseMembers |
|---|
| 1393 | if _getPart, _getPart.computeMatchingBaseMembers |
|---|
| 1394 | if _setPart, _setPart.computeMatchingBaseMembers |
|---|
| 1395 | |
|---|
| 1396 | def _bindImp is override |
|---|
| 1397 | base._bindImp |
|---|
| 1398 | if _getPart, _getPart.bindImp |
|---|
| 1399 | if _setPart, _setPart.bindImp |
|---|
| 1400 | |
|---|
| 1401 | def constructedFor(box as Box, gpToType as Dictionary<of GenericParam, IType>) as BoxMember # CC: as same |
|---|
| 1402 | newMember = base.constructedFor(box, gpToType) to ProperDexer # CC: this = base. ... |
|---|
| 1403 | assert newMember._returnType |
|---|
| 1404 | newMember._returnType = newMember._returnType.secondaryConstructedTypeFor(box, gpToType) |
|---|
| 1405 | return newMember |
|---|
| 1406 | |
|---|
| 1407 | |
|---|
| 1408 | class ProperDexerXetter |
|---|
| 1409 | is abstract, partial |
|---|
| 1410 | inherits AbstractMethod |
|---|
| 1411 | """ |
|---|
| 1412 | Base class for IndexerGetter, IndexerSetter, PropertyGetter and PropertySetter. |
|---|
| 1413 | """ |
|---|
| 1414 | |
|---|
| 1415 | var _parent as ProperDexer |
|---|
| 1416 | |
|---|
| 1417 | cue init(token as IToken, parent as ProperDexer, isNames as String*) |
|---|
| 1418 | base.init(token, token, parent.parentBox, nil, parent.name, List<of Param>(), isNames, AttributeList(), '') # TODO: should be docString, right? |
|---|
| 1419 | _name = parent.name + '.' + .xetPartName # CC: somewhat awkward. belongs in the base.init() call |
|---|
| 1420 | _parent = parent |
|---|
| 1421 | |
|---|
| 1422 | get canHaveMatchingBaseMember as bool is override |
|---|
| 1423 | return _parent.canHaveMatchingBaseMember |
|---|
| 1424 | |
|---|
| 1425 | get parent from var |
|---|
| 1426 | |
|---|
| 1427 | get xetPartName as String is abstract |
|---|
| 1428 | |
|---|
| 1429 | get isNames as String* is override |
|---|
| 1430 | # may need to get more sophisticated here if, in the future, we support C#'s somewhat recent ability to put access modifiers on just the set or get part of a property |
|---|
| 1431 | return _parent.isNames |
|---|
| 1432 | |
|---|
| 1433 | get isShared as bool is override |
|---|
| 1434 | return _parent.isShared |
|---|
| 1435 | |
|---|
| 1436 | get isOverride as bool is override |
|---|
| 1437 | return _parent.isOverride |
|---|
| 1438 | |
|---|
| 1439 | def usesBase is override |
|---|
| 1440 | # it's the property that needs to compute the "is override" or "is new" for itself, |
|---|
| 1441 | # not the get or set part |
|---|
| 1442 | _parent.usesBase |
|---|
| 1443 | |
|---|
| 1444 | |
|---|
| 1445 | class MemberOverload inherits BoxMember is partial |
|---|
| 1446 | """ |
|---|
| 1447 | Methods and indexers can be overloaded. |
|---|
| 1448 | """ |
|---|
| 1449 | |
|---|
| 1450 | var _members as List<of IOverloadable> |
|---|
| 1451 | |
|---|
| 1452 | cue init(member as IOverloadable) |
|---|
| 1453 | require |
|---|
| 1454 | member.name.length |
|---|
| 1455 | ensure |
|---|
| 1456 | .members.count |
|---|
| 1457 | .members[0] == member |
|---|
| 1458 | body |
|---|
| 1459 | base.init(member.token, member.idToken, member.parentBox, member.name, member.isNames, AttributeList()) |
|---|
| 1460 | member.overloadGroup = this |
|---|
| 1461 | _members = [member] |
|---|
| 1462 | _isNames = member.isNames.toList # pick up 'shared' for example |
|---|
| 1463 | |
|---|
| 1464 | def addMinFields is override |
|---|
| 1465 | base.addMinFields |
|---|
| 1466 | .addField('numMembers', .members.count) |
|---|
| 1467 | |
|---|
| 1468 | def addSubFields is override |
|---|
| 1469 | base.addSubFields |
|---|
| 1470 | .addField('members', _members) |
|---|
| 1471 | |
|---|
| 1472 | get members from var |
|---|
| 1473 | |
|---|
| 1474 | get englishName as String is override |
|---|
| 1475 | assert _members.count |
|---|
| 1476 | return _members[0].englishName |
|---|
| 1477 | |
|---|
| 1478 | get isCallable as bool is override |
|---|
| 1479 | assert _members.count |
|---|
| 1480 | return _members[0].isCallable |
|---|
| 1481 | |
|---|
| 1482 | get isMethod as bool is override |
|---|
| 1483 | return _members[0].isMethod |
|---|
| 1484 | |
|---|
| 1485 | def addMember(member as IOverloadable) |
|---|
| 1486 | require |
|---|
| 1487 | member.overloadGroup is nil |
|---|
| 1488 | member.name==.name |
|---|
| 1489 | member not in .members |
|---|
| 1490 | member.getType is .members[0].getType |
|---|
| 1491 | body |
|---|
| 1492 | member.overloadGroup = this |
|---|
| 1493 | _members.add(member) |
|---|
| 1494 | .parentBox.addDeclFromOverload(member, this) |
|---|
| 1495 | |
|---|
| 1496 | def addInheritedMember(member as IOverloadable) |
|---|
| 1497 | require |
|---|
| 1498 | member.name==.name |
|---|
| 1499 | member.parentBox is not .parentBox |
|---|
| 1500 | member not in .members |
|---|
| 1501 | member.getType is .members[0].getType |
|---|
| 1502 | body |
|---|
| 1503 | _members.add(member) |
|---|
| 1504 | |
|---|
| 1505 | def addInheritedMemberIfNoMatch(member as IOverloadable) |
|---|
| 1506 | """ |
|---|
| 1507 | Utility method for _finishOverloads to avoid the mistake of putting an ancestor method in an overload that was an override or new. |
|---|
| 1508 | """ |
|---|
| 1509 | for existing in _members, if member.matchesSignature(existing), return |
|---|
| 1510 | .addInheritedMember(member) |
|---|
| 1511 | |
|---|
| 1512 | get resultType as IType? is override |
|---|
| 1513 | assert .didBindInt |
|---|
| 1514 | return _members[0].resultType |
|---|
| 1515 | |
|---|
| 1516 | def unNilReturnType is override |
|---|
| 1517 | for member in _members |
|---|
| 1518 | member.unNilReturnType |
|---|
| 1519 | |
|---|
| 1520 | def computeBestOverload(args as List<of Expr>, genericArgTypes as List<of IType>?, strictBindChecking as bool) as IOverloadable |
|---|
| 1521 | # References: |
|---|
| 1522 | # http://blogs.msdn.com/ericlippert/archive/2007/11/05/c-3-0-return-type-inference-does-not-work-on-member-groups.aspx |
|---|
| 1523 | |
|---|
| 1524 | # I cooked up the algorithm below as a quick way to fix some bugs caused by the previous |
|---|
| 1525 | # implementation of um, doing nothing. |
|---|
| 1526 | # But this likely needs to be rewritten. |
|---|
| 1527 | candidates = [] |
|---|
| 1528 | # handle generic arguments to the method |
|---|
| 1529 | if genericArgTypes and genericArgTypes.count |
|---|
| 1530 | members = List<of IOverloadable>() |
|---|
| 1531 | for member as IOverloadable? in .members |
|---|
| 1532 | if member inherits Method |
|---|
| 1533 | if member.containsGenericParameters |
|---|
| 1534 | if member.genericParams.count == genericArgTypes.count |
|---|
| 1535 | member = member.constructedMethodWith(genericArgTypes to !) |
|---|
| 1536 | else |
|---|
| 1537 | member = nil |
|---|
| 1538 | if member, members.add(member) |
|---|
| 1539 | else |
|---|
| 1540 | members = .members |
|---|
| 1541 | |
|---|
| 1542 | _calcMembersScore(members, args, strictBindChecking, candidates) |
|---|
| 1543 | |
|---|
| 1544 | maxScore = -10_000 |
|---|
| 1545 | winner = nil to IOverloadable? |
|---|
| 1546 | for pair in candidates |
|---|
| 1547 | if false, print pair[0], (pair[1] to IOverloadable).idString |
|---|
| 1548 | if pair[0] to int > maxScore |
|---|
| 1549 | maxScore = pair[0] to int |
|---|
| 1550 | winner = pair[1] to IOverloadable |
|---|
| 1551 | |
|---|
| 1552 | if false |
|---|
| 1553 | # detect overload invocation ambiguity |
|---|
| 1554 | # TODO: not ready for this yet |
|---|
| 1555 | count = 0 |
|---|
| 1556 | for pair in candidates |
|---|
| 1557 | if pair[0] to int == maxScore |
|---|
| 1558 | count += 1 |
|---|
| 1559 | if count > 1 |
|---|
| 1560 | # TODO: do this for indexing too |
|---|
| 1561 | msg = 'The call is ambiguous between these methods: ' |
|---|
| 1562 | sep = '' |
|---|
| 1563 | for pair in candidates |
|---|
| 1564 | if pair[0] to int == maxScore |
|---|
| 1565 | msg += sep + (pair[1] to IOverloadable).cobraSourceSignature(false) |
|---|
| 1566 | sep = ', ' |
|---|
| 1567 | .throwError(msg) |
|---|
| 1568 | |
|---|
| 1569 | if false |
|---|
| 1570 | print |
|---|
| 1571 | trace .token.fileName |
|---|
| 1572 | trace maxScore, _name |
|---|
| 1573 | trace .token.toTechString |
|---|
| 1574 | trace winner |
|---|
| 1575 | print 'args:' |
|---|
| 1576 | for arg in args, print ' [arg]' |
|---|
| 1577 | print 'params:' |
|---|
| 1578 | for param in winner.params, print ' [param]' |
|---|
| 1579 | print 'overloads:' |
|---|
| 1580 | for member in .members, print ' [member]' |
|---|
| 1581 | # print 'winner:', score, winner |
|---|
| 1582 | assert winner |
|---|
| 1583 | return winner to ! |
|---|
| 1584 | |
|---|
| 1585 | def _calcMembersScore(members as IList<of IOverloadable>, args as List<of Expr>, strictBindChecking as bool, candidates) |
|---|
| 1586 | for member in members |
|---|
| 1587 | score = -1000 |
|---|
| 1588 | if member.params.count == args.count |
|---|
| 1589 | score = 0 |
|---|
| 1590 | if member inherits Method and (member to Method).genericParams.count > 0, score += 1 |
|---|
| 1591 | for i, param in member.params.numbered |
|---|
| 1592 | arg = args[i] |
|---|
| 1593 | if strictBindChecking and not arg.didBindImp |
|---|
| 1594 | trace arg |
|---|
| 1595 | trace arg.hasError |
|---|
| 1596 | if strictBindChecking and (arg.type == param.type or arg.type == param.type.nonNil) |
|---|
| 1597 | score += 20 |
|---|
| 1598 | else if strictBindChecking and arg.canBeAssignedTo(param.type) |
|---|
| 1599 | score += 10 |
|---|
| 1600 | else if strictBindChecking and arg.type.nonNil.isAssignableTo(param.type) |
|---|
| 1601 | # Cobra's code and data flow analysis sometimes leaves us with a nilable type that's not actually nil anymore |
|---|
| 1602 | # due to an assignment, possibly wrapped in an if statement. Eventually this will be corrected, but for now |
|---|
| 1603 | # compensate here. |
|---|
| 1604 | score += 1 |
|---|
| 1605 | else if not strictBindChecking |
|---|
| 1606 | if arg.type is not nil |
|---|
| 1607 | if arg.type == param.type |
|---|
| 1608 | score += 20 |
|---|
| 1609 | else if arg.type.isDynamic |
|---|
| 1610 | score += 10 |
|---|
| 1611 | else |
|---|
| 1612 | score -= 100 |
|---|
| 1613 | else |
|---|
| 1614 | score -= 100 |
|---|
| 1615 | # print 'candidate:', score, member.name, member.serialNum, Utils.join(', ', (for param in member.params get param.type.name)) |
|---|
| 1616 | candidates.add([score, member]) |
|---|
| 1617 | |
|---|
| 1618 | def _bindInt is override |
|---|
| 1619 | base._bindInt |
|---|
| 1620 | # sanity check that members all have the right name |
|---|
| 1621 | name = .name |
|---|
| 1622 | for member in _members, assert member.name == name |
|---|
| 1623 | # sanity check that members don't have the same signature |
|---|
| 1624 | #if name <> 'getType' # TODO: HACK: See Box.prepSystemObjectClass |
|---|
| 1625 | # TODO: having problems with explicit interface implementation. I think these probably should have their name changed from "foo" to "Type.foo" |
|---|
| 1626 | # for i = 0 .. _members.count |
|---|
| 1627 | # for j = i+1 .. _members.count |
|---|
| 1628 | # assert not _members[i].matchesSignature(_members[j]) |
|---|
| 1629 | # rename the test members of the overload by suffixing them with '_o1' '_o2' etc. |
|---|
| 1630 | |
|---|
| 1631 | for i, member in _members.numbered |
|---|
| 1632 | for tm in member.testMethods, tm.overloadId = i+1 |
|---|
| 1633 | |
|---|
| 1634 | # TODO: error if the two members have the same arguments |
|---|
| 1635 | |
|---|
| 1636 | def _computeMatchingBaseMembers |
|---|
| 1637 | base._computeMatchingBaseMembers |
|---|
| 1638 | for member in _members, member.computeMatchingBaseMembers |
|---|
| 1639 | |
|---|
| 1640 | def _bindImp is override |
|---|
| 1641 | base._bindImp |
|---|
| 1642 | |
|---|
| 1643 | |
|---|
| 1644 | class TestMethod |
|---|
| 1645 | is partial |
|---|
| 1646 | inherits Method |
|---|
| 1647 | """ |
|---|
| 1648 | TODO: Does TestMethod need to be altered in light of generic parameters to methods? |
|---|
| 1649 | """ |
|---|
| 1650 | |
|---|
| 1651 | var _forMember as BoxMember? |
|---|
| 1652 | var _forBox as Box? |
|---|
| 1653 | var _overloadId = -1 |
|---|
| 1654 | |
|---|
| 1655 | cue init(token as IToken, member as BoxMember) |
|---|
| 1656 | require |
|---|
| 1657 | member.name.length |
|---|
| 1658 | body |
|---|
| 1659 | base.init(token, Token.empty, member.parentBox, 'test_'+member.name.capitalized, List<of Param>(), .getCompiler.voidType, nil, ['shared'], AttributeList(), '') |
|---|
| 1660 | _forMember = member |
|---|
| 1661 | |
|---|
| 1662 | cue init(token as IToken, box as Box) |
|---|
| 1663 | require |
|---|
| 1664 | box.name.length |
|---|
| 1665 | body |
|---|
| 1666 | base.init(token, Token.empty, box, 'test_class_'+box.name.capitalized, List<of Param>(), .getCompiler.voidType, nil, ['shared'], AttributeList(), '') |
|---|
| 1667 | _forBox = box |
|---|
| 1668 | |
|---|
| 1669 | get shouldBeVirtual as bool is override |
|---|
| 1670 | return false |
|---|
| 1671 | |
|---|
| 1672 | pro name as String is new |
|---|
| 1673 | """ |
|---|
| 1674 | NamedNode.name does not allow the name to be set because that could mess up dictionaries |
|---|
| 1675 | that index the node by name. However, there are no such dictionaries for TestMethod and |
|---|
| 1676 | MemberOverload() needs to mangle test method names so they don't collide. |
|---|
| 1677 | """ |
|---|
| 1678 | get |
|---|
| 1679 | return base.name |
|---|
| 1680 | set |
|---|
| 1681 | require value.length |
|---|
| 1682 | _name = value |
|---|
| 1683 | |
|---|
| 1684 | pro overloadId from var |
|---|
| 1685 | |
|---|
| 1686 | |
|---|
| 1687 | class ContractPart |
|---|
| 1688 | is abstract, partial |
|---|
| 1689 | inherits SyntaxNode |
|---|
| 1690 | """ |
|---|
| 1691 | The abstract base class for RequirePart and EnsurePart. |
|---|
| 1692 | """ |
|---|
| 1693 | |
|---|
| 1694 | var _connectToken as IToken? |
|---|
| 1695 | var _codeMember as AbstractMethod |
|---|
| 1696 | var _exprs as List<of Expr> |
|---|
| 1697 | |
|---|
| 1698 | cue init(codeMember as AbstractMethod) |
|---|
| 1699 | base.init(codeMember.token) |
|---|
| 1700 | .isImplicit = true |
|---|
| 1701 | _codeMember = codeMember |
|---|
| 1702 | _exprs = List<of Expr>() |
|---|
| 1703 | |
|---|
| 1704 | cue init(connectToken as IToken?, mainToken as IToken, codeMember as AbstractMethod, exprs as List<of Expr>) |
|---|
| 1705 | base.init(mainToken) |
|---|
| 1706 | _connectToken = connectToken |
|---|
| 1707 | _codeMember = codeMember |
|---|
| 1708 | _exprs = exprs |
|---|
| 1709 | |
|---|
| 1710 | def addMinFields |
|---|
| 1711 | base.addMinFields |
|---|
| 1712 | .addField('connectToken', _connectToken) |
|---|
| 1713 | |
|---|
| 1714 | def addSubFields |
|---|
| 1715 | base.addSubFields |
|---|
| 1716 | .addField('exprs', _exprs) |
|---|
| 1717 | |
|---|
| 1718 | get codeMember from var |
|---|
| 1719 | |
|---|
| 1720 | get connectWord as String is abstract |
|---|
| 1721 | |
|---|
| 1722 | get exprs from var |
|---|
| 1723 | has Subnodes |
|---|
| 1724 | |
|---|
| 1725 | get haveConditions as bool is abstract |
|---|
| 1726 | """ |
|---|
| 1727 | Returns true if this contract part has any conditions, whether directly or through inheritance. |
|---|
| 1728 | """ |
|---|
| 1729 | |
|---|
| 1730 | get willGenerateCode as bool |
|---|
| 1731 | # classes that are sensitive with respect to the DST skip on contracts |
|---|
| 1732 | return _codeMember.canHaveDetailedStackTrace and .compiler.options['contracts'] <> 'none' and not (.willInlineCode and not .haveConditions) |
|---|
| 1733 | |
|---|
| 1734 | get willInlineCode as bool |
|---|
| 1735 | return .compiler.options['contracts'] == 'inline' |
|---|
| 1736 | |
|---|
| 1737 | def _bindImp is override |
|---|
| 1738 | base._bindImp |
|---|
| 1739 | # note: expressions cannot refer to locals in contracts |
|---|
| 1740 | newExprs = List<of Expr>() |
|---|
| 1741 | for expr in _exprs |
|---|
| 1742 | expr.bindImp # TODO: error recovery |
|---|
| 1743 | assert expr.type |
|---|
| 1744 | if not expr.hasError, _checkForLocalReferences(expr) |
|---|
| 1745 | if expr.type is not .compiler.boolType |
|---|
| 1746 | expr = TruthExpr(expr).bindAll to Expr # CC: axe cast when Cobra supports "as this" |
|---|
| 1747 | assert expr.type |
|---|
| 1748 | newExprs.add(expr) |
|---|
| 1749 | _exprs.clear |
|---|
| 1750 | _exprs.addRange(newExprs) |
|---|
| 1751 | |
|---|
| 1752 | def _checkForLocalReferences(expr as Expr) |
|---|
| 1753 | validDefs = Set<of dynamic>() |
|---|
| 1754 | lastExpr as Expr? |
|---|
| 1755 | for expr in expr.allExprs |
|---|
| 1756 | if expr inherits IdentifierExpr |
|---|
| 1757 | if expr.definition inherits LocalVar |
|---|
| 1758 | if (expr.name <> 'result' or not expr.definition.isImplicit) and (expr.definition not in validDefs) |
|---|
| 1759 | if lastExpr inherits ForExpr |
|---|
| 1760 | .flagVar(expr.definition to LocalVar) |
|---|
| 1761 | validDefs.add(expr.definition) |
|---|
| 1762 | else |
|---|
| 1763 | expr.recordError('Cannot refer to local variable "[expr.name]" in a contract. Use an "assert" statement in the implementation to verify conditions on locals.') |
|---|
| 1764 | lastExpr = expr |
|---|
| 1765 | |
|---|
| 1766 | def flagVar(varr as LocalVar) is abstract |
|---|
| 1767 | |
|---|
| 1768 | def checkConnectToken |
|---|
| 1769 | """ |
|---|
| 1770 | This method checks that you said 'or require' vs. 'require', or 'and ensure' vs. 'ensure'. |
|---|
| 1771 | The check cannot be made during _bindImp because it relies on the .codeMember.isNames which can be altered when binding the statements in the method body (per .usesBase). |
|---|
| 1772 | """ |
|---|
| 1773 | isNames = _codeMember.isNames |
|---|
| 1774 | if not .isImplicit |
|---|
| 1775 | if _connectToken |
|---|
| 1776 | if 'override' not in isNames |
|---|
| 1777 | .recordError('The contract was specified with "[_connectToken.text]", but the code member is neither an "override" nor "new".') |
|---|
| 1778 | else |
|---|
| 1779 | if 'override' in isNames |
|---|
| 1780 | what = if('override' in isNames, 'override', 'new') |
|---|
| 1781 | .recordError('The contract must be declared "[.connectWord] [.token.text]" rather than "[.token.text]" because the code member is "[what]".') |
|---|
| 1782 | |
|---|
| 1783 | |
|---|
| 1784 | class RequirePart |
|---|
| 1785 | is partial |
|---|
| 1786 | inherits ContractPart |
|---|
| 1787 | |
|---|
| 1788 | cue init(codeMember as AbstractMethod) |
|---|
| 1789 | base.init(codeMember) |
|---|
| 1790 | |
|---|
| 1791 | cue init(connectToken as IToken?, mainToken as IToken, codeMember as AbstractMethod, exprs as List<of Expr>) |
|---|
| 1792 | base.init(connectToken, mainToken, codeMember, exprs) |
|---|
| 1793 | codeMember.requirePart = this |
|---|
| 1794 | |
|---|
| 1795 | get connectWord as String is override |
|---|
| 1796 | return 'or' |
|---|
| 1797 | |
|---|
| 1798 | def flagVar(varr as LocalVar) is override |
|---|
| 1799 | varr.isForRequire = true |
|---|
| 1800 | |
|---|
| 1801 | get haveConditions as bool is override |
|---|
| 1802 | if _codeMember.isOverride |
|---|
| 1803 | have = false |
|---|
| 1804 | curCodeMember = _codeMember to ? |
|---|
| 1805 | post while curCodeMember |
|---|
| 1806 | if curCodeMember.requirePart.exprs.count |
|---|
| 1807 | have = true |
|---|
| 1808 | break |
|---|
| 1809 | curCodeMember = curCodeMember.matchingBaseMember to AbstractMethod? |
|---|
| 1810 | return have |
|---|
| 1811 | else |
|---|
| 1812 | return _codeMember.requirePart.exprs.count > 0 |
|---|
| 1813 | |
|---|
| 1814 | |
|---|
| 1815 | class EnsurePart inherits ContractPart is partial |
|---|
| 1816 | |
|---|
| 1817 | cue init(codeMember as AbstractMethod) |
|---|
| 1818 | base.init(codeMember) |
|---|
| 1819 | |
|---|
| 1820 | cue init(connectToken as IToken?, mainToken as IToken, codeMember as AbstractMethod, exprs as List<of Expr>) |
|---|
| 1821 | base.init(connectToken, mainToken, codeMember, exprs) |
|---|
| 1822 | codeMember.ensurePart = this |
|---|
| 1823 | |
|---|
| 1824 | get connectWord as String is override |
|---|
| 1825 | return 'and' |
|---|
| 1826 | |
|---|
| 1827 | def flagVar(varr as LocalVar) is override |
|---|
| 1828 | varr.isForEnsure = true |
|---|
| 1829 | |
|---|
| 1830 | get haveConditions as bool is override |
|---|
| 1831 | if _codeMember.parentBox.hasInvariants |
|---|
| 1832 | return true |
|---|
| 1833 | have = false |
|---|
| 1834 | curCodeMember = _codeMember to ? |
|---|
| 1835 | post while curCodeMember |
|---|
| 1836 | if curCodeMember.ensurePart.exprs.count |
|---|
| 1837 | have = true |
|---|
| 1838 | curCodeMember = curCodeMember.matchingBaseMember to AbstractMethod? # TODO: should a cast really be needed here? |
|---|
| 1839 | return have |
|---|
| 1840 | |
|---|
| 1841 | def _bindImp |
|---|
| 1842 | codeMember = _codeMember |
|---|
| 1843 | if codeMember.resultType is .compiler.voidType |
|---|
| 1844 | base._bindImp |
|---|
| 1845 | else |
|---|
| 1846 | resultLocal = codeMember.findLocal('result') |
|---|
| 1847 | resultBuiltIn = ResultVar(.token, _codeMember) |
|---|
| 1848 | if resultLocal, codeMember.replaceLocal(resultBuiltIn) |
|---|
| 1849 | else, codeMember.addLocal(resultBuiltIn) |
|---|
| 1850 | try |
|---|
| 1851 | base._bindImp |
|---|
| 1852 | finally |
|---|
| 1853 | if resultLocal, codeMember.replaceLocal(resultLocal to LocalVar) |
|---|
| 1854 | else, codeMember.removeLocal('result') |
|---|