Line data Source code
1 0 : /*
2 :
3 : OOJSMissionVariables.h
4 :
5 : JavaScript mission variables object.
6 :
7 :
8 : Oolite
9 : Copyright (C) 2004-2013 Giles C Williams and contributors
10 :
11 : This program is free software; you can redistribute it and/or
12 : modify it under the terms of the GNU General Public License
13 : as published by the Free Software Foundation; either version 2
14 : of the License, or (at your option) any later version.
15 :
16 : This program is distributed in the hope that it will be useful,
17 : but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : GNU General Public License for more details.
20 :
21 : You should have received a copy of the GNU General Public License
22 : along with this program; if not, write to the Free Software
23 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24 : MA 02110-1301, USA.
25 :
26 : */
27 :
28 : #import "OOJSMissionVariables.h"
29 : #import "OOJavaScriptEngine.h"
30 : #import "OOIsNumberLiteral.h"
31 :
32 : #import "OOJSPlayer.h"
33 :
34 :
35 0 : static NSString *KeyForPropertyID(JSContext *context, jsid propID)
36 : {
37 : NSCParameterAssert(JSID_IS_STRING(propID));
38 :
39 : NSString *key = OOStringFromJSString(context, JSID_TO_STRING(propID));
40 : if ([key hasPrefix:@"_"]) return nil;
41 : return [@"mission_" stringByAppendingString:key];
42 : }
43 :
44 :
45 : static JSBool MissionVariablesDeleteProperty(JSContext *context, JSObject *this, jsid propID, jsval *value);
46 : static JSBool MissionVariablesGetProperty(JSContext *context, JSObject *this, jsid propID, jsval *value);
47 : static JSBool MissionVariablesSetProperty(JSContext *context, JSObject *this, jsid propID, JSBool strict, jsval *value);
48 : static JSBool MissionVariablesEnumerate(JSContext *context, JSObject *object, JSIterateOp enumOp, jsval *state, jsid *idp);
49 :
50 : #ifndef NDEBUG
51 : static id MissionVariablesConverter(JSContext *context, JSObject *object);
52 : #endif
53 :
54 :
55 0 : static JSClass sMissionVariablesClass =
56 : {
57 : "MissionVariables",
58 : JSCLASS_NEW_ENUMERATE,
59 :
60 : JS_PropertyStub,
61 : MissionVariablesDeleteProperty,
62 : MissionVariablesGetProperty,
63 : MissionVariablesSetProperty,
64 : (JSEnumerateOp)MissionVariablesEnumerate,
65 : JS_ResolveStub,
66 : JS_ConvertStub,
67 : JS_FinalizeStub
68 : };
69 :
70 :
71 0 : void InitOOJSMissionVariables(JSContext *context, JSObject *global)
72 : {
73 : JS_DefineObject(context, global, "missionVariables", &sMissionVariablesClass, NULL, OOJS_PROP_READONLY);
74 :
75 : #ifndef NDEBUG
76 : // Allow callObjC() on missionVariables to call methods on the mission variables dictionary.
77 : OOJSRegisterObjectConverter(&sMissionVariablesClass, MissionVariablesConverter);
78 : #endif
79 : }
80 :
81 :
82 : #ifndef NDEBUG
83 0 : static id MissionVariablesConverter(JSContext *context, JSObject *object)
84 : {
85 : return [PLAYER missionVariables];
86 : }
87 : #endif
88 :
89 :
90 0 : static JSBool MissionVariablesDeleteProperty(JSContext *context, JSObject *this, jsid propID, jsval *value)
91 : {
92 : OOJS_NATIVE_ENTER(context)
93 :
94 : PlayerEntity *player = OOPlayerForScripting();
95 :
96 : if (JSID_IS_STRING(propID))
97 : {
98 : NSString *key = KeyForPropertyID(context, propID);
99 : [player setMissionVariable:nil forKey:key];
100 : }
101 : return YES;
102 :
103 : OOJS_NATIVE_EXIT
104 : }
105 :
106 :
107 0 : static JSBool MissionVariablesGetProperty(JSContext *context, JSObject *this, jsid propID, jsval *value)
108 : {
109 : OOJS_NATIVE_ENTER(context)
110 :
111 : PlayerEntity *player = OOPlayerForScripting();
112 :
113 : if (JSID_IS_STRING(propID))
114 : {
115 : NSString *key = KeyForPropertyID(context, propID);
116 : if (key == nil) return YES;
117 :
118 : id mvar = [player missionVariableForKey:key];
119 :
120 : if ([mvar isKindOfClass:[NSString class]]) // Currently there should only be strings, but we may want to change this.
121 : {
122 : if (OOIsNumberLiteral(mvar, YES))
123 : {
124 : return JS_NewNumberValue(context, [mvar doubleValue], value);
125 : }
126 : }
127 :
128 : *value = OOJSValueFromNativeObject(context, mvar);
129 : }
130 : return YES;
131 :
132 : OOJS_NATIVE_EXIT
133 : }
134 :
135 :
136 0 : static JSBool MissionVariablesSetProperty(JSContext *context, JSObject *this, jsid propID, JSBool strict, jsval *value)
137 : {
138 : OOJS_NATIVE_ENTER(context)
139 :
140 : PlayerEntity *player = OOPlayerForScripting();
141 :
142 : if (JSID_IS_STRING(propID))
143 : {
144 : NSString *key = KeyForPropertyID(context, propID);
145 : if (key == nil)
146 : {
147 : OOJSReportError(context, @"Invalid mission variable name \"%@\".", [OOStringFromJSID(propID) escapedForJavaScriptLiteral]);
148 : return NO;
149 : }
150 :
151 : NSString *objValue = OOStringFromJSValue(context, *value);
152 :
153 : if ([objValue isKindOfClass:[NSNull class]]) objValue = nil;
154 : [player setMissionVariable:objValue forKey:key];
155 : }
156 : return YES;
157 :
158 : OOJS_NATIVE_EXIT
159 : }
160 :
161 :
162 0 : static JSBool MissionVariablesEnumerate(JSContext *context, JSObject *object, JSIterateOp enumOp, jsval *state, jsid *idp)
163 : {
164 : OOJS_NATIVE_ENTER(context)
165 :
166 : NSEnumerator *enumerator = nil;
167 :
168 : switch (enumOp)
169 : {
170 : case JSENUMERATE_INIT:
171 : case JSENUMERATE_INIT_ALL: // For ES5 Object.getOwnPropertyNames(). Since we have no non-enumerable properties, this is the same as _INIT.
172 : {
173 : // -allKeys implicitly makes a copy, which is good since the enumerating code might mutate.
174 : NSArray *mvars = [[PLAYER missionVariables] allKeys];
175 : enumerator = [[mvars objectEnumerator] retain];
176 : *state = PRIVATE_TO_JSVAL(enumerator);
177 :
178 : NSUInteger count = [mvars count];
179 : assert(count <= INT32_MAX);
180 : if (idp != NULL) *idp = INT_TO_JSID((uint32_t)count);
181 : return YES;
182 : }
183 :
184 : case JSENUMERATE_NEXT:
185 : {
186 : enumerator = JSVAL_TO_PRIVATE(*state);
187 : for (;;)
188 : {
189 : NSString *next = [enumerator nextObject];
190 : if (next == nil) break;
191 : if (![next hasPrefix:@"mission_"]) continue; // Skip mission instructions, which aren't visible through missionVariables.
192 :
193 : next = [next substringFromIndex:8]; // Cut off "mission_".
194 :
195 : jsval val = [next oo_jsValueInContext:context];
196 : return JS_ValueToId(context, val, idp);
197 : }
198 :
199 : // If we got here, we've hit the end of the enumerator.
200 : *state = JSVAL_NULL;
201 : // Fall through.
202 : }
203 :
204 : case JSENUMERATE_DESTROY:
205 : {
206 : if (enumerator == nil && JSVAL_IS_DOUBLE(*state))
207 : {
208 : enumerator = JSVAL_TO_PRIVATE(*state);
209 : }
210 : [enumerator release];
211 :
212 : if (idp != NULL) *idp = JSID_VOID;
213 : return YES;
214 : }
215 : }
216 :
217 : OOJS_NATIVE_EXIT
218 : }
|