1 /*
2  *	@file 	ejsClass.c
3  *	@brief 	EJS class support
4  */
5 /********************************* Copyright **********************************/
6 /*
7  *	@copy	default
8  *
9  *	Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved.
10  *	Copyright (c) Michael O'Brien, 1994-1995. All Rights Reserved.
11  *
12  *	This software is distributed under commercial and open source licenses.
13  *	You may use the GPL open source license described below or you may acquire
14  *	a commercial license from Mbedthis Software. You agree to be fully bound
15  *	by the terms of either license. Consult the LICENSE.TXT distributed with
16  *	this software for full details.
17  *
18  *	This software is open source; you can redistribute it and/or modify it
19  *	under the terms of the GNU General Public License as published by the
20  *	Free Software Foundation; either version 2 of the License, or (at your
21  *	option) any later version. See the GNU General Public License for more
22  *	details at: http://www.mbedthis.com/downloads/gplLicense.html
23  *
24  *	This program is distributed WITHOUT ANY WARRANTY; without even the
25  *	implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
26  *
27  *	This GPL license does NOT permit incorporating this software into
28  *	proprietary programs. If you are unable to comply with the GPL, you must
29  *	acquire a commercial license to use this software. Commercial licenses
30  *	for this software and support services are available from Mbedthis
31  *	Software at http://www.mbedthis.com
32  *
33  *	@end
34  */
35 /********************************* Includes ***********************************/
36 
37 #include	"ejs.h"
38 
39 #if BLD_FEATURE_EJS
40 
41 /************************************ Code ************************************/
42 /*
43  *	Internal API
44  *
45  *	Routine to create a simple class object. This routine will create a
46  *	stand-alone class object. Callers must insert this into the relevant
47  *	"global" object for name resolution. From these class objects, instance
48  *	objects may be created via the javascript "new" command.
49  *
50  *	Users should use ejsDefineClass
51  */
52 
ejsCreateSimpleClass(Ejs * ep,EjsVar * baseClass,const char * className)53 EjsVar *ejsCreateSimpleClass(Ejs *ep, EjsVar *baseClass, const char *className)
54 {
55 	EjsProperty	*pp;
56 	EjsVar		*classObj;
57 
58 	/*
59 	 *	Create an instance of an Object to act as the static class object
60 	 */
61 	classObj = ejsCreateSimpleObjUsingClass(ep, baseClass);
62 	if (classObj == 0) {
63 		mprAssert(classObj);
64 		return 0;
65 	}
66 	ejsSetClassName(ep, classObj, className);
67 
68 	/*
69 	 *	Set the propotype property to point to this class.
70 	 *	Note: this is a self reference so the alive bit will not be turned on.
71 	 */
72 	pp = ejsSetProperty(ep, classObj, "prototype", classObj);
73 	ejsMakePropertyEnumerable(pp, 0);
74 
75 	return classObj;
76 }
77 
78 /******************************************************************************/
79 /*
80  *	Define a class in the given interpreter. If parentClass is specified, the
81  *	class is defined in the parent. Otherwise, the class will be defined
82  *	locally/globally. ClassName and extends are full variable specs
83  *	(may contain ".")
84  */
85 
ejsDefineClass(Ejs * ep,const char * className,const char * extends,EjsCMethod constructor)86 EjsVar *ejsDefineClass(Ejs *ep, const char *className, const char *extends,
87 	EjsCMethod constructor)
88 {
89 	EjsVar		*parentClass, *classObj, *baseClass, *vp;
90 	char		*name;
91 	char		*cp;
92 
93 	/*
94 	 *	If the className is a qualified name (with "."), then get the
95 	 *	parent class name.
96 	 */
97 	name = mprStrdup(ep, className);
98 	cp = strrchr(name, '.');
99 	if (cp != 0) {
100 		*cp++ = '\0';
101 		className = cp;
102 		parentClass = ejsFindProperty(ep, 0, 0, ep->global, ep->local, name, 0);
103 		if (parentClass == 0 || parentClass->type != EJS_TYPE_OBJECT) {
104 			mprError(ep, MPR_LOC, "Can't find class's parent class %s", name);
105 			mprFree(name);
106 			return 0;
107 		}
108 
109 	} else {
110 		/*
111 		 *	Simple class name without a "." so create the class locally
112 		 *	if a local scope exists, otherwise globally.
113 		 */
114 		parentClass = (ep->local) ? ep->local : ep->global;
115 	}
116 
117 	if (parentClass == 0) {
118 		mprError(ep, MPR_LOC, "Can't find parent class");
119 		mprFree(name);
120 		return 0;
121 	}
122 
123 	/* OPT should use function that doesn't parse [] . */
124 	baseClass = ejsGetClass(ep, 0, extends);
125 	if (baseClass == 0) {
126 		mprAssert(baseClass);
127 		mprFree(name);
128 		return 0;
129 	}
130 
131 	classObj = ejsCreateSimpleClass(ep, baseClass, className);
132 	if (classObj == 0) {
133 		mprAssert(classObj);
134 		mprFree(name);
135 		return 0;
136 	}
137 
138 	if (constructor) {
139 		ejsDefineCMethod(ep, classObj, className, constructor, 0);
140 	}
141 
142 	ejsSetPropertyAndFree(ep, parentClass, className, classObj);
143 
144 	vp = ejsGetPropertyAsVar(ep, parentClass, className);
145 	mprFree(name);
146 
147 	return vp;
148 }
149 
150 /******************************************************************************/
151 /*
152  *	Find a class and return the property defining the class. ClassName may
153  *	contain "." and is interpreted relative to obj. Obj is typically some
154  *	parent object, ep->local or ep->global. If obj is null, then the global
155  *	space is used.
156  */
157 
ejsGetClass(Ejs * ep,EjsVar * obj,const char * className)158 EjsVar *ejsGetClass(Ejs *ep, EjsVar *obj, const char *className)
159 {
160 	EjsVar		*vp;
161 
162 	mprAssert(ep);
163 
164 	/*
165 	 *	Search first for a constructor of the name of class
166 	 *	global may not be defined yet.
167 	 */
168 	if (obj) {
169 		vp = ejsFindProperty(ep, 0, 0, obj, 0, className, 0);
170 
171 	} else {
172 		mprAssert(ep->global);
173 		vp = ejsFindProperty(ep, 0, 0, ep->global, ep->local, className, 0);
174 	}
175 	if (vp == 0 || vp->type != EJS_TYPE_OBJECT) {
176 		return 0;
177 	}
178 
179 	/*
180 	 *	Return a reference to the prototype (self) reference. This
181 	 *	ensures that even if "obj" is deleted, this reference will remain
182 	 *	usable.
183 	 */
184 	return ejsGetPropertyAsVar(ep, vp, "prototype");
185 }
186 
187 /******************************************************************************/
188 /*
189  *	Return the class name of a class or object
190  */
191 
ejsGetClassName(EjsVar * vp)192 const char *ejsGetClassName(EjsVar *vp)
193 {
194 	EjsObj 	*obj;
195 
196 	mprAssert(vp);
197 	mprAssert(vp->type == EJS_TYPE_OBJECT);
198 	mprAssert(vp->objectState->baseClass);
199 
200 	if (vp == 0 || !ejsVarIsObject(vp)) {
201 		return 0;
202 	}
203 	obj = vp->objectState;
204 
205 	return obj->className;
206 }
207 
208 /******************************************************************************/
209 /*
210  *	Return the class name of an objects underlying class
211  *	If called on an object, it returns the base class.
212  *	If called on a class, it returns the base class for the class.
213  */
214 
ejsGetBaseClassName(EjsVar * vp)215 const char *ejsGetBaseClassName(EjsVar *vp)
216 {
217 	EjsObj 	*obj;
218 
219 	mprAssert(vp);
220 	mprAssert(vp->type == EJS_TYPE_OBJECT);
221 	mprAssert(vp->objectState->baseClass);
222 
223 	if (vp == 0 || !ejsVarIsObject(vp)) {
224 		return 0;
225 	}
226 	obj = vp->objectState;
227 	if (obj->baseClass == 0) {
228 		return 0;
229 	}
230 	mprAssert(obj->baseClass->objectState);
231 
232 	return obj->baseClass->objectState->className;
233 }
234 
235 /******************************************************************************/
236 
ejsGetBaseClass(EjsVar * vp)237 EjsVar *ejsGetBaseClass(EjsVar *vp)
238 {
239 	if (vp == 0 || !ejsVarIsObject(vp) || vp->objectState == 0) {
240 		mprAssert(0);
241 		return 0;
242 	}
243 	return vp->objectState->baseClass;
244 }
245 
246 /******************************************************************************/
247 
ejsSetBaseClass(EjsVar * vp,EjsVar * baseClass)248 void ejsSetBaseClass(EjsVar *vp, EjsVar *baseClass)
249 {
250 	if (vp == 0 || !ejsVarIsObject(vp) || vp->objectState == 0) {
251 		mprAssert(0);
252 		return;
253 	}
254 	vp->objectState->baseClass = baseClass;
255 }
256 
257 /******************************************************************************/
258 
259 #else
ejsProcsDummy()260 void ejsProcsDummy() {}
261 
262 /******************************************************************************/
263 #endif /* BLD_FEATURE_EJS */
264 
265 /*
266  * Local variables:
267  * tab-width: 4
268  * c-basic-offset: 4
269  * End:
270  * vim:tw=78
271  * vim600: sw=4 ts=4 fdm=marker
272  * vim<600: sw=4 ts=4
273  */
274