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