1 /*
2 AngelCode Scripting Library
3 Copyright (c) 2003-2017 Andreas Jonsson
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any
7 damages arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any
10 purpose, including commercial applications, and to alter it and
11 redistribute it freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you
14 must not claim that you wrote the original software. If you use
15 this software in a product, an acknowledgment in the product
16 documentation would be appreciated but is not required.
17
18 2. Altered source versions must be plainly marked as such, and
19 must not be misrepresented as being the original software.
20
21 3. This notice may not be removed or altered from any source
22 distribution.
23
24 The original version of this library can be located at:
25 http://www.angelcode.com/angelscript/
26
27 Andreas Jonsson
28 andreas@angelcode.com
29 */
30
31
32 //
33 // as_datatype.cpp
34 //
35 // This class describes the datatype for expressions during compilation
36 //
37
38 #include "as_config.h"
39 #include "as_datatype.h"
40 #include "as_tokendef.h"
41 #include "as_typeinfo.h"
42 #include "as_objecttype.h"
43 #include "as_scriptengine.h"
44 #include "as_tokenizer.h"
45
46 BEGIN_AS_NAMESPACE
47
asCDataType()48 asCDataType::asCDataType()
49 {
50 tokenType = ttUnrecognizedToken;
51 typeInfo = 0;
52 isReference = false;
53 isReadOnly = false;
54 isAuto = false;
55 isObjectHandle = false;
56 isConstHandle = false;
57 isHandleToAsHandleType = false;
58 ifHandleThenConst = false;
59 }
60
asCDataType(const asCDataType & dt)61 asCDataType::asCDataType(const asCDataType &dt)
62 {
63 tokenType = dt.tokenType;
64 typeInfo = dt.typeInfo;
65 isReference = dt.isReference;
66 isReadOnly = dt.isReadOnly;
67 isAuto = dt.isAuto;
68 isObjectHandle = dt.isObjectHandle;
69 isConstHandle = dt.isConstHandle;
70 isHandleToAsHandleType = dt.isHandleToAsHandleType;
71 ifHandleThenConst = dt.ifHandleThenConst;
72 }
73
~asCDataType()74 asCDataType::~asCDataType()
75 {
76 }
77
IsValid() const78 bool asCDataType::IsValid() const
79 {
80 if( tokenType == ttUnrecognizedToken &&
81 !isObjectHandle )
82 return false;
83
84 return true;
85 }
86
CreateType(asCTypeInfo * ti,bool isConst)87 asCDataType asCDataType::CreateType(asCTypeInfo *ti, bool isConst)
88 {
89 asCDataType dt;
90
91 dt.tokenType = ttIdentifier;
92 dt.typeInfo = ti;
93 dt.isReadOnly = isConst;
94
95 return dt;
96 }
97
CreateAuto(bool isConst)98 asCDataType asCDataType::CreateAuto(bool isConst)
99 {
100 asCDataType dt;
101
102 dt.tokenType = ttIdentifier;
103 dt.isReadOnly = isConst;
104 dt.isAuto = true;
105
106 return dt;
107 }
108
CreateObjectHandle(asCTypeInfo * ot,bool isConst)109 asCDataType asCDataType::CreateObjectHandle(asCTypeInfo *ot, bool isConst)
110 {
111 asCDataType dt;
112
113 asASSERT(CastToObjectType(ot));
114
115 dt.tokenType = ttIdentifier;
116 dt.typeInfo = ot;
117 dt.isObjectHandle = true;
118 dt.isConstHandle = isConst;
119
120 return dt;
121 }
122
CreatePrimitive(eTokenType tt,bool isConst)123 asCDataType asCDataType::CreatePrimitive(eTokenType tt, bool isConst)
124 {
125 asCDataType dt;
126
127 dt.tokenType = tt;
128 dt.isReadOnly = isConst;
129
130 return dt;
131 }
132
CreateNullHandle()133 asCDataType asCDataType::CreateNullHandle()
134 {
135 asCDataType dt;
136
137 dt.tokenType = ttUnrecognizedToken;
138 dt.isReadOnly = true;
139 dt.isObjectHandle = true;
140 dt.isConstHandle = true;
141
142 return dt;
143 }
144
IsNullHandle() const145 bool asCDataType::IsNullHandle() const
146 {
147 if( tokenType == ttUnrecognizedToken &&
148 typeInfo == 0 &&
149 isObjectHandle )
150 return true;
151
152 return false;
153 }
154
Format(asSNameSpace * currNs,bool includeNamespace) const155 asCString asCDataType::Format(asSNameSpace *currNs, bool includeNamespace) const
156 {
157 if( IsNullHandle() )
158 return "<null handle>";
159
160 asCString str;
161
162 if( isReadOnly )
163 str = "const ";
164
165 // If the type is not declared in the current namespace, then the namespace
166 // must always be informed to guarantee that the correct type is informed
167 if (includeNamespace || (typeInfo && typeInfo->nameSpace != currNs))
168 {
169 if (typeInfo && typeInfo->nameSpace && typeInfo->nameSpace->name != "")
170 str += typeInfo->nameSpace->name + "::";
171 }
172 if (typeInfo && typeInfo->nameSpace == 0)
173 {
174 // If funcDef->nameSpace is null it means the funcDef was declared as member of
175 // another type, in which case the scope should be built with the name of that type
176 str += CastToFuncdefType(typeInfo)->parentClass->name + "::";
177 }
178
179 if( tokenType != ttIdentifier )
180 {
181 str += asCTokenizer::GetDefinition(tokenType);
182 }
183 else if( IsArrayType() && typeInfo && !typeInfo->engine->ep.expandDefaultArrayToTemplate )
184 {
185 asCObjectType *ot = CastToObjectType(typeInfo);
186 asASSERT( ot && ot->templateSubTypes.GetLength() == 1 );
187 str += ot->templateSubTypes[0].Format(currNs, includeNamespace);
188 str += "[]";
189 }
190 else if(typeInfo)
191 {
192 str += typeInfo->name;
193 asCObjectType *ot = CastToObjectType(typeInfo);
194 if( ot && ot->templateSubTypes.GetLength() > 0 )
195 {
196 str += "<";
197 for( asUINT subtypeIndex = 0; subtypeIndex < ot->templateSubTypes.GetLength(); subtypeIndex++ )
198 {
199 str += ot->templateSubTypes[subtypeIndex].Format(currNs, includeNamespace);
200 if( subtypeIndex != ot->templateSubTypes.GetLength()-1 )
201 str += ",";
202 }
203 str += ">";
204 }
205 }
206 else if( isAuto )
207 {
208 str += "<auto>";
209 }
210 else
211 {
212 str = "<unknown>";
213 }
214
215 if( isObjectHandle )
216 {
217 str += "@";
218 if( isConstHandle )
219 str += "const";
220 }
221
222 if( isReference )
223 str += "&";
224
225 return str;
226 }
227
operator =(const asCDataType & dt)228 asCDataType &asCDataType::operator =(const asCDataType &dt)
229 {
230 tokenType = dt.tokenType;
231 isReference = dt.isReference;
232 typeInfo = dt.typeInfo;
233 isReadOnly = dt.isReadOnly;
234 isObjectHandle = dt.isObjectHandle;
235 isConstHandle = dt.isConstHandle;
236 isAuto = dt.isAuto;
237 isHandleToAsHandleType = dt.isHandleToAsHandleType;
238 ifHandleThenConst = dt.ifHandleThenConst;
239
240 return (asCDataType &)*this;
241 }
242
MakeHandle(bool b,bool acceptHandleForScope)243 int asCDataType::MakeHandle(bool b, bool acceptHandleForScope)
244 {
245 if( !b )
246 {
247 isObjectHandle = false;
248 isConstHandle = false;
249 isHandleToAsHandleType = false;
250 }
251 else
252 {
253 if( isAuto )
254 {
255 isObjectHandle = true;
256 }
257 else if( !isObjectHandle )
258 {
259 // Only reference types are allowed to be handles,
260 // but not nohandle reference types, and not scoped references
261 // (except when returned from registered function)
262 // funcdefs are special reference types and support handles
263 // value types with asOBJ_ASHANDLE are treated as a handle
264 if( (!typeInfo ||
265 !((typeInfo->flags & asOBJ_REF) || (typeInfo->flags & asOBJ_TEMPLATE_SUBTYPE) || (typeInfo->flags & asOBJ_ASHANDLE) || (typeInfo->flags & asOBJ_FUNCDEF)) ||
266 (typeInfo->flags & asOBJ_NOHANDLE) ||
267 ((typeInfo->flags & asOBJ_SCOPED) && !acceptHandleForScope)) )
268 return -1;
269
270 isObjectHandle = b;
271 isConstHandle = false;
272
273 // ASHANDLE supports being handle, but as it really is a value type it will not be marked as a handle
274 if( (typeInfo->flags & asOBJ_ASHANDLE) )
275 {
276 isObjectHandle = false;
277 isHandleToAsHandleType = true;
278 }
279 }
280 }
281
282 return 0;
283 }
284
MakeArray(asCScriptEngine * engine,asCModule * module)285 int asCDataType::MakeArray(asCScriptEngine *engine, asCModule *module)
286 {
287 if( engine->defaultArrayObjectType == 0 )
288 return asINVALID_TYPE;
289
290 bool tmpIsReadOnly = isReadOnly;
291 isReadOnly = false;
292 asCArray<asCDataType> subTypes;
293 subTypes.PushLast(*this);
294 asCObjectType *at = engine->GetTemplateInstanceType(engine->defaultArrayObjectType, subTypes, module);
295 isReadOnly = tmpIsReadOnly;
296
297 isObjectHandle = false;
298 isConstHandle = false;
299
300 typeInfo = at;
301 tokenType = ttIdentifier;
302
303 return 0;
304 }
305
MakeReference(bool b)306 int asCDataType::MakeReference(bool b)
307 {
308 isReference = b;
309
310 return 0;
311 }
312
MakeReadOnly(bool b)313 int asCDataType::MakeReadOnly(bool b)
314 {
315 if( isObjectHandle )
316 {
317 isConstHandle = b;
318 return 0;
319 }
320
321 isReadOnly = b;
322 return 0;
323 }
324
MakeHandleToConst(bool b)325 int asCDataType::MakeHandleToConst(bool b)
326 {
327 if( !isObjectHandle ) return -1;
328
329 isReadOnly = b;
330 return 0;
331 }
332
SupportHandles() const333 bool asCDataType::SupportHandles() const
334 {
335 if( typeInfo &&
336 (typeInfo->flags & (asOBJ_REF | asOBJ_ASHANDLE | asOBJ_FUNCDEF)) &&
337 !(typeInfo->flags & asOBJ_NOHANDLE) &&
338 !isObjectHandle )
339 return true;
340
341 return false;
342 }
343
CanBeInstantiated() const344 bool asCDataType::CanBeInstantiated() const
345 {
346 if( GetSizeOnStackDWords() == 0 ) // Void
347 return false;
348
349 if( !IsObject() && !IsFuncdef() ) // Primitives
350 return true;
351
352 if (IsNullHandle()) // null
353 return false;
354
355 if( IsObjectHandle() && !(typeInfo->flags & asOBJ_NOHANDLE) ) // Handles
356 return true;
357
358 // Funcdefs cannot be instantiated without being handles
359 // The exception being delegates, but these can only be created as temporary objects
360 if (IsFuncdef())
361 return false;
362
363 asCObjectType *ot = CastToObjectType(typeInfo);
364 if( ot && (ot->flags & asOBJ_REF) && ot->beh.factories.GetLength() == 0 ) // ref types without factories
365 return false;
366
367 if( ot && (ot->flags & asOBJ_ABSTRACT) && !IsObjectHandle() ) // Can't instantiate abstract classes
368 return false;
369
370 return true;
371 }
372
IsAbstractClass() const373 bool asCDataType::IsAbstractClass() const
374 {
375 return typeInfo && (typeInfo->flags & asOBJ_ABSTRACT) ? true : false;
376 }
377
IsInterface() const378 bool asCDataType::IsInterface() const
379 {
380 if (typeInfo == 0)
381 return false;
382
383 asCObjectType *ot = CastToObjectType(typeInfo);
384 return ot && ot->IsInterface();
385 }
386
CanBeCopied() const387 bool asCDataType::CanBeCopied() const
388 {
389 // All primitives can be copied
390 if( IsPrimitive() ) return true;
391
392 // Plain-old-data structures can always be copied
393 if( typeInfo->flags & asOBJ_POD ) return true;
394
395 // It must be possible to instantiate the type
396 if( !CanBeInstantiated() ) return false;
397
398 // It must have a default constructor or factory and the opAssign
399 // Alternatively it must have the copy constructor
400 asCObjectType *ot = CastToObjectType(typeInfo);
401 if (ot && (((ot->beh.construct != 0 || ot->beh.factory != 0) && ot->beh.copy != 0) ||
402 (ot->beh.copyconstruct != 0 || ot->beh.copyfactory != 0)) )
403 return true;
404
405 return false;
406 }
407
IsReadOnly() const408 bool asCDataType::IsReadOnly() const
409 {
410 if( isObjectHandle )
411 return isConstHandle;
412
413 return isReadOnly;
414 }
415
IsHandleToConst() const416 bool asCDataType::IsHandleToConst() const
417 {
418 if( !isObjectHandle ) return false;
419 return isReadOnly;
420 }
421
IsObjectConst() const422 bool asCDataType::IsObjectConst() const
423 {
424 if( IsObjectHandle() )
425 return IsHandleToConst();
426
427 return IsReadOnly();
428 }
429
430 // TODO: 3.0.0: This should be removed
IsArrayType() const431 bool asCDataType::IsArrayType() const
432 {
433 // This is only true if the type used is the default array type, i.e. the one used for the [] syntax form
434 if( typeInfo && typeInfo->engine->defaultArrayObjectType )
435 return typeInfo->name == typeInfo->engine->defaultArrayObjectType->name;
436
437 return false;
438 }
439
IsTemplate() const440 bool asCDataType::IsTemplate() const
441 {
442 if( typeInfo && (typeInfo->flags & asOBJ_TEMPLATE) )
443 return true;
444
445 return false;
446 }
447
IsScriptObject() const448 bool asCDataType::IsScriptObject() const
449 {
450 if( typeInfo && (typeInfo->flags & asOBJ_SCRIPT_OBJECT) )
451 return true;
452
453 return false;
454 }
455
GetSubType(asUINT subtypeIndex) const456 asCDataType asCDataType::GetSubType(asUINT subtypeIndex) const
457 {
458 asASSERT(typeInfo);
459 asCObjectType *ot = CastToObjectType(typeInfo);
460 return ot->templateSubTypes[subtypeIndex];
461 }
462
463
operator !=(const asCDataType & dt) const464 bool asCDataType::operator !=(const asCDataType &dt) const
465 {
466 return !(*this == dt);
467 }
468
operator ==(const asCDataType & dt) const469 bool asCDataType::operator ==(const asCDataType &dt) const
470 {
471 if( !IsEqualExceptRefAndConst(dt) ) return false;
472 if( isReference != dt.isReference ) return false;
473 if( isReadOnly != dt.isReadOnly ) return false;
474 if( isConstHandle != dt.isConstHandle ) return false;
475
476 return true;
477 }
478
IsEqualExceptRef(const asCDataType & dt) const479 bool asCDataType::IsEqualExceptRef(const asCDataType &dt) const
480 {
481 if( !IsEqualExceptRefAndConst(dt) ) return false;
482 if( isReadOnly != dt.isReadOnly ) return false;
483 if( isConstHandle != dt.isConstHandle ) return false;
484
485 return true;
486 }
487
IsEqualExceptRefAndConst(const asCDataType & dt) const488 bool asCDataType::IsEqualExceptRefAndConst(const asCDataType &dt) const
489 {
490 // Check base type
491 if( tokenType != dt.tokenType ) return false;
492 if( typeInfo != dt.typeInfo ) return false;
493 if( isObjectHandle != dt.isObjectHandle ) return false;
494 if( isObjectHandle )
495 if( isReadOnly != dt.isReadOnly ) return false;
496
497 return true;
498 }
499
IsEqualExceptConst(const asCDataType & dt) const500 bool asCDataType::IsEqualExceptConst(const asCDataType &dt) const
501 {
502 if( !IsEqualExceptRefAndConst(dt) ) return false;
503 if( isReference != dt.isReference ) return false;
504
505 return true;
506 }
507
IsPrimitive() const508 bool asCDataType::IsPrimitive() const
509 {
510 // Enumerations are primitives
511 if( IsEnumType() )
512 return true;
513
514 // A registered object is never a primitive neither is a pointer nor an array
515 if( typeInfo )
516 return false;
517
518 // Null handle doesn't have a typeInfo, but it is not a primitive
519 if( tokenType == ttUnrecognizedToken )
520 return false;
521
522 return true;
523 }
524
IsMathType() const525 bool asCDataType::IsMathType() const
526 {
527 if( tokenType == ttInt || tokenType == ttInt8 || tokenType == ttInt16 || tokenType == ttInt64 ||
528 tokenType == ttUInt || tokenType == ttUInt8 || tokenType == ttUInt16 || tokenType == ttUInt64 ||
529 tokenType == ttFloat || tokenType == ttDouble )
530 return true;
531
532 return false;
533 }
534
IsIntegerType() const535 bool asCDataType::IsIntegerType() const
536 {
537 if( tokenType == ttInt ||
538 tokenType == ttInt8 ||
539 tokenType == ttInt16 ||
540 tokenType == ttInt64 )
541 return true;
542
543 // Enums are also integer types
544 return IsEnumType();
545 }
546
IsUnsignedType() const547 bool asCDataType::IsUnsignedType() const
548 {
549 if( tokenType == ttUInt ||
550 tokenType == ttUInt8 ||
551 tokenType == ttUInt16 ||
552 tokenType == ttUInt64 )
553 return true;
554
555 return false;
556 }
557
IsFloatType() const558 bool asCDataType::IsFloatType() const
559 {
560 if( tokenType == ttFloat )
561 return true;
562
563 return false;
564 }
565
IsDoubleType() const566 bool asCDataType::IsDoubleType() const
567 {
568 if( tokenType == ttDouble )
569 return true;
570
571 return false;
572 }
573
IsBooleanType() const574 bool asCDataType::IsBooleanType() const
575 {
576 if( tokenType == ttBool )
577 return true;
578
579 return false;
580 }
581
IsObject() const582 bool asCDataType::IsObject() const
583 {
584 if( IsPrimitive() )
585 return false;
586
587 // Null handle doesn't have an object type but should still be considered an object
588 if( typeInfo == 0 )
589 return IsNullHandle();
590
591 // Template subtypes shouldn't be considered objects
592 return CastToObjectType(typeInfo) ? true : false;
593 }
594
IsFuncdef() const595 bool asCDataType::IsFuncdef() const
596 {
597 if (typeInfo && (typeInfo->flags & asOBJ_FUNCDEF))
598 return true;
599
600 return false;
601 }
602
GetSizeInMemoryBytes() const603 int asCDataType::GetSizeInMemoryBytes() const
604 {
605 if( typeInfo != 0 )
606 return typeInfo->size;
607
608 if( tokenType == ttVoid )
609 return 0;
610
611 if( tokenType == ttInt8 ||
612 tokenType == ttUInt8 )
613 return 1;
614
615 if( tokenType == ttInt16 ||
616 tokenType == ttUInt16 )
617 return 2;
618
619 if( tokenType == ttDouble ||
620 tokenType == ttInt64 ||
621 tokenType == ttUInt64 )
622 return 8;
623
624 if( tokenType == ttBool )
625 return AS_SIZEOF_BOOL;
626
627 // null handle
628 if( tokenType == ttUnrecognizedToken )
629 return 4*AS_PTR_SIZE;
630
631 return 4;
632 }
633
GetSizeInMemoryDWords() const634 int asCDataType::GetSizeInMemoryDWords() const
635 {
636 int s = GetSizeInMemoryBytes();
637 if( s == 0 ) return 0;
638 if( s <= 4 ) return 1;
639
640 // Pad the size to 4 bytes
641 if( s & 0x3 )
642 s += 4 - (s & 0x3);
643
644 return s/4;
645 }
646
GetSizeOnStackDWords() const647 int asCDataType::GetSizeOnStackDWords() const
648 {
649 // If the type is the variable type then the typeid is stored on the stack too
650 int size = tokenType == ttQuestion ? 1 : 0;
651
652 if( isReference ) return AS_PTR_SIZE + size;
653 if( typeInfo && !IsEnumType() ) return AS_PTR_SIZE + size;
654
655 return GetSizeInMemoryDWords() + size;
656 }
657
658 #ifdef WIP_16BYTE_ALIGN
GetAlignment() const659 int asCDataType::GetAlignment() const
660 {
661 if( typeInfo == NULL )
662 {
663 // TODO: Small primitives should not be aligned to 4 byte boundaries
664 return 4; //Default alignment
665 }
666
667 return typeInfo->alignment;
668 }
669 #endif
670
GetBehaviour() const671 asSTypeBehaviour *asCDataType::GetBehaviour() const
672 {
673 if (!typeInfo) return 0;
674 asCObjectType *ot = CastToObjectType(typeInfo);
675 return ot ? &ot->beh : 0;
676 }
677
IsEnumType() const678 bool asCDataType::IsEnumType() const
679 {
680 // Do a sanity check on the objectType, to verify that we aren't trying to access memory after it has been released
681 asASSERT(typeInfo == 0 || typeInfo->name.GetLength() < 100);
682
683 if (typeInfo && (typeInfo->flags & asOBJ_ENUM))
684 return true;
685
686 return false;
687 }
688
689 END_AS_NAMESPACE
690
691