1 /* Exports struct cache_entry to the world of ECMAScript */
9 #include "cache/cache.h"
10 #include "ecmascript/spidermonkey-shared.h"
11 #include "protocol/uri.h"
12 #include "scripting/smjs/cache_object.h"
13 #include "scripting/smjs/core.h"
14 #include "scripting/smjs/smjs.h"
15 #include "util/error.h"
16 #include "util/memory.h"
18 static const JSClass cache_entry_class
; /* defined below */
20 /* Tinyids of properties. Use negative values to distinguish these
21 * from array indexes (even though this object has no array elements).
22 * ECMAScript code should not use these directly as in cache_entry[-1];
23 * future versions of ELinks may change the numbers. */
24 enum cache_entry_prop
{
25 CACHE_ENTRY_CONTENT
= -1,
26 CACHE_ENTRY_TYPE
= -2,
27 CACHE_ENTRY_LENGTH
= -3,
28 CACHE_ENTRY_HEAD
= -4,
32 static const JSPropertySpec cache_entry_props
[] = {
33 { "content", CACHE_ENTRY_CONTENT
, JSPROP_ENUMERATE
},
34 { "type", CACHE_ENTRY_TYPE
, JSPROP_ENUMERATE
},
35 { "length", CACHE_ENTRY_LENGTH
, JSPROP_ENUMERATE
| JSPROP_READONLY
},
36 { "head", CACHE_ENTRY_HEAD
, JSPROP_ENUMERATE
},
37 { "uri", CACHE_ENTRY_URI
, JSPROP_ENUMERATE
| JSPROP_READONLY
},
41 /* @cache_entry_class.getProperty */
43 cache_entry_get_property(JSContext
*ctx
, JSObject
*obj
, jsid id
, jsval
*vp
)
45 struct cache_entry
*cached
;
48 /* This can be called if @obj if not itself an instance of the
49 * appropriate class but has one in its prototype chain. Fail
51 if (!JS_InstanceOf(ctx
, obj
, (JSClass
*) &cache_entry_class
, NULL
))
54 cached
= JS_GetInstancePrivate(ctx
, obj
,
55 (JSClass
*) &cache_entry_class
, NULL
);
56 if (!cached
) return JS_FALSE
; /* already detached */
58 assert(cache_entry_is_valid(cached
));
59 if_assert_failed
return JS_FALSE
;
61 /* Get a strong reference to the cache entry to prevent it
62 * from being deleted if some function called below decides to
63 * collect garbage. After this, all code paths must
64 * eventually unlock the object. */
67 undef_to_jsval(ctx
, vp
);
71 else switch (JSID_TO_INT(id
)) {
72 case CACHE_ENTRY_CONTENT
: {
73 struct fragment
*fragment
= get_cache_fragment(cached
);
80 *vp
= STRING_TO_JSVAL(JS_NewStringCopyN(smjs_ctx
,
87 case CACHE_ENTRY_TYPE
:
88 *vp
= STRING_TO_JSVAL(JS_NewStringCopyZ(smjs_ctx
,
89 cached
->content_type
));
93 case CACHE_ENTRY_HEAD
:
94 *vp
= STRING_TO_JSVAL(JS_NewStringCopyZ(smjs_ctx
,
99 case CACHE_ENTRY_LENGTH
:
100 *vp
= INT_TO_JSVAL(cached
->length
);
104 case CACHE_ENTRY_URI
:
105 *vp
= STRING_TO_JSVAL(JS_NewStringCopyZ(smjs_ctx
,
106 struri(cached
->uri
)));
111 /* Unrecognized integer property ID; someone is using
112 * the object as an array. SMJS builtin classes (e.g.
113 * js_RegExpClass) just return JS_TRUE in this case
114 * and leave *@vp unchanged. Do the same here.
115 * (Actually not quite the same, as we already used
116 * @undef_to_jsval.) */
121 object_unlock(cached
);
125 /* @cache_entry_class.setProperty */
127 cache_entry_set_property(JSContext
*ctx
, JSObject
*obj
, jsid id
, JSBool strict
, jsval
*vp
)
129 struct cache_entry
*cached
;
132 /* This can be called if @obj if not itself an instance of the
133 * appropriate class but has one in its prototype chain. Fail
135 if (!JS_InstanceOf(ctx
, obj
, (JSClass
*) &cache_entry_class
, NULL
))
138 cached
= JS_GetInstancePrivate(ctx
, obj
,
139 (JSClass
*) &cache_entry_class
, NULL
);
140 if (!cached
) return JS_FALSE
; /* already detached */
142 assert(cache_entry_is_valid(cached
));
143 if_assert_failed
return JS_FALSE
;
145 /* Get a strong reference to the cache entry to prevent it
146 * from being deleted if some function called below decides to
147 * collect garbage. After this, all code paths must
148 * eventually unlock the object. */
151 if (!JSID_IS_INT(id
))
153 else switch (JSID_TO_INT(id
)) {
154 case CACHE_ENTRY_CONTENT
: {
155 JSString
*jsstr
= JS_ValueToString(smjs_ctx
, *vp
);
156 unsigned char *str
= JS_EncodeString(smjs_ctx
, jsstr
);
157 size_t len
= JS_GetStringLength(jsstr
);
159 add_fragment(cached
, 0, str
, len
);
160 normalize_cache_entry(cached
, len
);
165 case CACHE_ENTRY_TYPE
: {
166 JSString
*jsstr
= JS_ValueToString(smjs_ctx
, *vp
);
167 unsigned char *str
= JS_EncodeString(smjs_ctx
, jsstr
);
169 mem_free_set(&cached
->content_type
, stracpy(str
));
174 case CACHE_ENTRY_HEAD
: {
175 JSString
*jsstr
= JS_ValueToString(smjs_ctx
, *vp
);
176 unsigned char *str
= JS_EncodeString(smjs_ctx
, jsstr
);
178 mem_free_set(&cached
->head
, stracpy(str
));
184 /* Unrecognized integer property ID; someone is using
185 * the object as an array. SMJS builtin classes (e.g.
186 * js_RegExpClass) just return JS_TRUE in this case.
187 * Do the same here. */
192 object_unlock(cached
);
196 /** Pointed to by cache_entry_class.finalize. SpiderMonkey
197 * automatically finalizes all objects before it frees the JSRuntime,
198 * so cache_entry.jsobject won't be left dangling. */
200 cache_entry_finalize(JSContext
*ctx
, JSObject
*obj
)
202 struct cache_entry
*cached
;
204 assert(JS_InstanceOf(ctx
, obj
, (JSClass
*) &cache_entry_class
, NULL
));
205 if_assert_failed
return;
207 cached
= JS_GetInstancePrivate(ctx
, obj
,
208 (JSClass
*) &cache_entry_class
, NULL
);
210 if (!cached
) return; /* already detached */
212 JS_SetPrivate(ctx
, obj
, NULL
); /* perhaps not necessary */
213 assert(cached
->jsobject
== obj
);
214 if_assert_failed
return;
215 cached
->jsobject
= NULL
;
218 static const JSClass cache_entry_class
= {
220 JSCLASS_HAS_PRIVATE
, /* struct cache_entry *; a weak reference */
221 JS_PropertyStub
, JS_PropertyStub
,
222 cache_entry_get_property
, cache_entry_set_property
,
223 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, cache_entry_finalize
226 /** Return an SMJS object through which scripts can access @a cached.
227 * If there already is such an object, return that; otherwise create a
228 * new one. The SMJS object holds only a weak reference to @a cached;
229 * so if the caller wants to guarantee that @a cached exists at least
230 * until a script returns, it should use lock_object() and
231 * unlock_object(). */
233 smjs_get_cache_entry_object(struct cache_entry
*cached
)
235 JSObject
*cache_entry_object
;
237 if (cached
->jsobject
) return cached
->jsobject
;
240 if_assert_failed
return NULL
;
242 cache_entry_object
= JS_NewObject(smjs_ctx
,
243 (JSClass
*) &cache_entry_class
,
246 if (!cache_entry_object
) return NULL
;
248 if (JS_FALSE
== JS_DefineProperties(smjs_ctx
, cache_entry_object
,
249 (JSPropertySpec
*) cache_entry_props
))
252 /* Do this last, so that if any previous step fails, we can
253 * just forget the object and its finalizer won't attempt to
255 if (JS_FALSE
== JS_SetPrivate(smjs_ctx
, cache_entry_object
, cached
)) /* to @cache_entry_class */
258 cached
->jsobject
= cache_entry_object
;
259 return cache_entry_object
;
262 /** Ensure that no JSObject contains the pointer @a cached. This is
263 * called when the reference count of the cache entry *@a cached is
264 * already 0 and it is about to be freed. If a JSObject was
265 * previously attached to the cache entry, the object will remain in
266 * memory but it will no longer be able to access the cache entry. */
268 smjs_detach_cache_entry_object(struct cache_entry
*cached
)
272 if_assert_failed
return;
274 if (!cached
->jsobject
) return;
276 assert(JS_GetInstancePrivate(smjs_ctx
, cached
->jsobject
,
277 (JSClass
*) &cache_entry_class
, NULL
)
281 JS_SetPrivate(smjs_ctx
, cached
->jsobject
, NULL
);
282 cached
->jsobject
= NULL
;