· 6 years ago · Sep 18, 2019, 09:32 AM
1--[[
2 Lplus: Object oriented programming library for Lua
3]]
4
5 Lplus = {}
6
7----------------------------------------
8--
9-- external functions
10--
11----------------------------------------
12
13local rawget = rawget
14local type = type
15local pairs = pairs
16local setmetatable = setmetatable
17local getmetatable = getmetatable
18local tostring = tostring
19local error = error
20local select = select
21local assert = assert
22local require = require
23local pcall = pcall
24local debug = debug
25
26-- debug
27local _G = _G
28--~ local _error = error
29--~ local function error (what)
30--~ return _error(what, 2)
31--~ end
32
33local _ENV = nil -- help to do spelling checking, compatible with lua 5.1
34
35----------------------------------------
36--
37-- Utilities part 1
38--
39----------------------------------------
40
41local function createProxy (metatable)
42 return setmetatable({}, metatable)
43end
44
45local function shallowCopy (destTable, sourceTable)
46 for k, v in pairs(sourceTable) do
47 destTable[k] = v
48 end
49end
50
51local function argError (iArg, funcName, needs, gets, errLevel)
52 error(("bad argument #%d to '%s' (%s expected, got %s)")
53 :format(iArg, funcName, needs, gets), errLevel + 1)
54end
55
56local function argListError (iArg, memberName, needs, gets, errLevel)
57 if gets == nil then
58 error(([[bad argument #%d to argument list of %s (%s)]])
59 :format(iArg, memberName, needs), errLevel + 1)
60 else
61 error(([[bad argument #%d to argument list of %s (%s expected, got %s)]])
62 :format(iArg, memberName, needs, gets), errLevel + 1)
63 end
64end
65
66local function initValueError (fieldName, needs, gets, errLevel)
67 if gets == nil then
68 error(([[bad inital value to field '%s' (%s)]])
69 :format(fieldName, needs), errLevel + 1)
70 else
71 error(([[bad inital value to field '%s' (%s expected, got %s)]])
72 :format(fieldName, needs, gets), errLevel + 1)
73 end
74end
75
76----------------------------------------
77--
78-- Lplus config
79--
80----------------------------------------
81
82local config =
83{
84 --default config
85 reflection = false,
86 declare_checking = true,
87 accessing_checking = true,
88 calling_checking = true,
89 reload = false,
90}
91
92--
93-- load config
94--
95local loadedConfig
96do
97 -- load from module Lplus_config
98 local currentModule = ...
99 if currentModule ~= nil then
100 local configModule = currentModule .. "_config"
101 local bRet, result = pcall(require, configModule)
102 if bRet then
103 if type(result) ~= "table" then
104 error("module Lplus_config should return a table, got: " .. type(result))
105 end
106 loadedConfig = result
107 end
108 end
109end
110
111if loadedConfig ~= nil then
112 shallowCopy(config, loadedConfig)
113end
114
115--
116-- feature config
117--
118
119local feature_reflection = config.reflection
120local feature_declare_checking = config.declare_checking
121local feature_accessing_checking = config.accessing_checking
122local feature_calling_checking = config.calling_checking
123local feature_reload = config.reload
124
125--
126-- derived feature config
127--
128
129local feature_member_info = feature_declare_checking or feature_accessing_checking
130
131
132----------------------------------------
133--
134-- Lplus global variables
135--
136----------------------------------------
137
138-- set flag that Lplus library is initializing
139local bInitializingLplus = true
140
141-- to determine value is Lplus object
142local lplusObjectMagic = {}
143
144-- to determine value is Lplus type table
145local lplusTypeTableMagic = {}
146
147-- to control function wrapper
148local lplusFunctionWrapperControlMagic = {}
149
150-- mapping type name to type table
151local typeNameToTableMap = {}
152
153----------------------------------------
154--
155-- Utilities part 2
156--
157----------------------------------------
158
159local basicTypeSet =
160{
161 ["nil"] = true,
162 ["number"] = true,
163 ["string"] = true,
164 ["boolean"] = true,
165 ["table"] = true,
166 ["function"] = true,
167 ["thread"] = true,
168 ["userdata"] = true,
169
170 ["dynamic"] = true, --any type (lua default style)
171}
172
173local primaryTypeSet =
174{
175 ["nil"] = true,
176 ["number"] = true,
177 ["string"] = true,
178 ["boolean"] = true,
179}
180
181local nonNilPrimaryTypeSet =
182{
183 ["number"] = true,
184 ["string"] = true,
185 ["boolean"] = true,
186}
187
188local function getTypeTableMeta (typeTable)
189 if type(typeTable) ~= "table" then
190 return nil
191 end
192 print(typeTable)
193 local meta = getmetatable(typeTable)
194 print(meta)
195 --check magic
196 if meta == nil or meta.magic ~= lplusTypeTableMagic then
197 return nil
198 end
199
200 return meta
201end
202
203local getTypeTableMetaNoCheck = getmetatable
204
205local function getObjectMeta (obj)
206 if type(obj) ~= "table" then
207 return nil
208 end
209
210 --check magic
211 local meta = getmetatable(obj)
212 if meta == nil or meta.magic ~= lplusObjectMagic then
213 return nil
214 end
215
216 return meta
217end
218
219local getObjectMetaNoCheck = getmetatable
220
221local function getObjectTypeTable (obj)
222 return getObjectMetaNoCheck(obj).typeTable
223end
224
225local function getObjectTypeMeta (obj)
226 return getTypeTableMetaNoCheck(getObjectTypeTable(obj))
227end
228
229
230local function checkTypeTable (typeTable, iArg, who, what, errLevel)
231 if getTypeTableMeta(typeTable) == nil then
232 argError(iArg, funcName, "type table", type(typeTable), errLevel+1)
233 end
234end
235
236local function formatTypeName (typeValue)
237 if typeValue == nil then
238 return "<none>"
239 elseif type(typeValue) == "string" then
240 return '"' .. typeValue .. '"'
241 else
242 return tostring(typeValue)
243 end
244end
245
246local function formatObjectTypeName (object)
247 if Lplus.is(object, Lplus.Object) then
248 return tostring(object:getTypeTable())
249 else
250 return '"' .. type(object) .. '"'
251 end
252end
253
254local function checkValidArgType (typeValue, iParam, memberName, errLevel)
255 if type(typeValue) == "string" then
256 if not basicTypeSet[typeValue] then
257 argListError(iParam, memberName, "vaild basic type name", '"'..typeValue..'"', errLevel+1)
258 end
259 else
260 if getTypeTableMeta(typeValue) == nil then
261 argListError(iParam, memberName, "type table", type(typeValue), errLevel+1)
262 end
263 end
264end
265
266local function isTypeCompatible (value, needType)
267 if needType == "dynamic" then
268 return true
269 elseif type(needType) == "string" then
270 if nonNilPrimaryTypeSet[needType] then
271 return type(value) == needType
272 else
273 -- nilable type is compatible with nil
274 return value == nil or type(value) == needType
275 end
276 else
277 return value == nil or Lplus.is(value, needType)
278 end
279end
280
281
282local function checkValueCompatible (value, needType, format, who, errorLevel)
283 if not isTypeCompatible(value, needType) then
284 local what = format:format(who)
285 error(([[bad %s (%s expected, got %s)]])
286 :format(what, formatTypeName(needType), formatObjectTypeName(value)), errorLevel + 1)
287 end
288end
289
290local function isTypeTableInterface (typeTable)
291 return getTypeTableMetaNoCheck(typeTable).isInterface
292end
293
294----------------------------------------
295--
296-- type creation
297--
298----------------------------------------
299
300local function inheritMemberInfo (derivedMemberInfo, baseMemberInfo)
301 for name, info in pairs(baseMemberInfo) do
302 if info.inheritable then
303 derivedMemberInfo[name] = info
304 end
305 end
306end
307
308-- copy base members to derived type meta
309local function inheritMembers (derivedMeta, baseTypeTable)
310 -- inherit from baseTypeTable
311 local baseMeta = getTypeTableMetaNoCheck(baseTypeTable)
312
313 shallowCopy(derivedMeta.fields, baseMeta.fields)
314 shallowCopy(derivedMeta.methods, baseMeta.methods)
315 shallowCopy(derivedMeta.abstractMethods, baseMeta.abstractMethods)
316 shallowCopy(derivedMeta.staticMembers, baseMeta.staticMembers)
317 shallowCopy(derivedMeta.compatibleTypes, baseMeta.compatibleTypes)
318
319 if feature_member_info then
320 inheritMemberInfo(derivedMeta.memberInfoMap, baseMeta.memberInfoMap)
321 end
322end
323
324-- param paramList: { param1, param2, ..., "=>", ret1, ret2, ... }
325-- return: { [n] = paramN, [-n] = retN }
326local function checkMethodParamAndMakeList (paramCount, paramList, methodName, errorLevel)
327 local result = {}
328
329 local bReturnPart = false
330 local iSeperator = -1
331 local bGotParamValist = false
332 local bGotReturnValist = false
333
334 for i = 1, paramCount do
335 local inParam = paramList[i]
336 if inParam == "=>" then
337 bReturnPart = true
338 iSeperator = i
339 elseif inParam == "varlist" then
340 if bReturnPart then
341 bGotReturnValist = true
342 result[-(i-iSeperator)] = inParam -- last return value type
343 else
344 bGotParamValist = true
345 result[i] = inParam -- last param value type
346 end
347 else
348 checkValidArgType(inParam, i, methodName, errorLevel+1)
349 if bReturnPart then
350 if bGotReturnValist then
351 argListError(i, methodName, '"varlist" must be the last return value type', nil, errorLevel + 1)
352 end
353 result[-(i-iSeperator)] = inParam -- a return value type
354 else
355 if bGotParamValist then
356 argListError(i, methodName, '"varlist" must be the last param value type', nil, errorLevel + 1)
357 end
358 result[i] = inParam -- a param value type
359 end
360 end
361 end
362
363 return result
364end
365
366-- return diff index, or nil if equal
367local function compareSignature (signatureLeft, signatureRight)
368 local iParam = 1
369 repeat
370 local paramLeft = signatureLeft[iParam]
371 local paramRight = signatureRight[iParam]
372
373 if paramLeft ~= paramRight then
374 return iParam
375 end
376
377 iParam = iParam + 1
378 until paramLeft == nil
379
380 local iReturn = 1
381 repeat
382 local returnLeft = signatureLeft[-iReturn]
383 local returnRight = signatureRight[-iReturn]
384
385 if returnLeft ~= returnRight then
386 return -iReturn
387 end
388
389 iReturn = iReturn + 1
390 until returnLeft == nil
391
392 return nil
393end
394
395local function checkMethodOverrideSignature (signatureBase, signatureOverride, methodType, methodName, errLevel)
396 local iDiff = compareSignature(signatureBase, signatureOverride)
397 if iDiff ~= nil then
398 local what
399 local iWhat
400 if iDiff > 0 then
401 what = "param"
402 iWhat = iDiff
403 else
404 what = "return value"
405 iWhat = -iDiff
406 end
407
408 error(([[bad %s type #%d to %s '%s' (%s expected, got %s)]])
409 :format(what, iWhat, methodType, methodName,
410 formatTypeName(signatureBase[iDiff]),
411 formatTypeName(signatureOverride[iDiff])),
412 errLevel+1)
413 end
414end
415
416local forwardDeclaredType = {}
417
418local function createForwardDeclareType (typeName, errorLevel)
419 if typeName ~= nil then
420 if type(typeName) ~= "string" then
421 error("invalid type name (string expected, got " .. type(typeName) .. ")", errorLevel+1)
422 end
423
424 local typeTable = typeNameToTableMap[typeName]
425 if typeTable ~= nil then
426 return typeTable
427 end
428 end
429
430 local typeTable = forwardDeclaredType[typeName]
431 if typeTable ~= nil then
432 return typeTable
433 end
434
435
436 local typeString = (typeName or "anonymousType") .. "(forward declare)"
437 local typeMeta =
438 {
439 magic = lplusTypeTableMagic;
440 isForwardDeclared = true;
441 typeName = typeName;
442 __tostring = function (_)
443 return typeString
444 end;
445 }
446
447 typeTable = {}
448 setmetatable(typeTable, typeMeta)
449
450 if typeName ~= nil then
451 forwardDeclaredType[typeName] = typeTable
452 end
453
454 return typeTable
455end
456
457local function isTypeTableForwardDeclared (typeTable)
458 return getTypeTableMetaNoCheck(typeTable).isForwardDeclared
459end
460
461-- if forwardDeclare is given, it's name should equal to typeName
462local function createEmptyType (typeName, forwardDeclare, errorLevel)
463 local resultTable
464 if forwardDeclare ~= nil then
465 resultTable = forwardDeclare
466 else
467 if typeName == nil then
468 resultTable = {}
469 else
470 resultTable = forwardDeclaredType[typeName] or {}
471 end
472 end
473
474 if typeName ~= nil then
475 forwardDeclaredType[typeName] = nil -- fetch the forward declared type table
476 end
477
478 return setmetatable(resultTable, nil)
479end
480
481-- check ..., return ...
482-- needTypes.selfType = type of self when not static
483-- needTypes.format: format(who), needTypes.format: format(i, who)
484local function checkValuesCompatible (needTypes, errLevel, ...)
485 local nGets = select("#", ...)
486 local nNeeds = #needTypes
487 local bVarlist = needTypes.bVarlist
488
489 if nGets < nNeeds then
490 local what = needTypes.format:format(needTypes.who)
491 error(([[too little %s (%d expected, got %d)]])
492 :format(what, nNeeds, nGets), errLevel+1)
493 end
494
495 if not bVarlist and nGets > nNeeds then
496 local what = needTypes.format:format(needTypes.who)
497 error(([[too many %s (%d expected, got %d)]])
498 :format(what, nNeeds, nGets), errLevel+1)
499 end
500
501 local selfType = needTypes.selfType
502 if selfType ~= nil then
503 local self = ...
504 if self == nil then
505 local what = needTypes.ithFormat:format(1, needTypes.who)
506 error(([[bad %s (%s expected, got %s)]])
507 :format(what, formatTypeName(selfType), formatObjectTypeName(self)), errLevel + 1)
508 end
509 end
510 for i = 1, nNeeds do
511 local getValue = select(i, ...)
512 local needType = needTypes[i]
513
514 if needType == "varlist" then --"varlist" matches anything
515 return
516 end
517
518 if not isTypeCompatible(getValue, needType) then
519 local what = needTypes.ithFormat:format(i, needTypes.who)
520 error(([[bad %s (%s expected, got %s)]])
521 :format(what, formatTypeName(needType), formatObjectTypeName(getValue)), errLevel + 1)
522 end
523 end
524
525 return ...
526end
527
528local function createEmptyFunctionWrapper ()
529 local func
530 local methodName
531 local tableType
532 local bCheckType
533
534 local arguments
535 local returns
536
537 return function (...)
538 local magic, op = ...
539 if magic == lplusFunctionWrapperControlMagic then -- control command
540 if op == "set" then
541 local _, methodInfo
542 _, _, func, methodName, tableType, methodInfo, bCheckType = ...
543
544 local bInstanceMethod =
545 methodInfo.style:find("s", 1, true) == nil
546 and methodInfo.style:find("f", 1, true) == nil
547
548 local needTypes = methodInfo.valueType
549 arguments =
550 {
551 format="arguments to '%s' in " .. tostring(tableType),
552 ithFormat="argument #%d to '%s' in " .. tostring(tableType),
553 who = methodName,
554 }
555 returns =
556 {
557 format="return values from '%s' in " .. tostring(tableType),
558 ithFormat="return value #%d from '%s' in " .. tostring(tableType),
559 who = methodName,
560 }
561 do
562 if bInstanceMethod then
563 arguments[#arguments + 1] = tableType
564 arguments.selfType = tableType
565 end
566 local iArg = 1
567 while needTypes[iArg] do
568 local needType = needTypes[iArg]
569 if needType == "varlist" then
570 arguments.bVarlist = true
571 else
572 arguments[#arguments + 1] = needType
573 end
574 iArg = iArg + 1
575 end
576 end
577 do
578 local iRet = 1
579 while needTypes[-iRet] do
580 local needType = needTypes[-iRet]
581 if needType == "varlist" then
582 returns.bVarlist = true
583 else
584 returns[iRet] = needTypes[-iRet]
585 end
586 iRet = iRet + 1
587 end
588 end
589 end
590
591 return
592 end
593
594 -- function call
595 if bCheckType then
596 return checkValuesCompatible(returns, 1, func(checkValuesCompatible(arguments, 2, ...)))
597 else
598 return func(...)
599 end
600 end
601end
602
603local function setFunctionWrapper (wrapper, func, methodName, tableType, methodInfo, bCheckType)
604 wrapper(lplusFunctionWrapperControlMagic, "set", func, methodName, tableType, methodInfo, bCheckType)
605end
606
607local function createFunctionWrapper (func, methodName, tableType, methodInfo, bCheckType)
608 local wrapper = createEmptyFunctionWrapper()
609 setFunctionWrapper(wrapper, func, methodName, tableType, methodInfo, bCheckType)
610
611 return wrapper
612end
613
614-- create type with given base type and type name
615-- base type could be nil, which means bCreatingClass Object
616local function createType (baseTypeTable, typeNameOrForwardDeclare, isInterface)
617 --
618 -- checking parameters
619 --
620
621 local theForwardDeclare
622 local typeName
623
624 if type(typeNameOrForwardDeclare) == "table" then
625 theForwardDeclare = typeNameOrForwardDeclare
626
627 local forwardDeclareMeta = getTypeTableMeta(theForwardDeclare)
628
629 if forwardDeclareMeta == nil then -- not type table
630 error("invalid forward declare (type table expected, got " .. type(typeName) .. ")", 2)
631 end
632 if not forwardDeclareMeta.isForwardDeclared then
633 error("invalid forward declare (type is already defined)", 2)
634 end
635 if forwardDeclareMeta.typeName ~= nil then
636 error('invalid forward declare (anonymous expected, got name "' .. forwardDeclareMeta.typeName .. '")', 2)
637 end
638 else
639 typeName = typeNameOrForwardDeclare
640
641 --check typeName
642 if typeName ~= nil then
643 if type(typeName) ~= "string" then
644 error("invalid type name (string expected, got " .. type(typeName) .. ")", 2)
645 end
646 if typeNameToTableMap[typeName] ~= nil then
647 error('type with name "' .. typeName .. '" already exist', 2)
648 end
649 end
650 end
651
652 --checking base type
653 if baseTypeTable ~= nil then
654 local baseMeta = getTypeTableMeta(baseTypeTable)
655 if baseMeta == nil then
656 if not bInitializingLplus then --when Type extends Object, baseMeta is nil
657 error ("Param 1 expect a Lplus type table, got: " .. type(baseTypeTable), 2)
658 end
659 else
660 if baseMeta.isForwardDeclared then
661 error("forward declared type " .. tostring(baseTypeTable) .. " can not be inherited", 2)
662 end
663
664 local isBaseInterface = baseMeta.isInterface
665 if isBaseInterface then -- both explicit interface declaration and extending an interface make the type an interface
666 isInterface = true
667 end
668
669 if baseTypeTable ~= Lplus.Object and isBaseInterface ~= isInterface then
670 if isInterface then
671 error("interface can not inherit from non-interface type", 2)
672 else
673 error("non-interface can not inherit from interface type", 2)
674 end
675 end
676 end
677 end
678
679 -- set flag that the creation of type has not finished
680 local bCreatingClass = true
681
682 -- the created type
683 local theType = createEmptyType(typeName, theForwardDeclare, 2)
684
685 -- metatable of the type
686 local typeMeta = {}
687
688 --
689 -- setup typeMeta
690 --
691 do
692 typeMeta.magic = lplusTypeTableMagic
693
694 --
695 -- members
696 --
697
698 -- fields
699 typeMeta.fields = {}
700 -- non-static methods
701 typeMeta.abstractMethods = {}
702 -- abstract non-static methods
703 typeMeta.methods = {}
704 -- static methods, static and constant fields
705 typeMeta.staticMembers = {}
706
707 -- members info map (for checking)
708 if feature_member_info then
709 typeMeta.memberInfoMap = {}
710 end
711
712 --
713 -- extra information
714 --
715
716 typeMeta.isInterface = isInterface
717 typeMeta.baseTypeTable = baseTypeTable
718 typeMeta.typeName = typeName
719
720 local typeString = (typeName or "anonymousType") .. "(" .. tostring(theType) .. ")"
721 typeMeta.__tostring = function (_)
722 return typeString
723 end
724
725 -- build type info (for convertion)
726 typeMeta.typeInfo = nil --will be created when call obj:getType()
727 typeMeta.compatibleTypes = { [theType] = true } -- base type will be add by inheritMembers()
728 typeMeta.implementInterfaces = {} -- only directly implemented interfaces
729 end
730
731 -- inherit from baseTypeTable
732 if baseTypeTable ~= nil then
733 inheritMembers(typeMeta, baseTypeTable)
734 end
735
736 --
737 -- Add interface
738 --
739
740 --[[
741 Special operator Implement
742 declare that this type implements an interface
743 param interfaceTypeTable: type table of interface
744 return type table itself
745 e.g: MyClass.Implement(IEquatable).Implement(IComparable)
746 ]]
747 function theType.Implement (interfaceTypeTable)
748 typeMeta.compatibleTypes[interfaceTypeTable] = true
749 typeMeta.implementInterfaces[interfaceTypeTable] = true
750 return theType
751 end
752
753 --
754 -- member declare
755 --
756
757 -- style is a string, each char stands for a flag
758 -- 'c': constant
759 local function FieldInternal (style, fieldType)
760 if isInterface then
761 error("interface can not have field", 2)
762 end
763 if not bCreatingClass then
764 error("bad field defining (type " .. tostring(theType) .. " is already committed)", 2)
765 end
766
767 local bConstant = (style == "c")
768
769 return createProxy
770 {
771 __index = function (_, fieldName)
772 error("You need to give field a default value", 2)
773 end;
774
775 __newindex = function (_, fieldName, initValue)
776 if type(fieldName) ~= "string" then
777 error("Field name should be string, got: "..type(fieldName), 2)
778 end
779
780 local memberInfo
781 if feature_member_info then
782 local memberInfoMap = typeMeta.memberInfoMap
783
784 -- checking name conflict
785 if memberInfoMap[fieldName] ~= nil then
786 error('member with name "' .. fieldName .. '" already exists', 2)
787 end
788
789 checkValidArgType(fieldType, 1, fieldName, 2)
790 -- record member info
791 memberInfo =
792 {
793 memberType = "field",
794 style = style,
795 typeTable = theType,
796 inheritable = true,
797 valueType = fieldType
798 }
799 end
800
801 if bConstant then
802 checkValueCompatible(initValue, fieldType, [[initial value to field '%s']], fieldName, 2)
803 typeMeta.staticMembers[fieldName] = initValue
804 else
805 local initValueType = type(initValue)
806 if not primaryTypeSet[initValueType] and initValueType ~= "function" then
807 initValueError(fieldName, "number, boolean, string, nil or function", initValueType, 2)
808 end
809
810 if type(initValue) ~= "function" then
811 checkValueCompatible(initValue, fieldType, [[initial value to field '%s']], fieldName, 2)
812 end
813 typeMeta.fields[fieldName] = initValue --... checking whether table, userdata
814 end
815
816 -- register member info only when succeeded
817 if feature_member_info then
818 local memberInfoMap = typeMeta.memberInfoMap
819 memberInfoMap[fieldName] = memberInfo
820 end
821 end;
822 }
823 end
824
825 --[[
826 Special operator Field
827 Declare a field
828 Initial value of the field can only be number, boolean, string, nil or function
829 If initial value is a function, it will be called when instantiate object. The return value will be assigned to field
830 param fieldType: type of field
831 e.g:
832 Field("number").m_fieldName_1 = 1
833 Field("table").m_fieldName_2 = function () return {"new table"} end
834 ]]
835 local function Field (fieldType)
836 return FieldInternal("", fieldType)
837 end
838
839 --[[
840 Special operator ConstField
841 declare a static field
842 param fieldType: type of field
843 e.g: Field("number").m_fieldName = 1
844 ]]
845 local function ConstField (fieldType)
846 return FieldInternal("c", fieldType)
847 end
848
849 -- style is a string, each char stands for a flag
850 -- 's': static, 'v': virtual, 'o': override, 'f': final
851 local function MethodInternal (style, ...)
852 if not bCreatingClass then
853 error("bad method defining (type " .. tostring(theType) .. " is already committed)", 2)
854 end
855
856 assert(#style <= 1)
857
858 local bStatic = (style == "s")
859 local bVirtual = (style == "v")
860 local bOverride = (style == "o")
861 local bFinal = (style == "f")
862
863 local paramCount
864 local paramList
865 if feature_member_info then
866 paramCount = select("#", ...)
867 paramList = {...}
868 end
869
870 return createProxy
871 {
872 __index = function (_, methodName)
873 error("You need to give method a function body", 2)
874 end;
875
876 __newindex = function (_, methodName, functionBody)
877 if type(methodName) ~= "string" then
878 error("Method name should be string, got: "..type(methodName), 2)
879 end
880 if type(functionBody) ~= "function" then
881 error("Need function body, got: "..type(functionBody), 2)
882 end
883
884 if feature_member_info then
885 local memberInfoMap = typeMeta.memberInfoMap
886
887 local baseMemberInfo
888
889 -- checking name conflict
890 if memberInfoMap[methodName] ~= nil then
891 local oldInfo = memberInfoMap[methodName]
892 if bOverride and oldInfo.memberType == "method" --only "override" method can override others
893 and oldInfo.typeTable ~= theType then --can only override once in one type
894 if oldInfo.style == "v" or oldInfo.style == "o" then
895 baseMemberInfo = oldInfo -- will check signature later
896 else
897 error('Can not override non-virtual method "' .. methodName .. '"', 2)
898 end
899 else
900 error('member with name "' .. methodName .. '" already exists', 2)
901 end
902 end
903
904 -- checking override
905 if bOverride and baseMemberInfo == nil then
906 error('overrided method with name "' .. methodName .. '" not exists', 2)
907 end
908
909 -- record member info
910 local memberInfo =
911 {
912 memberType = "method",
913 style = style,
914 typeTable = theType,
915 inheritable = not bFinal,
916 valueType = checkMethodParamAndMakeList(paramCount, paramList, methodName, 2)
917 }
918
919 if baseMemberInfo ~= nil then
920 checkMethodOverrideSignature(baseMemberInfo.valueType, memberInfo.valueType, "override method", methodName, 2)
921 end
922
923 memberInfoMap[methodName] = memberInfo
924
925 if feature_calling_checking then
926 functionBody = createFunctionWrapper(functionBody, methodName, theType, memberInfo, true)
927 end
928 end
929
930 if bStatic or bFinal then
931 if isInterface then
932 error("Interface can not have static method", 2)
933 end
934
935 typeMeta.staticMembers[methodName] = functionBody
936 else
937 if isInterface then
938 typeMeta.abstractMethods[methodName] = functionBody
939 else
940 typeMeta.methods[methodName] = functionBody
941 end
942 end
943 end;
944 }
945 end
946
947
948 --[[
949 Special operation Method
950 declare a method
951 params: params types and return value types, params types and return types are seperated by "=>"
952 e.g:
953 -- declare method with one number param and no return value
954 Method("number").foo = function (self, numberParam) end
955 -- declare method with one number param, one string param, one boolean return value, and one string return value
956 Method("number", "string", "=>", "boolean", "string").foo = function (self, numberParam) end
957 ]]
958 local function Method (...)
959 return MethodInternal("", ...)
960 end
961
962 --[[
963 Special operation VirtualMethod
964 declare a virtual method
965 a virtual method is similar to normal method, but can be overrided
966 ]]
967 local function VirtualMethod (...)
968 return MethodInternal("v", ...)
969 end
970
971 --[[
972 Special operation OverrideMethod
973 declare a override method
974 Virtual method of base type can be overrided
975 ]]
976 local function OverrideMethod (...)
977 return MethodInternal("o", ...)
978 end
979
980 --[[
981 Special operation StaticMethod
982 declare a static method
983 params: params types and return value types, params types and return types are seperated by "=>"
984 e.g:
985 -- declare method with one number param and no return value
986 StaticMethod("number").foo = function (numberParam) end
987 -- declare method with one number param, one string param, one boolean return value, and one string return value
988 StaticMethod("number", "string", "=>", "boolean", "string").foo = function (numberParam) end
989 ]]
990 local function StaticMethod (...)
991 return MethodInternal("s", ...)
992 end
993
994 --[[
995 Special operation FinalMethod
996 declare a final method. Final method is static, and is not inheritable
997 params: params types and return value types, params types and return types are seperated by "=>"
998 e.g:
999 -- declare method with one number param and no return value
1000 StaticMethod("number").foo = function (numberParam) end
1001 -- declare method with one number param, one string param, one boolean return value, and one string return value
1002 StaticMethod("number", "string", "=>", "boolean", "string").foo = function (numberParam) end
1003 ]]
1004 local function FinalMethod (...)
1005 return MethodInternal("f", ...)
1006 end
1007
1008 local define =
1009 {
1010 field = Field,
1011 const = ConstField,
1012 method = Method,
1013 virtual = VirtualMethod,
1014 override = OverrideMethod,
1015 static = StaticMethod,
1016 final = FinalMethod,
1017 }
1018
1019 typeMeta.define = define
1020
1021 --[[
1022 Special operation define
1023 get member declaring operators
1024 e.g:
1025 Class.define.field("number").m_fieldName = 1
1026 ]]
1027 theType.define = define
1028
1029 --
1030 -- finish class creation
1031 --
1032
1033 local function commit ()
1034 bCreatingClass = false
1035
1036 --
1037 -- remove special operators that are only used in class creation
1038 --
1039 do
1040 theType.Implement = nil
1041 theType.define = nil
1042 theType.Commit = nil
1043 end
1044
1045 --
1046 -- check whether interfaces are properly implemented
1047 --
1048
1049 if not isInterface and feature_declare_checking then
1050 local selfMembers = typeMeta.memberInfoMap
1051
1052 for interface, _ in pairs(typeMeta.implementInterfaces) do
1053 local interfaceMeta = getTypeTableMetaNoCheck(interface)
1054 for methodName, methodInfo in pairs(interfaceMeta.memberInfoMap) do
1055 assert(methodInfo.memberType == "method")
1056
1057 local selfMemberInfo = selfMembers[methodName]
1058 if selfMemberInfo == nil or selfMemberInfo.memberType ~= "method" then
1059 error(("method '%s' in interface '%s' not implemented"):format(methodName, tostring(interface)), 2)
1060 end
1061
1062 checkMethodOverrideSignature(methodInfo.valueType, selfMemberInfo.valueType, "method implemetation", methodName, 2)
1063 end
1064 end
1065 end
1066
1067 --
1068 -- add members (including instance members and static members)
1069 --
1070
1071 if feature_accessing_checking then
1072 local memberInfoMap = typeMeta.memberInfoMap
1073
1074 -- return member info
1075 local function checkMemberInfo (memberName, bAccessInstanceMember, errorLevel)
1076 local memberInfo = memberInfoMap[memberName]
1077 if memberInfo == nil then
1078 error('member "' .. memberName .. '" not found in ' .. tostring(theType), errorLevel+1)
1079 end
1080
1081 local style = memberInfo.style
1082 local bIsInstanceMember = (style ~= "s" and style ~= "c" and style ~= "f")
1083
1084 if bAccessInstanceMember then
1085 if not bIsInstanceMember then
1086 error('can not access non-instance member "' .. memberName .. '" in ' .. tostring(theType), errorLevel+1)
1087 end
1088 else
1089 if bIsInstanceMember and memberInfo.memberType ~= "method" then
1090 error('can not access instance member "' .. memberName .. '" in ' .. tostring(theType), errorLevel+1)
1091 end
1092 end
1093 return memberInfo
1094 end
1095
1096 local staticMembers = typeMeta.staticMembers
1097 local methods = typeMeta.methods
1098
1099 function typeMeta.__index (_, memberName)
1100 local memberInfo = checkMemberInfo(memberName, false, 2)
1101 -- value in methods can only be function
1102 return methods[memberName] or staticMembers[memberName]
1103 end
1104
1105 function typeMeta.__newindex (_, memberName, newValue)
1106 error('can not assign to static member "' .. memberName .. '"', 2)
1107 end
1108
1109 function typeMeta.__tryget (_, memberName)
1110 local method = methods[memberName]
1111 if method ~= nil then
1112 return method
1113 else
1114 return staticMembers[memberName]
1115 end
1116 end
1117
1118 -- add constructor
1119 -- note: interface does not have constructor
1120 function typeMeta.__call (_, ...)
1121 if isInterface then
1122 error("interface " .. tostring(theType) .. " can not instantiate", 2)
1123 end
1124
1125 if select("#", ...) ~= 0 then
1126 error(([[bad argument to construct of %s (no argument expected, got %d)]])
1127 :format(tostring(theType), select("#", ...)), 2)
1128 end
1129
1130 -- metatable of class instances
1131 local objMeta =
1132 {
1133 -- for getTypeTable() in Object
1134 typeTable = theType,
1135 magic = lplusObjectMagic,
1136 }
1137
1138 local memberInfoMap = typeMeta.memberInfoMap
1139
1140 local objMembers = setmetatable({}, {__index = typeMeta.methods})
1141
1142 function objMeta.__index (_, memberName)
1143 checkMemberInfo(memberName, true, 2)
1144 return objMembers[memberName]
1145 end
1146
1147 function objMeta.__tostring (t)
1148 return t:toString()
1149 end
1150
1151 local function assignInstanceMember (memberName, newValue, errorLevel)
1152 local memberInfo = checkMemberInfo(memberName, true, errorLevel+1)
1153 if memberInfo.memberType ~= "field" then
1154 error('can not assign to non-field member "' .. memberName .. '"', errorLevel+1)
1155 end
1156
1157 if memberInfo.style:find("c", 1, true) ~= nil then
1158 error('can not assign to constant field "' .. memberName .. '"', errorLevel+1)
1159 end
1160
1161 checkValueCompatible(newValue, memberInfo.valueType, [[assginment to field '%s']], memberName, errorLevel+1)
1162
1163 objMembers[memberName] = newValue
1164 end
1165
1166 local function initInstanceMembers (errorLevel)
1167 for name, initValue in pairs(typeMeta.fields) do
1168 local realInitValue = initValue
1169 -- if init value is a function should call it
1170 if type(initValue) == "function" then
1171 local bSucc, ret = pcall(initValue)
1172 if not bSucc then
1173 error('failed to get initial value for member "' .. name .. '" :\n ' .. ret)
1174 end
1175 initValue = ret
1176 end
1177
1178 assignInstanceMember(name, initValue, errorLevel + 1)
1179 end
1180 end
1181
1182 function objMeta.__newindex (_, memberName, newValue)
1183 assignInstanceMember(memberName, newValue, 2)
1184 end
1185
1186 function objMeta.__tryget (_, memberName)
1187 local memberInfo = memberInfoMap[memberName]
1188 if memberInfo then
1189 return objMembers[memberName]
1190 else
1191 return nil
1192 end
1193 end
1194
1195 local obj = {}
1196 setmetatable(obj, objMeta)
1197 initInstanceMembers(2)
1198 return obj
1199 end
1200 else
1201 local function initInstanceMembers (obj, fields)
1202 local type = type
1203 for name, initValue in pairs(fields) do
1204 -- if init value is a function should call it
1205 if type(initValue) == "function" then
1206 obj[name] = initValue()
1207 else
1208 obj[name] = initValue
1209 end
1210 end
1211 end
1212
1213 -- copy typeMeta.staticMembers to type table
1214 shallowCopy(theType, typeMeta.staticMembers)
1215 shallowCopy(theType, typeMeta.methods)
1216
1217 -- metatable of class instances
1218 local objMeta =
1219 {
1220 -- for getTypeTable() in Object
1221 typeTable = theType,
1222 magic = lplusObjectMagic,
1223 __index = typeMeta.methods
1224 }
1225
1226 -- add constructor
1227 function typeMeta.__call(_, ...)
1228 local obj = {}
1229 initInstanceMembers(obj, typeMeta.fields)
1230 return setmetatable(obj, objMeta)
1231 end
1232 end
1233
1234 -- metatable content changed, refresh it
1235 return setmetatable(theType, typeMeta)
1236 end
1237 typeMeta.commit = commit
1238
1239 --[[
1240 Special operation Commit
1241 finish class creation, new members can not be added any more after commit
1242 ]]
1243 theType.Commit = commit
1244
1245 --
1246 -- register type
1247 --
1248 if typeName ~= nil then
1249 typeNameToTableMap[typeName] = theType
1250 end
1251
1252 return setmetatable(theType, typeMeta)
1253end
1254
1255
1256-- utility function to get type info from type table
1257local function typeTableToTypeInfo (typeTable)
1258 local typeMeta = getTypeTableMetaNoCheck(typeTable)
1259 local typeInfo = typeMeta.typeInfo
1260 if typeInfo == nil then
1261 -- create type info if absent
1262 typeInfo = Lplus.Type.fromTypeTable(typeTable)
1263 typeMeta.typeInfo = typeInfo
1264 end
1265 return typeInfo
1266end
1267
1268local function isCompatibleTypeTable (obj, typeTable)
1269 return getObjectTypeMeta(obj).compatibleTypes[typeTable] ~= nil
1270end
1271
1272--[[
1273 Forward declare a class
1274 param typeName: type name of the class
1275 return an empty class, which can be used as parameter value type
1276]]
1277function Lplus.ForwardDeclare (typeName)
1278 return createForwardDeclareType(typeName, 1)
1279end
1280
1281--
1282-- Create the ultimate base type of all classes: Object, and the type of type info: Type
1283--
1284
1285do
1286 local Type = Lplus.ForwardDeclare("System.Type")
1287
1288 local function getObjectTypeTable (obj)
1289 return getObjectMetaNoCheck(obj).typeTable
1290 end
1291
1292 local function getObjectTypeMeta (obj)
1293 return getTypeTableMetaNoCheck(getObjectTypeTable(obj))
1294 end
1295
1296 local Object = createType(nil, "System.Object", false)
1297 local def = Object.define
1298
1299 --[[
1300 get type of this object
1301 The result value can be used as base class, but it is not a Lplus object
1302 To get Lplus object that represent the type, use getType
1303 ]]
1304 def.method("=>", "table").getTypeTable = function (self)
1305 return getObjectTypeTable(self)
1306 end
1307
1308 --[[
1309 get type info of this object
1310 ]]
1311 def.method("=>", Type).getType = function (self)
1312 local typeTable = getObjectTypeTable(self)
1313 return typeTableToTypeInfo(typeTable)
1314 end
1315
1316 def.method("table", "=>", "boolean").is = function (self, typeTable)
1317 return isCompatibleTypeTable(self, typeTable)
1318 end
1319
1320 def.method("table", "=>", Object).as = function (self, typeTable)
1321 if self:is(typeTable) then
1322 return self
1323 else
1324 return nil
1325 end
1326 end
1327
1328 def.method("table", "=>", Object).cast = function (self, theType)
1329 if self:is(theType) then
1330 return self
1331 else
1332 error("bad cast from " .. tostring(self:getTypeTable()) .. " to " .. tostring(theType), 2)
1333 end
1334 end
1335
1336 def.virtual("=>", "string").toString = function (self)
1337 return ("%s#%d"):format(self:getType():getName(), Lplus.tableid(self))
1338 end
1339
1340 --[[
1341 get member value while bypassing accessing checking
1342 ]]
1343 def.method("string", "=>", "dynamic").tryget = function (self, memberName)
1344 if not feature_accessing_checking then
1345 return self[memberName]
1346 else
1347 return getmetatable(self).__tryget(self, memberName)
1348 end
1349 end
1350
1351 Object.Commit()
1352
1353 -- register Object into Lplus
1354 Lplus.Object = Object
1355end
1356
1357--
1358-- Create the type of type info: Type
1359--
1360
1361do
1362 local Type = createType(Lplus.Object, "System.Type", false)
1363 local def = Type.define
1364
1365 --[[
1366 get type table that the type stands for
1367 ]]
1368 def.method("=>", "table").toTypeTable = function (self)
1369 return self.m_typeTable
1370 end
1371
1372 def.method("=>", "string").getName = function (self)
1373 return getTypeTableMetaNoCheck(self.m_typeTable).typeName or "anonymousType"
1374 end
1375
1376 def.method("=>", "table").getBaseTypeTable = function (self)
1377 return getTypeTableMetaNoCheck(self.m_typeTable).baseTypeTable
1378 end
1379
1380 def.method("=>", Type).getBaseType = function (self)
1381 local baseTypeTable = self:getBaseTypeTable()
1382 if baseTypeTable == nil then -- has no base type
1383 return nil
1384 else
1385 return typeTableToTypeInfo(baseTypeTable)
1386 end
1387 end
1388
1389 def.override("=>", "string").toString = function (self)
1390 return self:getName()
1391 end
1392
1393 def.field("table").m_typeTable = nil
1394
1395 def.final("table", "=>", Type).fromTypeTable = function (typeTable)
1396 local typeInfo = Type()
1397 typeInfo.m_typeTable = typeTable
1398 return typeInfo
1399 end
1400
1401 Type.Commit()
1402
1403 -- register Type into Lplus
1404 Lplus.Type = Type
1405end
1406
1407--[[
1408 Create a class with Object to be it's base type
1409 param typeNameOrForwardDeclare:
1410 when is a string: name of the created class used for reflection
1411 typeName should be look like: "Namespace.Subspace.Subspace2.....SubspaceN.ClassShortName"
1412 typeName also can have no namespace: "ClassName"
1413 when is a table: anonymous forward declared type (return value by Lplus.ForwardDeclare)
1414 return the created type table
1415]]
1416function Lplus.Class (typeNameOrForwardDeclare)
1417 return createType(Lplus.Object, typeNameOrForwardDeclare, false)
1418end
1419
1420--[[
1421 Create a class with baseTypeTable to be it's base type
1422 param baseTypeTable: base type of the created class
1423 param typeNameOrForwardDeclare:
1424 when is a string: name of the created class used for reflection
1425 typeName should be look like: "Namespace.Subspace.Subspace2.....SubspaceN.ClassShortName"
1426 typeName also can have no namespace: "ClassName"
1427 when is a table: anonymous forward declared type (return value by Lplus.ForwardDeclare)
1428 return the created type table
1429]]
1430function Lplus.Extend (baseTypeTable, typeNameOrForwardDeclare)
1431 if not getTypeTableMeta(baseTypeTable) then
1432 argError(1, "Extend", "type table", type(baseTypeTable), 2)
1433 end
1434 return createType(baseTypeTable, typeNameOrForwardDeclare, false)
1435end
1436
1437--[[
1438 Create a interface with Object to be it's base type
1439 return the created interface type table
1440]]
1441function Lplus.Interface (typeNameOrForwardDeclare)
1442 return createType(Lplus.Object, typeNameOrForwardDeclare, true)
1443end
1444
1445--[[
1446 Create a interface with baseTypeTable to be it's base type, baseTypeTable must be an interface
1447 return the created interface type table
1448]]
1449function Lplus.ExtendInterface (baseTypeTable, typeNameOrForwardDeclare)
1450 return createType(baseTypeTable, typeNameOrForwardDeclare, true)
1451end
1452
1453----------------------------------------
1454--
1455-- Type checking and convertion
1456--
1457----------------------------------------
1458
1459--[[
1460 get type from given type table
1461 param typeTable: a type table
1462]]
1463function Lplus.typeof (typeTable)
1464 if getTypeTableMeta(typeTable) == nil then
1465 argError(1, "typeof", "type table", type(typeTable), 2)
1466 end
1467 return typeTableToTypeInfo(typeTable)
1468end
1469
1470--[[
1471 check whether given value is a type table
1472 param typeTable: the type table to check
1473]]
1474function Lplus.isTypeTable (typeTable)
1475 return getTypeTableMeta(typeTable) ~= nil
1476end
1477
1478--[[
1479 check whether given object is instance of certain type
1480 param obj: the object to check
1481 param typeTable: type table of the type
1482]]
1483function Lplus.is (obj, typeTable)
1484 if getObjectMeta(obj) == nil then
1485 return false
1486 end
1487
1488 return isCompatibleTypeTable(obj, typeTable)
1489end
1490
1491--[[
1492 get value while bypassing access checking
1493 param key: key of the value
1494]]
1495function Lplus.tryget (obj, key)
1496 if not feature_accessing_checking then
1497 return obj[key]
1498 end
1499
1500 if type(obj) ~= "table" then
1501 argError(1, "tryget", "table", type(obj), 2)
1502 end
1503
1504 local typeTableMeta = getTypeTableMeta(obj)
1505 if typeTableMeta then
1506 return typeTableMeta.__tryget(obj, key)
1507 end
1508
1509 local objMeta = getObjectMeta(obj)
1510 if objMeta then
1511 return objMeta.__tryget(obj, key)
1512 end
1513
1514 return obj[key]
1515end
1516
1517----------------------------------------
1518--
1519-- Misc tools
1520--
1521----------------------------------------
1522
1523local l_tableidmap = {}
1524local l_tableidUnique = 0
1525setmetatable(l_tableidmap, {__mode="kv"})
1526
1527function Lplus.tableid (t)
1528 if type(t) ~= "table" then
1529 argError(1, "tableid", "table", type(t), 2)
1530 end
1531
1532 local curid = l_tableidmap[t]
1533 if curid then
1534 return curid
1535 end
1536
1537 local newid = l_tableidUnique + 1
1538 l_tableidmap[t] = newid
1539 l_tableidUnique = newid
1540
1541 return newid
1542end
1543
1544----------------------------------------
1545--
1546-- Finish
1547--
1548----------------------------------------
1549
1550bInitializingLplus = false
1551return Lplus