1 |
siliconforks |
332 |
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 |
|
|
* vim: set ts=8 sw=4 et tw=78: |
3 |
|
|
* |
4 |
|
|
* ***** BEGIN LICENSE BLOCK ***** |
5 |
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
6 |
|
|
* |
7 |
|
|
* The contents of this file are subject to the Mozilla Public License Version |
8 |
|
|
* 1.1 (the "License"); you may not use this file except in compliance with |
9 |
|
|
* the License. You may obtain a copy of the License at |
10 |
|
|
* http://www.mozilla.org/MPL/ |
11 |
|
|
* |
12 |
|
|
* Software distributed under the License is distributed on an "AS IS" basis, |
13 |
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
14 |
|
|
* for the specific language governing rights and limitations under the |
15 |
|
|
* License. |
16 |
|
|
* |
17 |
|
|
* The Original Code is Mozilla Communicator client code, released |
18 |
|
|
* March 31, 1998. |
19 |
|
|
* |
20 |
|
|
* The Initial Developer of the Original Code is |
21 |
|
|
* Netscape Communications Corporation. |
22 |
|
|
* Portions created by the Initial Developer are Copyright (C) 1998 |
23 |
|
|
* the Initial Developer. All Rights Reserved. |
24 |
|
|
* |
25 |
|
|
* Contributor(s): |
26 |
|
|
* |
27 |
|
|
* Alternatively, the contents of this file may be used under the terms of |
28 |
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"), |
29 |
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
30 |
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead |
31 |
|
|
* of those above. If you wish to allow use of your version of this file only |
32 |
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to |
33 |
|
|
* use your version of this file under the terms of the MPL, indicate your |
34 |
|
|
* decision by deleting the provisions above and replace them with the notice |
35 |
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete |
36 |
|
|
* the provisions above, a recipient may use your version of this file under |
37 |
|
|
* the terms of any one of the MPL, the GPL or the LGPL. |
38 |
|
|
* |
39 |
|
|
* ***** END LICENSE BLOCK ***** */ |
40 |
|
|
|
41 |
|
|
/* |
42 |
|
|
* JS script operations. |
43 |
|
|
*/ |
44 |
|
|
#include "jsstddef.h" |
45 |
|
|
#include <string.h> |
46 |
|
|
#include "jstypes.h" |
47 |
|
|
#include "jsutil.h" /* Added by JSIFY */ |
48 |
|
|
#include "jsprf.h" |
49 |
|
|
#include "jsapi.h" |
50 |
|
|
#include "jsatom.h" |
51 |
|
|
#include "jscntxt.h" |
52 |
|
|
#include "jsversion.h" |
53 |
|
|
#include "jsdbgapi.h" |
54 |
|
|
#include "jsemit.h" |
55 |
|
|
#include "jsfun.h" |
56 |
|
|
#include "jsinterp.h" |
57 |
|
|
#include "jslock.h" |
58 |
|
|
#include "jsnum.h" |
59 |
|
|
#include "jsopcode.h" |
60 |
|
|
#include "jsparse.h" |
61 |
|
|
#include "jsscope.h" |
62 |
|
|
#include "jsscript.h" |
63 |
siliconforks |
460 |
#include "jstracer.h" |
64 |
siliconforks |
332 |
#if JS_HAS_XDR |
65 |
|
|
#include "jsxdrapi.h" |
66 |
|
|
#endif |
67 |
|
|
|
68 |
|
|
#if JS_HAS_SCRIPT_OBJECT |
69 |
|
|
|
70 |
|
|
static const char js_script_exec_str[] = "Script.prototype.exec"; |
71 |
|
|
static const char js_script_compile_str[] = "Script.prototype.compile"; |
72 |
|
|
|
73 |
|
|
/* |
74 |
|
|
* This routine requires that obj has been locked previously. |
75 |
|
|
*/ |
76 |
|
|
static jsint |
77 |
|
|
GetScriptExecDepth(JSContext *cx, JSObject *obj) |
78 |
|
|
{ |
79 |
|
|
jsval v; |
80 |
|
|
|
81 |
|
|
JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); |
82 |
|
|
v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_START(&js_ScriptClass)); |
83 |
|
|
return JSVAL_TO_INT(v); |
84 |
|
|
} |
85 |
|
|
|
86 |
|
|
static void |
87 |
|
|
AdjustScriptExecDepth(JSContext *cx, JSObject *obj, jsint delta) |
88 |
|
|
{ |
89 |
|
|
jsint execDepth; |
90 |
|
|
|
91 |
|
|
JS_LOCK_OBJ(cx, obj); |
92 |
|
|
execDepth = GetScriptExecDepth(cx, obj); |
93 |
|
|
LOCKED_OBJ_SET_SLOT(obj, JSSLOT_START(&js_ScriptClass), |
94 |
|
|
INT_TO_JSVAL(execDepth + delta)); |
95 |
|
|
JS_UNLOCK_OBJ(cx, obj); |
96 |
|
|
} |
97 |
|
|
|
98 |
|
|
#if JS_HAS_TOSOURCE |
99 |
|
|
static JSBool |
100 |
|
|
script_toSource(JSContext *cx, uintN argc, jsval *vp) |
101 |
|
|
{ |
102 |
|
|
JSObject *obj; |
103 |
|
|
uint32 indent; |
104 |
|
|
JSScript *script; |
105 |
|
|
size_t i, j, k, n; |
106 |
|
|
char buf[16]; |
107 |
|
|
jschar *s, *t; |
108 |
|
|
JSString *str; |
109 |
|
|
|
110 |
|
|
obj = JS_THIS_OBJECT(cx, vp); |
111 |
|
|
if (!JS_InstanceOf(cx, obj, &js_ScriptClass, vp + 2)) |
112 |
|
|
return JS_FALSE; |
113 |
|
|
|
114 |
|
|
indent = 0; |
115 |
|
|
if (argc != 0) { |
116 |
|
|
indent = js_ValueToECMAUint32(cx, &vp[2]); |
117 |
|
|
if (JSVAL_IS_NULL(vp[2])) |
118 |
|
|
return JS_FALSE; |
119 |
|
|
} |
120 |
|
|
|
121 |
|
|
script = (JSScript *) JS_GetPrivate(cx, obj); |
122 |
|
|
|
123 |
|
|
/* Let n count the source string length, j the "front porch" length. */ |
124 |
|
|
j = JS_snprintf(buf, sizeof buf, "(new %s(", js_ScriptClass.name); |
125 |
|
|
n = j + 2; |
126 |
|
|
if (!script) { |
127 |
|
|
/* Let k count the constructor argument string length. */ |
128 |
|
|
k = 0; |
129 |
|
|
s = NULL; /* quell GCC overwarning */ |
130 |
|
|
} else { |
131 |
|
|
str = JS_DecompileScript(cx, script, "Script.prototype.toSource", |
132 |
|
|
(uintN)indent); |
133 |
|
|
if (!str) |
134 |
|
|
return JS_FALSE; |
135 |
|
|
str = js_QuoteString(cx, str, '\''); |
136 |
|
|
if (!str) |
137 |
|
|
return JS_FALSE; |
138 |
|
|
JSSTRING_CHARS_AND_LENGTH(str, s, k); |
139 |
|
|
n += k; |
140 |
|
|
} |
141 |
|
|
|
142 |
|
|
/* Allocate the source string and copy into it. */ |
143 |
|
|
t = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); |
144 |
|
|
if (!t) |
145 |
|
|
return JS_FALSE; |
146 |
|
|
for (i = 0; i < j; i++) |
147 |
|
|
t[i] = buf[i]; |
148 |
|
|
for (j = 0; j < k; i++, j++) |
149 |
|
|
t[i] = s[j]; |
150 |
|
|
t[i++] = ')'; |
151 |
|
|
t[i++] = ')'; |
152 |
|
|
t[i] = 0; |
153 |
|
|
|
154 |
|
|
/* Create and return a JS string for t. */ |
155 |
|
|
str = JS_NewUCString(cx, t, n); |
156 |
|
|
if (!str) { |
157 |
|
|
JS_free(cx, t); |
158 |
|
|
return JS_FALSE; |
159 |
|
|
} |
160 |
|
|
*vp = STRING_TO_JSVAL(str); |
161 |
|
|
return JS_TRUE; |
162 |
|
|
} |
163 |
|
|
#endif /* JS_HAS_TOSOURCE */ |
164 |
|
|
|
165 |
|
|
static JSBool |
166 |
|
|
script_toString(JSContext *cx, uintN argc, jsval *vp) |
167 |
|
|
{ |
168 |
|
|
uint32 indent; |
169 |
|
|
JSObject *obj; |
170 |
|
|
JSScript *script; |
171 |
|
|
JSString *str; |
172 |
|
|
|
173 |
|
|
indent = 0; |
174 |
|
|
if (argc != 0) { |
175 |
|
|
indent = js_ValueToECMAUint32(cx, &vp[2]); |
176 |
|
|
if (JSVAL_IS_NULL(vp[2])) |
177 |
|
|
return JS_FALSE; |
178 |
|
|
} |
179 |
|
|
|
180 |
|
|
obj = JS_THIS_OBJECT(cx, vp); |
181 |
|
|
if (!JS_InstanceOf(cx, obj, &js_ScriptClass, vp + 2)) |
182 |
|
|
return JS_FALSE; |
183 |
|
|
script = (JSScript *) JS_GetPrivate(cx, obj); |
184 |
|
|
if (!script) { |
185 |
|
|
*vp = STRING_TO_JSVAL(cx->runtime->emptyString); |
186 |
|
|
return JS_TRUE; |
187 |
|
|
} |
188 |
|
|
|
189 |
|
|
str = JS_DecompileScript(cx, script, "Script.prototype.toString", |
190 |
|
|
(uintN)indent); |
191 |
|
|
if (!str) |
192 |
|
|
return JS_FALSE; |
193 |
|
|
*vp = STRING_TO_JSVAL(str); |
194 |
|
|
return JS_TRUE; |
195 |
|
|
} |
196 |
|
|
|
197 |
|
|
static JSBool |
198 |
|
|
script_compile_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, |
199 |
|
|
jsval *rval) |
200 |
|
|
{ |
201 |
|
|
JSString *str; |
202 |
|
|
JSObject *scopeobj; |
203 |
|
|
jsval v; |
204 |
|
|
JSScript *script, *oldscript; |
205 |
|
|
JSStackFrame *caller; |
206 |
|
|
const char *file; |
207 |
|
|
uintN line; |
208 |
|
|
JSPrincipals *principals; |
209 |
|
|
uint32 tcflags; |
210 |
|
|
jsint execDepth; |
211 |
|
|
|
212 |
|
|
/* Make sure obj is a Script object. */ |
213 |
|
|
if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) |
214 |
|
|
return JS_FALSE; |
215 |
|
|
|
216 |
|
|
/* If no args, leave private undefined and return early. */ |
217 |
|
|
if (argc == 0) |
218 |
|
|
goto out; |
219 |
|
|
|
220 |
|
|
/* Otherwise, the first arg is the script source to compile. */ |
221 |
|
|
str = js_ValueToString(cx, argv[0]); |
222 |
|
|
if (!str) |
223 |
|
|
return JS_FALSE; |
224 |
|
|
argv[0] = STRING_TO_JSVAL(str); |
225 |
|
|
|
226 |
|
|
scopeobj = NULL; |
227 |
|
|
if (argc >= 2) { |
228 |
|
|
if (!js_ValueToObject(cx, argv[1], &scopeobj)) |
229 |
|
|
return JS_FALSE; |
230 |
|
|
argv[1] = OBJECT_TO_JSVAL(scopeobj); |
231 |
|
|
} |
232 |
|
|
|
233 |
|
|
/* Compile using the caller's scope chain, which js_Invoke passes to fp. */ |
234 |
siliconforks |
460 |
caller = js_GetScriptedCaller(cx, NULL); |
235 |
siliconforks |
332 |
JS_ASSERT(!caller || cx->fp->scopeChain == caller->scopeChain); |
236 |
|
|
|
237 |
|
|
if (caller) { |
238 |
|
|
if (!scopeobj) { |
239 |
|
|
scopeobj = js_GetScopeChain(cx, caller); |
240 |
|
|
if (!scopeobj) |
241 |
|
|
return JS_FALSE; |
242 |
|
|
} |
243 |
|
|
|
244 |
|
|
principals = JS_EvalFramePrincipals(cx, cx->fp, caller); |
245 |
|
|
file = js_ComputeFilename(cx, caller, principals, &line); |
246 |
|
|
} else { |
247 |
|
|
file = NULL; |
248 |
|
|
line = 0; |
249 |
|
|
principals = NULL; |
250 |
|
|
} |
251 |
|
|
|
252 |
|
|
/* Ensure we compile this script with the right (inner) principals. */ |
253 |
|
|
scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_compile_str); |
254 |
|
|
if (!scopeobj) |
255 |
|
|
return JS_FALSE; |
256 |
|
|
|
257 |
|
|
/* |
258 |
|
|
* Compile the new script using the caller's scope chain, a la eval(). |
259 |
|
|
* Unlike jsobj.c:obj_eval, however, we do not pass TCF_COMPILE_N_GO in |
260 |
|
|
* tcflags and use NULL for the callerFrame argument, because compilation |
261 |
|
|
* is here separated from execution, and the run-time scope chain may not |
262 |
|
|
* match the compile-time. TCF_COMPILE_N_GO is tested in jsemit.c and |
263 |
|
|
* jsparse.c to optimize based on identity of run- and compile-time scope. |
264 |
|
|
*/ |
265 |
|
|
tcflags = 0; |
266 |
siliconforks |
460 |
script = JSCompiler::compileScript(cx, scopeobj, NULL, principals, tcflags, |
267 |
|
|
JSSTRING_CHARS(str), JSSTRING_LENGTH(str), |
268 |
|
|
NULL, file, line); |
269 |
siliconforks |
332 |
if (!script) |
270 |
|
|
return JS_FALSE; |
271 |
|
|
|
272 |
|
|
JS_LOCK_OBJ(cx, obj); |
273 |
|
|
execDepth = GetScriptExecDepth(cx, obj); |
274 |
|
|
|
275 |
|
|
/* |
276 |
|
|
* execDepth must be 0 to allow compilation here, otherwise the JSScript |
277 |
|
|
* struct can be released while running. |
278 |
|
|
*/ |
279 |
|
|
if (execDepth > 0) { |
280 |
|
|
JS_UNLOCK_OBJ(cx, obj); |
281 |
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
282 |
|
|
JSMSG_COMPILE_EXECED_SCRIPT); |
283 |
|
|
return JS_FALSE; |
284 |
|
|
} |
285 |
|
|
|
286 |
|
|
/* Swap script for obj's old script, if any. */ |
287 |
|
|
v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PRIVATE); |
288 |
|
|
oldscript = (JSScript*) (!JSVAL_IS_VOID(v) ? JSVAL_TO_PRIVATE(v) : NULL); |
289 |
|
|
LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script)); |
290 |
|
|
JS_UNLOCK_OBJ(cx, obj); |
291 |
|
|
|
292 |
|
|
if (oldscript) |
293 |
|
|
js_DestroyScript(cx, oldscript); |
294 |
|
|
|
295 |
|
|
script->u.object = obj; |
296 |
|
|
js_CallNewScriptHook(cx, script, NULL); |
297 |
|
|
|
298 |
|
|
out: |
299 |
|
|
/* Return the object. */ |
300 |
|
|
*rval = OBJECT_TO_JSVAL(obj); |
301 |
|
|
return JS_TRUE; |
302 |
|
|
} |
303 |
|
|
|
304 |
|
|
static JSBool |
305 |
|
|
script_compile(JSContext *cx, uintN argc, jsval *vp) |
306 |
|
|
{ |
307 |
|
|
return script_compile_sub(cx, JS_THIS_OBJECT(cx, vp), argc, vp + 2, vp); |
308 |
|
|
} |
309 |
|
|
|
310 |
|
|
static JSBool |
311 |
|
|
script_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, |
312 |
|
|
jsval *rval) |
313 |
|
|
{ |
314 |
siliconforks |
460 |
JSObject *scopeobj; |
315 |
|
|
JSStackFrame *caller; |
316 |
siliconforks |
332 |
JSPrincipals *principals; |
317 |
|
|
JSScript *script; |
318 |
|
|
JSBool ok; |
319 |
|
|
|
320 |
|
|
if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) |
321 |
|
|
return JS_FALSE; |
322 |
|
|
|
323 |
|
|
scopeobj = NULL; |
324 |
|
|
if (argc != 0) { |
325 |
|
|
if (!js_ValueToObject(cx, argv[0], &scopeobj)) |
326 |
|
|
return JS_FALSE; |
327 |
|
|
argv[0] = OBJECT_TO_JSVAL(scopeobj); |
328 |
|
|
} |
329 |
|
|
|
330 |
|
|
/* |
331 |
|
|
* Emulate eval() by using caller's this, var object, sharp array, etc., |
332 |
|
|
* all propagated by js_Execute via a non-null fourth (down) argument to |
333 |
|
|
* js_Execute. If there is no scripted caller, js_Execute uses its second |
334 |
|
|
* (chain) argument to set the exec frame's varobj, thisp, and scopeChain. |
335 |
|
|
* |
336 |
|
|
* Unlike eval, which the compiler detects, Script.prototype.exec may be |
337 |
|
|
* called from a lightweight function, or even from native code (in which |
338 |
|
|
* case fp->varobj and fp->scopeChain are null). If exec is called from |
339 |
|
|
* a lightweight function, we will need to get a Call object representing |
340 |
|
|
* its frame, to act as the var object and scope chain head. |
341 |
|
|
*/ |
342 |
siliconforks |
460 |
caller = js_GetScriptedCaller(cx, NULL); |
343 |
siliconforks |
332 |
if (caller && !caller->varobj) { |
344 |
|
|
/* Called from a lightweight function. */ |
345 |
|
|
JS_ASSERT(caller->fun && !JSFUN_HEAVYWEIGHT_TEST(caller->fun->flags)); |
346 |
|
|
|
347 |
siliconforks |
460 |
/* Scope chain links from Call object to caller's scope chain. */ |
348 |
|
|
if (!js_GetCallObject(cx, caller)) |
349 |
siliconforks |
332 |
return JS_FALSE; |
350 |
|
|
} |
351 |
|
|
|
352 |
|
|
if (!scopeobj) { |
353 |
|
|
/* No scope object passed in: try to use the caller's scope chain. */ |
354 |
|
|
if (caller) { |
355 |
|
|
/* |
356 |
|
|
* Load caller->scopeChain after the conditional js_GetCallObject |
357 |
|
|
* call above, which resets scopeChain as well as varobj. |
358 |
|
|
*/ |
359 |
|
|
scopeobj = js_GetScopeChain(cx, caller); |
360 |
|
|
if (!scopeobj) |
361 |
|
|
return JS_FALSE; |
362 |
|
|
} else { |
363 |
|
|
/* |
364 |
|
|
* Called from native code, so we don't know what scope object to |
365 |
siliconforks |
460 |
* use. We could use the caller's scope chain (see above), but Script.prototype.exec |
366 |
siliconforks |
332 |
* might be a shared/sealed "superglobal" method. A more general |
367 |
|
|
* approach would use cx->globalObject, which will be the same as |
368 |
|
|
* exec.__parent__ in the non-superglobal case. In the superglobal |
369 |
|
|
* case it's the right object: the global, not the superglobal. |
370 |
|
|
*/ |
371 |
|
|
scopeobj = cx->globalObject; |
372 |
|
|
} |
373 |
|
|
} |
374 |
|
|
|
375 |
|
|
scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_exec_str); |
376 |
|
|
if (!scopeobj) |
377 |
|
|
return JS_FALSE; |
378 |
|
|
|
379 |
|
|
/* Keep track of nesting depth for the script. */ |
380 |
|
|
AdjustScriptExecDepth(cx, obj, 1); |
381 |
|
|
|
382 |
|
|
/* Must get to out label after this */ |
383 |
|
|
script = (JSScript *) JS_GetPrivate(cx, obj); |
384 |
|
|
if (!script) { |
385 |
|
|
ok = JS_FALSE; |
386 |
|
|
goto out; |
387 |
|
|
} |
388 |
|
|
|
389 |
|
|
/* Belt-and-braces: check that this script object has access to scopeobj. */ |
390 |
|
|
principals = script->principals; |
391 |
|
|
ok = js_CheckPrincipalsAccess(cx, scopeobj, principals, |
392 |
|
|
CLASS_ATOM(cx, Script)); |
393 |
|
|
if (!ok) |
394 |
|
|
goto out; |
395 |
|
|
|
396 |
|
|
ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); |
397 |
|
|
|
398 |
|
|
out: |
399 |
|
|
AdjustScriptExecDepth(cx, obj, -1); |
400 |
|
|
return ok; |
401 |
|
|
} |
402 |
|
|
|
403 |
|
|
static JSBool |
404 |
|
|
script_exec(JSContext *cx, uintN argc, jsval *vp) |
405 |
|
|
{ |
406 |
|
|
return script_exec_sub(cx, JS_THIS_OBJECT(cx, vp), argc, vp + 2, vp); |
407 |
|
|
} |
408 |
|
|
|
409 |
|
|
#endif /* JS_HAS_SCRIPT_OBJECT */ |
410 |
|
|
|
411 |
|
|
#if JS_HAS_XDR |
412 |
|
|
|
413 |
|
|
JSBool |
414 |
|
|
js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic) |
415 |
|
|
{ |
416 |
|
|
JSContext *cx; |
417 |
|
|
JSScript *script, *oldscript; |
418 |
|
|
JSBool ok; |
419 |
|
|
jsbytecode *code; |
420 |
|
|
uint32 length, lineno, nslots, magic; |
421 |
|
|
uint32 natoms, nsrcnotes, ntrynotes, nobjects, nupvars, nregexps, i; |
422 |
|
|
uint32 prologLength, version; |
423 |
|
|
JSTempValueRooter tvr; |
424 |
|
|
JSPrincipals *principals; |
425 |
|
|
uint32 encodeable; |
426 |
|
|
JSBool filenameWasSaved; |
427 |
|
|
jssrcnote *notes, *sn; |
428 |
|
|
JSSecurityCallbacks *callbacks; |
429 |
|
|
|
430 |
|
|
cx = xdr->cx; |
431 |
|
|
script = *scriptp; |
432 |
|
|
nsrcnotes = ntrynotes = natoms = nobjects = nupvars = nregexps = 0; |
433 |
|
|
filenameWasSaved = JS_FALSE; |
434 |
|
|
notes = NULL; |
435 |
|
|
|
436 |
|
|
if (xdr->mode == JSXDR_ENCODE) |
437 |
|
|
magic = JSXDR_MAGIC_SCRIPT_CURRENT; |
438 |
|
|
if (!JS_XDRUint32(xdr, &magic)) |
439 |
|
|
return JS_FALSE; |
440 |
|
|
if (magic != JSXDR_MAGIC_SCRIPT_CURRENT) { |
441 |
|
|
/* We do not provide binary compatibility with older scripts. */ |
442 |
|
|
if (!hasMagic) { |
443 |
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
444 |
|
|
JSMSG_BAD_SCRIPT_MAGIC); |
445 |
|
|
return JS_FALSE; |
446 |
|
|
} |
447 |
|
|
*hasMagic = JS_FALSE; |
448 |
|
|
return JS_TRUE; |
449 |
|
|
} |
450 |
|
|
if (hasMagic) |
451 |
|
|
*hasMagic = JS_TRUE; |
452 |
|
|
|
453 |
|
|
if (xdr->mode == JSXDR_ENCODE) { |
454 |
|
|
length = script->length; |
455 |
|
|
prologLength = PTRDIFF(script->main, script->code, jsbytecode); |
456 |
|
|
JS_ASSERT((int16)script->version != JSVERSION_UNKNOWN); |
457 |
|
|
version = (uint32)script->version | (script->nfixed << 16); |
458 |
|
|
lineno = (uint32)script->lineno; |
459 |
|
|
nslots = (uint32)script->nslots; |
460 |
siliconforks |
460 |
nslots = (uint32)((script->staticLevel << 16) | script->nslots); |
461 |
siliconforks |
332 |
natoms = (uint32)script->atomMap.length; |
462 |
|
|
|
463 |
|
|
/* Count the srcnotes, keeping notes pointing at the first one. */ |
464 |
|
|
notes = SCRIPT_NOTES(script); |
465 |
|
|
for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) |
466 |
|
|
continue; |
467 |
|
|
nsrcnotes = PTRDIFF(sn, notes, jssrcnote); |
468 |
|
|
nsrcnotes++; /* room for the terminator */ |
469 |
|
|
|
470 |
|
|
if (script->objectsOffset != 0) |
471 |
|
|
nobjects = JS_SCRIPT_OBJECTS(script)->length; |
472 |
|
|
if (script->upvarsOffset != 0) |
473 |
|
|
nupvars = JS_SCRIPT_UPVARS(script)->length; |
474 |
|
|
if (script->regexpsOffset != 0) |
475 |
|
|
nregexps = JS_SCRIPT_REGEXPS(script)->length; |
476 |
|
|
if (script->trynotesOffset != 0) |
477 |
|
|
ntrynotes = JS_SCRIPT_TRYNOTES(script)->length; |
478 |
|
|
} |
479 |
|
|
|
480 |
|
|
if (!JS_XDRUint32(xdr, &length)) |
481 |
|
|
return JS_FALSE; |
482 |
|
|
if (!JS_XDRUint32(xdr, &prologLength)) |
483 |
|
|
return JS_FALSE; |
484 |
|
|
if (!JS_XDRUint32(xdr, &version)) |
485 |
|
|
return JS_FALSE; |
486 |
|
|
|
487 |
|
|
/* |
488 |
|
|
* To fuse allocations, we need srcnote, atom, objects, upvar, regexp, |
489 |
|
|
* and trynote counts early. |
490 |
|
|
*/ |
491 |
|
|
if (!JS_XDRUint32(xdr, &natoms)) |
492 |
|
|
return JS_FALSE; |
493 |
|
|
if (!JS_XDRUint32(xdr, &nsrcnotes)) |
494 |
|
|
return JS_FALSE; |
495 |
|
|
if (!JS_XDRUint32(xdr, &ntrynotes)) |
496 |
|
|
return JS_FALSE; |
497 |
|
|
if (!JS_XDRUint32(xdr, &nobjects)) |
498 |
|
|
return JS_FALSE; |
499 |
|
|
if (!JS_XDRUint32(xdr, &nupvars)) |
500 |
|
|
return JS_FALSE; |
501 |
|
|
if (!JS_XDRUint32(xdr, &nregexps)) |
502 |
|
|
return JS_FALSE; |
503 |
|
|
|
504 |
|
|
if (xdr->mode == JSXDR_DECODE) { |
505 |
|
|
script = js_NewScript(cx, length, nsrcnotes, natoms, nobjects, nupvars, |
506 |
|
|
nregexps, ntrynotes); |
507 |
|
|
if (!script) |
508 |
|
|
return JS_FALSE; |
509 |
|
|
|
510 |
|
|
script->main += prologLength; |
511 |
siliconforks |
460 |
script->version = JSVersion(version & 0xffff); |
512 |
|
|
script->nfixed = uint16(version >> 16); |
513 |
siliconforks |
332 |
|
514 |
|
|
/* If we know nsrcnotes, we allocated space for notes in script. */ |
515 |
|
|
notes = SCRIPT_NOTES(script); |
516 |
|
|
*scriptp = script; |
517 |
|
|
JS_PUSH_TEMP_ROOT_SCRIPT(cx, script, &tvr); |
518 |
|
|
} |
519 |
|
|
|
520 |
|
|
/* |
521 |
|
|
* Control hereafter must goto error on failure, in order for the |
522 |
|
|
* DECODE case to destroy script. |
523 |
|
|
*/ |
524 |
|
|
oldscript = xdr->script; |
525 |
|
|
code = script->code; |
526 |
|
|
if (xdr->mode == JSXDR_ENCODE) { |
527 |
|
|
code = js_UntrapScriptCode(cx, script); |
528 |
|
|
if (!code) |
529 |
|
|
goto error; |
530 |
|
|
} |
531 |
|
|
|
532 |
|
|
xdr->script = script; |
533 |
|
|
ok = JS_XDRBytes(xdr, (char *) code, length * sizeof(jsbytecode)); |
534 |
|
|
|
535 |
|
|
if (code != script->code) |
536 |
|
|
JS_free(cx, code); |
537 |
|
|
|
538 |
|
|
if (!ok) |
539 |
|
|
goto error; |
540 |
|
|
|
541 |
|
|
if (!JS_XDRBytes(xdr, (char *)notes, nsrcnotes * sizeof(jssrcnote)) || |
542 |
|
|
!JS_XDRCStringOrNull(xdr, (char **)&script->filename) || |
543 |
|
|
!JS_XDRUint32(xdr, &lineno) || |
544 |
|
|
!JS_XDRUint32(xdr, &nslots)) { |
545 |
|
|
goto error; |
546 |
|
|
} |
547 |
|
|
|
548 |
|
|
callbacks = JS_GetSecurityCallbacks(cx); |
549 |
|
|
if (xdr->mode == JSXDR_ENCODE) { |
550 |
|
|
principals = script->principals; |
551 |
|
|
encodeable = callbacks && callbacks->principalsTranscoder; |
552 |
|
|
if (!JS_XDRUint32(xdr, &encodeable)) |
553 |
|
|
goto error; |
554 |
|
|
if (encodeable && |
555 |
|
|
!callbacks->principalsTranscoder(xdr, &principals)) { |
556 |
|
|
goto error; |
557 |
|
|
} |
558 |
|
|
} else { |
559 |
|
|
if (!JS_XDRUint32(xdr, &encodeable)) |
560 |
|
|
goto error; |
561 |
|
|
if (encodeable) { |
562 |
|
|
if (!(callbacks && callbacks->principalsTranscoder)) { |
563 |
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
564 |
|
|
JSMSG_CANT_DECODE_PRINCIPALS); |
565 |
|
|
goto error; |
566 |
|
|
} |
567 |
|
|
if (!callbacks->principalsTranscoder(xdr, &principals)) |
568 |
|
|
goto error; |
569 |
|
|
script->principals = principals; |
570 |
|
|
} |
571 |
|
|
} |
572 |
|
|
|
573 |
|
|
if (xdr->mode == JSXDR_DECODE) { |
574 |
|
|
const char *filename = script->filename; |
575 |
|
|
if (filename) { |
576 |
|
|
filename = js_SaveScriptFilename(cx, filename); |
577 |
|
|
if (!filename) |
578 |
|
|
goto error; |
579 |
|
|
JS_free(cx, (void *) script->filename); |
580 |
|
|
script->filename = filename; |
581 |
|
|
filenameWasSaved = JS_TRUE; |
582 |
|
|
} |
583 |
|
|
script->lineno = (uintN)lineno; |
584 |
|
|
script->nslots = (uint16)nslots; |
585 |
siliconforks |
460 |
script->staticLevel = (uint16)(nslots >> 16); |
586 |
siliconforks |
332 |
} |
587 |
|
|
|
588 |
|
|
for (i = 0; i != natoms; ++i) { |
589 |
|
|
if (!js_XDRAtom(xdr, &script->atomMap.vector[i])) |
590 |
|
|
goto error; |
591 |
|
|
} |
592 |
|
|
|
593 |
|
|
/* |
594 |
|
|
* Here looping from 0-to-length to xdr objects is essential. It ensures |
595 |
|
|
* that block objects from the script->objects array will be written and |
596 |
siliconforks |
460 |
* restored in the outer-to-inner order. js_XDRBlockObject relies on this |
597 |
|
|
* to restore the parent chain. |
598 |
siliconforks |
332 |
*/ |
599 |
|
|
for (i = 0; i != nobjects; ++i) { |
600 |
siliconforks |
460 |
JSObject **objp = &JS_SCRIPT_OBJECTS(script)->vector[i]; |
601 |
|
|
uint32 isBlock; |
602 |
|
|
if (xdr->mode == JSXDR_ENCODE) { |
603 |
|
|
JSClass *clasp = STOBJ_GET_CLASS(*objp); |
604 |
|
|
JS_ASSERT(clasp == &js_FunctionClass || |
605 |
|
|
clasp == &js_BlockClass); |
606 |
|
|
isBlock = (clasp == &js_BlockClass) ? 1 : 0; |
607 |
|
|
} |
608 |
|
|
if (!JS_XDRUint32(xdr, &isBlock)) |
609 |
siliconforks |
332 |
goto error; |
610 |
siliconforks |
460 |
if (isBlock == 0) { |
611 |
|
|
if (!js_XDRFunctionObject(xdr, objp)) |
612 |
|
|
goto error; |
613 |
|
|
} else { |
614 |
|
|
JS_ASSERT(isBlock == 1); |
615 |
|
|
if (!js_XDRBlockObject(xdr, objp)) |
616 |
|
|
goto error; |
617 |
|
|
} |
618 |
siliconforks |
332 |
} |
619 |
|
|
for (i = 0; i != nupvars; ++i) { |
620 |
|
|
if (!JS_XDRUint32(xdr, &JS_SCRIPT_UPVARS(script)->vector[i])) |
621 |
|
|
goto error; |
622 |
|
|
} |
623 |
|
|
for (i = 0; i != nregexps; ++i) { |
624 |
siliconforks |
460 |
if (!js_XDRRegExpObject(xdr, &JS_SCRIPT_REGEXPS(script)->vector[i])) |
625 |
siliconforks |
332 |
goto error; |
626 |
|
|
} |
627 |
|
|
|
628 |
|
|
if (ntrynotes != 0) { |
629 |
|
|
/* |
630 |
|
|
* We combine tn->kind and tn->stackDepth when serializing as XDR is not |
631 |
|
|
* efficient when serializing small integer types. |
632 |
|
|
*/ |
633 |
|
|
JSTryNote *tn, *tnfirst; |
634 |
|
|
uint32 kindAndDepth; |
635 |
|
|
JS_STATIC_ASSERT(sizeof(tn->kind) == sizeof(uint8)); |
636 |
|
|
JS_STATIC_ASSERT(sizeof(tn->stackDepth) == sizeof(uint16)); |
637 |
|
|
|
638 |
|
|
tnfirst = JS_SCRIPT_TRYNOTES(script)->vector; |
639 |
|
|
JS_ASSERT(JS_SCRIPT_TRYNOTES(script)->length == ntrynotes); |
640 |
|
|
tn = tnfirst + ntrynotes; |
641 |
|
|
do { |
642 |
|
|
--tn; |
643 |
|
|
if (xdr->mode == JSXDR_ENCODE) { |
644 |
|
|
kindAndDepth = ((uint32)tn->kind << 16) |
645 |
|
|
| (uint32)tn->stackDepth; |
646 |
|
|
} |
647 |
|
|
if (!JS_XDRUint32(xdr, &kindAndDepth) || |
648 |
|
|
!JS_XDRUint32(xdr, &tn->start) || |
649 |
|
|
!JS_XDRUint32(xdr, &tn->length)) { |
650 |
|
|
goto error; |
651 |
|
|
} |
652 |
|
|
if (xdr->mode == JSXDR_DECODE) { |
653 |
|
|
tn->kind = (uint8)(kindAndDepth >> 16); |
654 |
|
|
tn->stackDepth = (uint16)kindAndDepth; |
655 |
|
|
} |
656 |
|
|
} while (tn != tnfirst); |
657 |
|
|
} |
658 |
|
|
|
659 |
|
|
xdr->script = oldscript; |
660 |
|
|
if (xdr->mode == JSXDR_DECODE) |
661 |
|
|
JS_POP_TEMP_ROOT(cx, &tvr); |
662 |
|
|
return JS_TRUE; |
663 |
|
|
|
664 |
|
|
error: |
665 |
|
|
if (xdr->mode == JSXDR_DECODE) { |
666 |
|
|
JS_POP_TEMP_ROOT(cx, &tvr); |
667 |
|
|
if (script->filename && !filenameWasSaved) { |
668 |
|
|
JS_free(cx, (void *) script->filename); |
669 |
|
|
script->filename = NULL; |
670 |
|
|
} |
671 |
|
|
js_DestroyScript(cx, script); |
672 |
|
|
*scriptp = NULL; |
673 |
|
|
} |
674 |
|
|
xdr->script = oldscript; |
675 |
|
|
return JS_FALSE; |
676 |
|
|
} |
677 |
|
|
|
678 |
|
|
#if JS_HAS_SCRIPT_OBJECT && JS_HAS_XDR_FREEZE_THAW |
679 |
|
|
/* |
680 |
|
|
* These cannot be exposed to web content, and chrome does not need them, so |
681 |
|
|
* we take them out of the Mozilla client altogether. Fortunately, there is |
682 |
|
|
* no way to serialize a native function (see fun_xdrObject in jsfun.c). |
683 |
|
|
*/ |
684 |
|
|
|
685 |
|
|
static JSBool |
686 |
|
|
script_freeze(JSContext *cx, uintN argc, jsval *vp) |
687 |
|
|
{ |
688 |
|
|
JSObject *obj; |
689 |
|
|
JSXDRState *xdr; |
690 |
|
|
JSScript *script; |
691 |
|
|
JSBool ok, hasMagic; |
692 |
|
|
uint32 len; |
693 |
|
|
void *buf; |
694 |
|
|
JSString *str; |
695 |
|
|
|
696 |
|
|
obj = JS_THIS_OBJECT(cx, vp); |
697 |
|
|
if (!JS_InstanceOf(cx, obj, &js_ScriptClass, vp + 2)) |
698 |
|
|
return JS_FALSE; |
699 |
|
|
script = (JSScript *) JS_GetPrivate(cx, obj); |
700 |
|
|
if (!script) |
701 |
|
|
return JS_TRUE; |
702 |
|
|
|
703 |
|
|
/* create new XDR */ |
704 |
|
|
xdr = JS_XDRNewMem(cx, JSXDR_ENCODE); |
705 |
|
|
if (!xdr) |
706 |
|
|
return JS_FALSE; |
707 |
|
|
|
708 |
|
|
/* write */ |
709 |
|
|
ok = js_XDRScript(xdr, &script, &hasMagic); |
710 |
|
|
if (!ok) |
711 |
|
|
goto out; |
712 |
|
|
if (!hasMagic) { |
713 |
|
|
*vp = JSVAL_VOID; |
714 |
|
|
goto out; |
715 |
|
|
} |
716 |
|
|
|
717 |
|
|
buf = JS_XDRMemGetData(xdr, &len); |
718 |
|
|
if (!buf) { |
719 |
|
|
ok = JS_FALSE; |
720 |
|
|
goto out; |
721 |
|
|
} |
722 |
|
|
|
723 |
|
|
JS_ASSERT((jsword)buf % sizeof(jschar) == 0); |
724 |
|
|
len /= sizeof(jschar); |
725 |
|
|
#if IS_BIG_ENDIAN |
726 |
|
|
{ |
727 |
|
|
jschar *chars; |
728 |
|
|
uint32 i; |
729 |
|
|
|
730 |
|
|
/* Swap bytes in Unichars to keep frozen strings machine-independent. */ |
731 |
|
|
chars = (jschar *)buf; |
732 |
|
|
for (i = 0; i < len; i++) |
733 |
|
|
chars[i] = JSXDR_SWAB16(chars[i]); |
734 |
|
|
} |
735 |
|
|
#endif |
736 |
|
|
str = JS_NewUCStringCopyN(cx, (jschar *)buf, len); |
737 |
|
|
if (!str) { |
738 |
|
|
ok = JS_FALSE; |
739 |
|
|
goto out; |
740 |
|
|
} |
741 |
|
|
|
742 |
|
|
*vp = STRING_TO_JSVAL(str); |
743 |
|
|
|
744 |
|
|
out: |
745 |
|
|
JS_XDRDestroy(xdr); |
746 |
|
|
return ok; |
747 |
|
|
} |
748 |
|
|
|
749 |
|
|
static JSBool |
750 |
|
|
script_thaw(JSContext *cx, uintN argc, jsval *vp) |
751 |
|
|
{ |
752 |
|
|
JSObject *obj; |
753 |
|
|
JSXDRState *xdr; |
754 |
|
|
JSString *str; |
755 |
|
|
void *buf; |
756 |
|
|
uint32 len; |
757 |
|
|
jsval v; |
758 |
|
|
JSScript *script, *oldscript; |
759 |
|
|
JSBool ok, hasMagic; |
760 |
|
|
jsint execDepth; |
761 |
|
|
|
762 |
|
|
obj = JS_THIS_OBJECT(cx, vp); |
763 |
|
|
if (!JS_InstanceOf(cx, obj, &js_ScriptClass, vp + 2)) |
764 |
|
|
return JS_FALSE; |
765 |
|
|
|
766 |
|
|
if (argc == 0) |
767 |
|
|
return JS_TRUE; |
768 |
|
|
str = js_ValueToString(cx, vp[2]); |
769 |
|
|
if (!str) |
770 |
|
|
return JS_FALSE; |
771 |
|
|
vp[2] = STRING_TO_JSVAL(str); |
772 |
|
|
|
773 |
|
|
/* create new XDR */ |
774 |
|
|
xdr = JS_XDRNewMem(cx, JSXDR_DECODE); |
775 |
|
|
if (!xdr) |
776 |
|
|
return JS_FALSE; |
777 |
|
|
|
778 |
|
|
JSSTRING_CHARS_AND_LENGTH(str, buf, len); |
779 |
|
|
#if IS_BIG_ENDIAN |
780 |
|
|
{ |
781 |
|
|
jschar *from, *to; |
782 |
|
|
uint32 i; |
783 |
|
|
|
784 |
|
|
/* Swap bytes in Unichars to keep frozen strings machine-independent. */ |
785 |
|
|
from = (jschar *)buf; |
786 |
|
|
to = (jschar *) JS_malloc(cx, len * sizeof(jschar)); |
787 |
|
|
if (!to) { |
788 |
|
|
JS_XDRDestroy(xdr); |
789 |
|
|
return JS_FALSE; |
790 |
|
|
} |
791 |
|
|
for (i = 0; i < len; i++) |
792 |
|
|
to[i] = JSXDR_SWAB16(from[i]); |
793 |
|
|
buf = (char *)to; |
794 |
|
|
} |
795 |
|
|
#endif |
796 |
|
|
len *= sizeof(jschar); |
797 |
|
|
JS_XDRMemSetData(xdr, buf, len); |
798 |
|
|
|
799 |
|
|
/* XXXbe should magic mismatch be error, or false return value? */ |
800 |
|
|
ok = js_XDRScript(xdr, &script, &hasMagic); |
801 |
|
|
if (!ok) |
802 |
|
|
goto out; |
803 |
|
|
if (!hasMagic) { |
804 |
|
|
*vp = JSVAL_FALSE; |
805 |
|
|
goto out; |
806 |
|
|
} |
807 |
|
|
|
808 |
|
|
JS_LOCK_OBJ(cx, obj); |
809 |
|
|
execDepth = GetScriptExecDepth(cx, obj); |
810 |
|
|
|
811 |
|
|
/* |
812 |
|
|
* execDepth must be 0 to allow compilation here, otherwise the JSScript |
813 |
|
|
* struct can be released while running. |
814 |
|
|
*/ |
815 |
|
|
if (execDepth > 0) { |
816 |
|
|
JS_UNLOCK_OBJ(cx, obj); |
817 |
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
818 |
|
|
JSMSG_COMPILE_EXECED_SCRIPT); |
819 |
|
|
goto out; |
820 |
|
|
} |
821 |
|
|
|
822 |
|
|
/* Swap script for obj's old script, if any. */ |
823 |
|
|
v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PRIVATE); |
824 |
|
|
oldscript = !JSVAL_IS_VOID(v) ? JSVAL_TO_PRIVATE(v) : NULL; |
825 |
|
|
LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script)); |
826 |
|
|
JS_UNLOCK_OBJ(cx, obj); |
827 |
|
|
|
828 |
|
|
if (oldscript) |
829 |
|
|
js_DestroyScript(cx, oldscript); |
830 |
|
|
|
831 |
|
|
script->u.object = obj; |
832 |
|
|
js_CallNewScriptHook(cx, script, NULL); |
833 |
|
|
|
834 |
|
|
out: |
835 |
|
|
/* |
836 |
|
|
* We reset the buffer to be NULL so that it doesn't free the chars |
837 |
|
|
* memory owned by str (vp[2]). |
838 |
|
|
*/ |
839 |
|
|
JS_XDRMemSetData(xdr, NULL, 0); |
840 |
|
|
JS_XDRDestroy(xdr); |
841 |
|
|
#if IS_BIG_ENDIAN |
842 |
|
|
JS_free(cx, buf); |
843 |
|
|
#endif |
844 |
|
|
*vp = JSVAL_TRUE; |
845 |
|
|
return ok; |
846 |
|
|
} |
847 |
|
|
|
848 |
|
|
static const char js_thaw_str[] = "thaw"; |
849 |
|
|
|
850 |
|
|
#endif /* JS_HAS_SCRIPT_OBJECT && JS_HAS_XDR_FREEZE_THAW */ |
851 |
|
|
#endif /* JS_HAS_XDR */ |
852 |
|
|
|
853 |
|
|
#if JS_HAS_SCRIPT_OBJECT |
854 |
|
|
|
855 |
|
|
static JSFunctionSpec script_methods[] = { |
856 |
|
|
#if JS_HAS_TOSOURCE |
857 |
|
|
JS_FN(js_toSource_str, script_toSource, 0,0), |
858 |
|
|
#endif |
859 |
|
|
JS_FN(js_toString_str, script_toString, 0,0), |
860 |
|
|
JS_FN("compile", script_compile, 2,0), |
861 |
|
|
JS_FN("exec", script_exec, 1,0), |
862 |
|
|
#if JS_HAS_XDR_FREEZE_THAW |
863 |
|
|
JS_FN("freeze", script_freeze, 0,0), |
864 |
|
|
JS_FN(js_thaw_str, script_thaw, 1,0), |
865 |
|
|
#endif /* JS_HAS_XDR_FREEZE_THAW */ |
866 |
|
|
JS_FS_END |
867 |
|
|
}; |
868 |
|
|
|
869 |
|
|
#endif /* JS_HAS_SCRIPT_OBJECT */ |
870 |
|
|
|
871 |
|
|
static void |
872 |
|
|
script_finalize(JSContext *cx, JSObject *obj) |
873 |
|
|
{ |
874 |
|
|
JSScript *script; |
875 |
|
|
|
876 |
|
|
script = (JSScript *) JS_GetPrivate(cx, obj); |
877 |
|
|
if (script) |
878 |
|
|
js_DestroyScript(cx, script); |
879 |
|
|
} |
880 |
|
|
|
881 |
|
|
static JSBool |
882 |
|
|
script_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
883 |
|
|
{ |
884 |
|
|
#if JS_HAS_SCRIPT_OBJECT |
885 |
|
|
return script_exec_sub(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval); |
886 |
|
|
#else |
887 |
|
|
return JS_FALSE; |
888 |
|
|
#endif |
889 |
|
|
} |
890 |
|
|
|
891 |
|
|
static void |
892 |
|
|
script_trace(JSTracer *trc, JSObject *obj) |
893 |
|
|
{ |
894 |
|
|
JSScript *script; |
895 |
|
|
|
896 |
|
|
script = (JSScript *) JS_GetPrivate(trc->context, obj); |
897 |
|
|
if (script) |
898 |
|
|
js_TraceScript(trc, script); |
899 |
|
|
} |
900 |
|
|
|
901 |
|
|
#if !JS_HAS_SCRIPT_OBJECT |
902 |
|
|
#define JSProto_Script JSProto_Object |
903 |
|
|
#endif |
904 |
|
|
|
905 |
|
|
JS_FRIEND_DATA(JSClass) js_ScriptClass = { |
906 |
|
|
js_Script_str, |
907 |
|
|
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | |
908 |
|
|
JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Script), |
909 |
|
|
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, |
910 |
|
|
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, script_finalize, |
911 |
|
|
NULL, NULL, script_call, NULL,/*XXXbe xdr*/ |
912 |
|
|
NULL, NULL, JS_CLASS_TRACE(script_trace), NULL |
913 |
|
|
}; |
914 |
|
|
|
915 |
|
|
#if JS_HAS_SCRIPT_OBJECT |
916 |
|
|
|
917 |
|
|
static JSBool |
918 |
|
|
Script(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
919 |
|
|
{ |
920 |
|
|
/* If not constructing, replace obj with a new Script object. */ |
921 |
siliconforks |
460 |
if (!JS_IsConstructing(cx)) { |
922 |
siliconforks |
332 |
obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL, 0); |
923 |
|
|
if (!obj) |
924 |
|
|
return JS_FALSE; |
925 |
|
|
|
926 |
|
|
/* |
927 |
|
|
* script_compile_sub does not use rval to root its temporaries so we |
928 |
|
|
* can use it to root obj. |
929 |
|
|
*/ |
930 |
|
|
*rval = OBJECT_TO_JSVAL(obj); |
931 |
|
|
} |
932 |
|
|
|
933 |
|
|
if (!JS_SetReservedSlot(cx, obj, 0, INT_TO_JSVAL(0))) |
934 |
|
|
return JS_FALSE; |
935 |
|
|
|
936 |
|
|
return script_compile_sub(cx, obj, argc, argv, rval); |
937 |
|
|
} |
938 |
|
|
|
939 |
|
|
#if JS_HAS_SCRIPT_OBJECT && JS_HAS_XDR_FREEZE_THAW |
940 |
|
|
|
941 |
|
|
static JSBool |
942 |
|
|
script_static_thaw(JSContext *cx, uintN argc, jsval *vp) |
943 |
|
|
{ |
944 |
|
|
JSObject *obj; |
945 |
|
|
|
946 |
|
|
obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); |
947 |
|
|
if (!obj) |
948 |
|
|
return JS_FALSE; |
949 |
|
|
vp[1] = OBJECT_TO_JSVAL(obj); |
950 |
|
|
if (!script_thaw(cx, argc, vp)) |
951 |
|
|
return JS_FALSE; |
952 |
|
|
*vp = OBJECT_TO_JSVAL(obj); |
953 |
|
|
return JS_TRUE; |
954 |
|
|
} |
955 |
|
|
|
956 |
|
|
static JSFunctionSpec script_static_methods[] = { |
957 |
|
|
JS_FN(js_thaw_str, script_static_thaw, 1,0), |
958 |
|
|
JS_FS_END |
959 |
|
|
}; |
960 |
|
|
|
961 |
|
|
#else /* !JS_HAS_SCRIPT_OBJECT || !JS_HAS_XDR_FREEZE_THAW */ |
962 |
|
|
|
963 |
|
|
#define script_static_methods NULL |
964 |
|
|
|
965 |
|
|
#endif /* !JS_HAS_SCRIPT_OBJECT || !JS_HAS_XDR_FREEZE_THAW */ |
966 |
|
|
|
967 |
|
|
JSObject * |
968 |
|
|
js_InitScriptClass(JSContext *cx, JSObject *obj) |
969 |
|
|
{ |
970 |
|
|
return JS_InitClass(cx, obj, NULL, &js_ScriptClass, Script, 1, |
971 |
|
|
NULL, script_methods, NULL, script_static_methods); |
972 |
|
|
} |
973 |
|
|
|
974 |
|
|
#endif /* JS_HAS_SCRIPT_OBJECT */ |
975 |
|
|
|
976 |
|
|
/* |
977 |
|
|
* Shared script filename management. |
978 |
|
|
*/ |
979 |
|
|
static int |
980 |
|
|
js_compare_strings(const void *k1, const void *k2) |
981 |
|
|
{ |
982 |
|
|
return strcmp((const char *) k1, (const char *) k2) == 0; |
983 |
|
|
} |
984 |
|
|
|
985 |
|
|
/* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */ |
986 |
|
|
typedef struct ScriptFilenameEntry { |
987 |
|
|
JSHashEntry *next; /* hash chain linkage */ |
988 |
|
|
JSHashNumber keyHash; /* key hash function result */ |
989 |
|
|
const void *key; /* ptr to filename, below */ |
990 |
|
|
uint32 flags; /* user-defined filename prefix flags */ |
991 |
|
|
JSPackedBool mark; /* GC mark flag */ |
992 |
|
|
char filename[3]; /* two or more bytes, NUL-terminated */ |
993 |
|
|
} ScriptFilenameEntry; |
994 |
|
|
|
995 |
|
|
static void * |
996 |
|
|
js_alloc_table_space(void *priv, size_t size) |
997 |
|
|
{ |
998 |
|
|
return malloc(size); |
999 |
|
|
} |
1000 |
|
|
|
1001 |
|
|
static void |
1002 |
siliconforks |
460 |
js_free_table_space(void *priv, void *item, size_t size) |
1003 |
siliconforks |
332 |
{ |
1004 |
|
|
free(item); |
1005 |
|
|
} |
1006 |
|
|
|
1007 |
|
|
static JSHashEntry * |
1008 |
|
|
js_alloc_sftbl_entry(void *priv, const void *key) |
1009 |
|
|
{ |
1010 |
|
|
size_t nbytes = offsetof(ScriptFilenameEntry, filename) + |
1011 |
|
|
strlen((const char *) key) + 1; |
1012 |
|
|
|
1013 |
|
|
return (JSHashEntry *) malloc(JS_MAX(nbytes, sizeof(JSHashEntry))); |
1014 |
|
|
} |
1015 |
|
|
|
1016 |
|
|
static void |
1017 |
|
|
js_free_sftbl_entry(void *priv, JSHashEntry *he, uintN flag) |
1018 |
|
|
{ |
1019 |
|
|
if (flag != HT_FREE_ENTRY) |
1020 |
|
|
return; |
1021 |
|
|
free(he); |
1022 |
|
|
} |
1023 |
|
|
|
1024 |
|
|
static JSHashAllocOps sftbl_alloc_ops = { |
1025 |
|
|
js_alloc_table_space, js_free_table_space, |
1026 |
|
|
js_alloc_sftbl_entry, js_free_sftbl_entry |
1027 |
|
|
}; |
1028 |
|
|
|
1029 |
|
|
JSBool |
1030 |
|
|
js_InitRuntimeScriptState(JSRuntime *rt) |
1031 |
|
|
{ |
1032 |
|
|
#ifdef JS_THREADSAFE |
1033 |
|
|
JS_ASSERT(!rt->scriptFilenameTableLock); |
1034 |
|
|
rt->scriptFilenameTableLock = JS_NEW_LOCK(); |
1035 |
|
|
if (!rt->scriptFilenameTableLock) |
1036 |
|
|
return JS_FALSE; |
1037 |
|
|
#endif |
1038 |
|
|
JS_ASSERT(!rt->scriptFilenameTable); |
1039 |
|
|
rt->scriptFilenameTable = |
1040 |
|
|
JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL, |
1041 |
|
|
&sftbl_alloc_ops, NULL); |
1042 |
|
|
if (!rt->scriptFilenameTable) { |
1043 |
|
|
js_FinishRuntimeScriptState(rt); /* free lock if threadsafe */ |
1044 |
|
|
return JS_FALSE; |
1045 |
|
|
} |
1046 |
|
|
JS_INIT_CLIST(&rt->scriptFilenamePrefixes); |
1047 |
|
|
return JS_TRUE; |
1048 |
|
|
} |
1049 |
|
|
|
1050 |
|
|
typedef struct ScriptFilenamePrefix { |
1051 |
|
|
JSCList links; /* circular list linkage for easy deletion */ |
1052 |
|
|
const char *name; /* pointer to pinned ScriptFilenameEntry string */ |
1053 |
|
|
size_t length; /* prefix string length, precomputed */ |
1054 |
|
|
uint32 flags; /* user-defined flags to inherit from this prefix */ |
1055 |
|
|
} ScriptFilenamePrefix; |
1056 |
|
|
|
1057 |
|
|
void |
1058 |
|
|
js_FinishRuntimeScriptState(JSRuntime *rt) |
1059 |
|
|
{ |
1060 |
|
|
if (rt->scriptFilenameTable) { |
1061 |
|
|
JS_HashTableDestroy(rt->scriptFilenameTable); |
1062 |
|
|
rt->scriptFilenameTable = NULL; |
1063 |
|
|
} |
1064 |
|
|
#ifdef JS_THREADSAFE |
1065 |
|
|
if (rt->scriptFilenameTableLock) { |
1066 |
|
|
JS_DESTROY_LOCK(rt->scriptFilenameTableLock); |
1067 |
|
|
rt->scriptFilenameTableLock = NULL; |
1068 |
|
|
} |
1069 |
|
|
#endif |
1070 |
|
|
} |
1071 |
|
|
|
1072 |
|
|
void |
1073 |
|
|
js_FreeRuntimeScriptState(JSRuntime *rt) |
1074 |
|
|
{ |
1075 |
|
|
ScriptFilenamePrefix *sfp; |
1076 |
|
|
|
1077 |
|
|
if (!rt->scriptFilenameTable) |
1078 |
|
|
return; |
1079 |
|
|
|
1080 |
|
|
while (!JS_CLIST_IS_EMPTY(&rt->scriptFilenamePrefixes)) { |
1081 |
|
|
sfp = (ScriptFilenamePrefix *) rt->scriptFilenamePrefixes.next; |
1082 |
|
|
JS_REMOVE_LINK(&sfp->links); |
1083 |
|
|
free(sfp); |
1084 |
|
|
} |
1085 |
|
|
js_FinishRuntimeScriptState(rt); |
1086 |
|
|
} |
1087 |
|
|
|
1088 |
|
|
#ifdef DEBUG_brendan |
1089 |
|
|
#define DEBUG_SFTBL |
1090 |
|
|
#endif |
1091 |
|
|
#ifdef DEBUG_SFTBL |
1092 |
|
|
size_t sftbl_savings = 0; |
1093 |
|
|
#endif |
1094 |
|
|
|
1095 |
|
|
static ScriptFilenameEntry * |
1096 |
|
|
SaveScriptFilename(JSRuntime *rt, const char *filename, uint32 flags) |
1097 |
|
|
{ |
1098 |
|
|
JSHashTable *table; |
1099 |
|
|
JSHashNumber hash; |
1100 |
|
|
JSHashEntry **hep; |
1101 |
|
|
ScriptFilenameEntry *sfe; |
1102 |
|
|
size_t length; |
1103 |
|
|
JSCList *head, *link; |
1104 |
|
|
ScriptFilenamePrefix *sfp; |
1105 |
|
|
|
1106 |
|
|
table = rt->scriptFilenameTable; |
1107 |
|
|
hash = JS_HashString(filename); |
1108 |
|
|
hep = JS_HashTableRawLookup(table, hash, filename); |
1109 |
|
|
sfe = (ScriptFilenameEntry *) *hep; |
1110 |
|
|
#ifdef DEBUG_SFTBL |
1111 |
|
|
if (sfe) |
1112 |
|
|
sftbl_savings += strlen(sfe->filename); |
1113 |
|
|
#endif |
1114 |
|
|
|
1115 |
|
|
if (!sfe) { |
1116 |
|
|
sfe = (ScriptFilenameEntry *) |
1117 |
|
|
JS_HashTableRawAdd(table, hep, hash, filename, NULL); |
1118 |
|
|
if (!sfe) |
1119 |
|
|
return NULL; |
1120 |
|
|
sfe->key = strcpy(sfe->filename, filename); |
1121 |
|
|
sfe->flags = 0; |
1122 |
|
|
sfe->mark = JS_FALSE; |
1123 |
|
|
} |
1124 |
|
|
|
1125 |
|
|
/* If saving a prefix, add it to the set in rt->scriptFilenamePrefixes. */ |
1126 |
|
|
if (flags != 0) { |
1127 |
|
|
/* Search in case filename was saved already; we must be idempotent. */ |
1128 |
|
|
sfp = NULL; |
1129 |
|
|
length = strlen(filename); |
1130 |
|
|
for (head = link = &rt->scriptFilenamePrefixes; |
1131 |
|
|
link->next != head; |
1132 |
|
|
link = link->next) { |
1133 |
|
|
/* Lag link behind sfp to insert in non-increasing length order. */ |
1134 |
|
|
sfp = (ScriptFilenamePrefix *) link->next; |
1135 |
|
|
if (!strcmp(sfp->name, filename)) |
1136 |
|
|
break; |
1137 |
|
|
if (sfp->length <= length) { |
1138 |
|
|
sfp = NULL; |
1139 |
|
|
break; |
1140 |
|
|
} |
1141 |
|
|
sfp = NULL; |
1142 |
|
|
} |
1143 |
|
|
|
1144 |
|
|
if (!sfp) { |
1145 |
|
|
/* No such prefix: add one now. */ |
1146 |
|
|
sfp = (ScriptFilenamePrefix *) malloc(sizeof(ScriptFilenamePrefix)); |
1147 |
|
|
if (!sfp) |
1148 |
|
|
return NULL; |
1149 |
|
|
JS_INSERT_AFTER(&sfp->links, link); |
1150 |
|
|
sfp->name = sfe->filename; |
1151 |
|
|
sfp->length = length; |
1152 |
|
|
sfp->flags = 0; |
1153 |
|
|
} |
1154 |
|
|
|
1155 |
|
|
/* |
1156 |
|
|
* Accumulate flags in both sfe and sfp: sfe for later access from the |
1157 |
|
|
* JS_GetScriptedCallerFilenameFlags debug-API, and sfp so that longer |
1158 |
|
|
* filename entries can inherit by prefix. |
1159 |
|
|
*/ |
1160 |
|
|
sfe->flags |= flags; |
1161 |
|
|
sfp->flags |= flags; |
1162 |
|
|
} |
1163 |
|
|
|
1164 |
siliconforks |
460 |
#ifdef JS_FUNCTION_METERING |
1165 |
|
|
size_t len = strlen(sfe->filename); |
1166 |
|
|
if (len >= sizeof rt->lastScriptFilename) |
1167 |
|
|
len = sizeof rt->lastScriptFilename - 1; |
1168 |
|
|
memcpy(rt->lastScriptFilename, sfe->filename, len); |
1169 |
|
|
rt->lastScriptFilename[len] = '\0'; |
1170 |
|
|
#endif |
1171 |
|
|
|
1172 |
siliconforks |
332 |
return sfe; |
1173 |
|
|
} |
1174 |
|
|
|
1175 |
|
|
const char * |
1176 |
|
|
js_SaveScriptFilename(JSContext *cx, const char *filename) |
1177 |
|
|
{ |
1178 |
|
|
JSRuntime *rt; |
1179 |
|
|
ScriptFilenameEntry *sfe; |
1180 |
|
|
JSCList *head, *link; |
1181 |
|
|
ScriptFilenamePrefix *sfp; |
1182 |
|
|
|
1183 |
|
|
rt = cx->runtime; |
1184 |
|
|
JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock); |
1185 |
|
|
sfe = SaveScriptFilename(rt, filename, 0); |
1186 |
|
|
if (!sfe) { |
1187 |
|
|
JS_RELEASE_LOCK(rt->scriptFilenameTableLock); |
1188 |
|
|
JS_ReportOutOfMemory(cx); |
1189 |
|
|
return NULL; |
1190 |
|
|
} |
1191 |
|
|
|
1192 |
|
|
/* |
1193 |
|
|
* Try to inherit flags by prefix. We assume there won't be more than a |
1194 |
|
|
* few (dozen! ;-) prefixes, so linear search is tolerable. |
1195 |
|
|
* XXXbe every time I've assumed that in the JS engine, I've been wrong! |
1196 |
|
|
*/ |
1197 |
|
|
for (head = &rt->scriptFilenamePrefixes, link = head->next; |
1198 |
|
|
link != head; |
1199 |
|
|
link = link->next) { |
1200 |
|
|
sfp = (ScriptFilenamePrefix *) link; |
1201 |
|
|
if (!strncmp(sfp->name, filename, sfp->length)) { |
1202 |
|
|
sfe->flags |= sfp->flags; |
1203 |
|
|
break; |
1204 |
|
|
} |
1205 |
|
|
} |
1206 |
|
|
JS_RELEASE_LOCK(rt->scriptFilenameTableLock); |
1207 |
|
|
return sfe->filename; |
1208 |
|
|
} |
1209 |
|
|
|
1210 |
|
|
const char * |
1211 |
|
|
js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags) |
1212 |
|
|
{ |
1213 |
|
|
ScriptFilenameEntry *sfe; |
1214 |
|
|
|
1215 |
|
|
/* This may be called very early, via the jsdbgapi.h entry point. */ |
1216 |
|
|
if (!rt->scriptFilenameTable && !js_InitRuntimeScriptState(rt)) |
1217 |
|
|
return NULL; |
1218 |
|
|
|
1219 |
|
|
JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock); |
1220 |
|
|
sfe = SaveScriptFilename(rt, filename, flags); |
1221 |
|
|
JS_RELEASE_LOCK(rt->scriptFilenameTableLock); |
1222 |
|
|
if (!sfe) |
1223 |
|
|
return NULL; |
1224 |
|
|
|
1225 |
|
|
return sfe->filename; |
1226 |
|
|
} |
1227 |
|
|
|
1228 |
|
|
/* |
1229 |
|
|
* Back up from a saved filename by its offset within its hash table entry. |
1230 |
|
|
*/ |
1231 |
|
|
#define FILENAME_TO_SFE(fn) \ |
1232 |
|
|
((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename))) |
1233 |
|
|
|
1234 |
|
|
/* |
1235 |
|
|
* The sfe->key member, redundant given sfe->filename but required by the old |
1236 |
|
|
* jshash.c code, here gives us a useful sanity check. This assertion will |
1237 |
|
|
* very likely botch if someone tries to mark a string that wasn't allocated |
1238 |
|
|
* as an sfe->filename. |
1239 |
|
|
*/ |
1240 |
|
|
#define ASSERT_VALID_SFE(sfe) JS_ASSERT((sfe)->key == (sfe)->filename) |
1241 |
|
|
|
1242 |
|
|
uint32 |
1243 |
|
|
js_GetScriptFilenameFlags(const char *filename) |
1244 |
|
|
{ |
1245 |
|
|
ScriptFilenameEntry *sfe; |
1246 |
|
|
|
1247 |
|
|
sfe = FILENAME_TO_SFE(filename); |
1248 |
|
|
ASSERT_VALID_SFE(sfe); |
1249 |
|
|
return sfe->flags; |
1250 |
|
|
} |
1251 |
|
|
|
1252 |
|
|
void |
1253 |
|
|
js_MarkScriptFilename(const char *filename) |
1254 |
|
|
{ |
1255 |
|
|
ScriptFilenameEntry *sfe; |
1256 |
|
|
|
1257 |
|
|
sfe = FILENAME_TO_SFE(filename); |
1258 |
|
|
ASSERT_VALID_SFE(sfe); |
1259 |
|
|
sfe->mark = JS_TRUE; |
1260 |
|
|
} |
1261 |
|
|
|
1262 |
|
|
static intN |
1263 |
|
|
js_script_filename_marker(JSHashEntry *he, intN i, void *arg) |
1264 |
|
|
{ |
1265 |
|
|
ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he; |
1266 |
|
|
|
1267 |
|
|
sfe->mark = JS_TRUE; |
1268 |
|
|
return HT_ENUMERATE_NEXT; |
1269 |
|
|
} |
1270 |
|
|
|
1271 |
|
|
void |
1272 |
|
|
js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms) |
1273 |
|
|
{ |
1274 |
|
|
JSCList *head, *link; |
1275 |
|
|
ScriptFilenamePrefix *sfp; |
1276 |
|
|
|
1277 |
|
|
if (!rt->scriptFilenameTable) |
1278 |
|
|
return; |
1279 |
|
|
|
1280 |
|
|
if (keepAtoms) { |
1281 |
|
|
JS_HashTableEnumerateEntries(rt->scriptFilenameTable, |
1282 |
|
|
js_script_filename_marker, |
1283 |
|
|
rt); |
1284 |
|
|
} |
1285 |
|
|
for (head = &rt->scriptFilenamePrefixes, link = head->next; |
1286 |
|
|
link != head; |
1287 |
|
|
link = link->next) { |
1288 |
|
|
sfp = (ScriptFilenamePrefix *) link; |
1289 |
|
|
js_MarkScriptFilename(sfp->name); |
1290 |
|
|
} |
1291 |
|
|
} |
1292 |
|
|
|
1293 |
|
|
static intN |
1294 |
|
|
js_script_filename_sweeper(JSHashEntry *he, intN i, void *arg) |
1295 |
|
|
{ |
1296 |
|
|
ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he; |
1297 |
|
|
|
1298 |
|
|
if (!sfe->mark) |
1299 |
|
|
return HT_ENUMERATE_REMOVE; |
1300 |
|
|
sfe->mark = JS_FALSE; |
1301 |
|
|
return HT_ENUMERATE_NEXT; |
1302 |
|
|
} |
1303 |
|
|
|
1304 |
|
|
void |
1305 |
|
|
js_SweepScriptFilenames(JSRuntime *rt) |
1306 |
|
|
{ |
1307 |
|
|
if (!rt->scriptFilenameTable) |
1308 |
|
|
return; |
1309 |
|
|
|
1310 |
|
|
JS_HashTableEnumerateEntries(rt->scriptFilenameTable, |
1311 |
|
|
js_script_filename_sweeper, |
1312 |
|
|
rt); |
1313 |
|
|
#ifdef DEBUG_notme |
1314 |
|
|
#ifdef DEBUG_SFTBL |
1315 |
|
|
printf("script filename table savings so far: %u\n", sftbl_savings); |
1316 |
|
|
#endif |
1317 |
|
|
#endif |
1318 |
|
|
} |
1319 |
|
|
|
1320 |
|
|
/* |
1321 |
|
|
* JSScript data structures memory alignment: |
1322 |
|
|
* |
1323 |
|
|
* JSScript |
1324 |
|
|
* JSObjectArray script objects' descriptor if JSScript.objectsOffset != 0, |
1325 |
|
|
* use JS_SCRIPT_OBJECTS(script) macro to access it. |
1326 |
|
|
* JSObjectArray script regexps' descriptor if JSScript.regexpsOffset != 0, |
1327 |
|
|
* use JS_SCRIPT_REGEXPS(script) macro to access it. |
1328 |
|
|
* JSTryNoteArray script try notes' descriptor if JSScript.tryNotesOffset |
1329 |
|
|
* != 0, use JS_SCRIPT_TRYNOTES(script) macro to access it. |
1330 |
|
|
* JSAtom *a[] array of JSScript.atomMap.length atoms pointed by |
1331 |
|
|
* JSScript.atomMap.vector if any. |
1332 |
|
|
* JSObject *o[] array of JS_SCRIPT_OBJECTS(script)->length objects if any |
1333 |
|
|
* pointed by JS_SCRIPT_OBJECTS(script)->vector. |
1334 |
|
|
* JSObject *r[] array of JS_SCRIPT_REGEXPS(script)->length regexps if any |
1335 |
|
|
* pointed by JS_SCRIPT_REGEXPS(script)->vector. |
1336 |
|
|
* JSTryNote t[] array of JS_SCRIPT_TRYNOTES(script)->length try notes if any |
1337 |
|
|
* pointed by JS_SCRIPT_TRYNOTES(script)->vector. |
1338 |
|
|
* jsbytecode b[] script bytecode pointed by JSScript.code. |
1339 |
|
|
* jssrcnote s[] script source notes, use SCRIPT_NOTES(script) to access it |
1340 |
|
|
* |
1341 |
|
|
* The alignment avoids gaps between entries as alignment requirement for each |
1342 |
|
|
* subsequent structure or array is the same or divides the alignment |
1343 |
|
|
* requirement for the previous one. |
1344 |
|
|
* |
1345 |
|
|
* The followings asserts checks that assuming that the alignment requirement |
1346 |
|
|
* for JSObjectArray and JSTryNoteArray are sizeof(void *) and for JSTryNote |
1347 |
|
|
* it is sizeof(uint32) as the structure consists of 3 uint32 fields. |
1348 |
|
|
*/ |
1349 |
|
|
JS_STATIC_ASSERT(sizeof(JSScript) % sizeof(void *) == 0); |
1350 |
|
|
JS_STATIC_ASSERT(sizeof(JSObjectArray) % sizeof(void *) == 0); |
1351 |
|
|
JS_STATIC_ASSERT(sizeof(JSTryNoteArray) == sizeof(JSObjectArray)); |
1352 |
|
|
JS_STATIC_ASSERT(sizeof(JSAtom *) == sizeof(JSObject *)); |
1353 |
|
|
JS_STATIC_ASSERT(sizeof(JSObject *) % sizeof(uint32) == 0); |
1354 |
|
|
JS_STATIC_ASSERT(sizeof(JSTryNote) == 3 * sizeof(uint32)); |
1355 |
|
|
JS_STATIC_ASSERT(sizeof(uint32) % sizeof(jsbytecode) == 0); |
1356 |
|
|
JS_STATIC_ASSERT(sizeof(jsbytecode) % sizeof(jssrcnote) == 0); |
1357 |
|
|
|
1358 |
|
|
/* |
1359 |
|
|
* Check that uint8 offset for object, upvar, regexp, and try note arrays is |
1360 |
|
|
* sufficient. |
1361 |
|
|
*/ |
1362 |
|
|
JS_STATIC_ASSERT(sizeof(JSScript) + 2 * sizeof(JSObjectArray) + |
1363 |
|
|
sizeof(JSUpvarArray) < JS_BIT(8)); |
1364 |
|
|
|
1365 |
|
|
JSScript * |
1366 |
|
|
js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms, |
1367 |
|
|
uint32 nobjects, uint32 nupvars, uint32 nregexps, |
1368 |
|
|
uint32 ntrynotes) |
1369 |
|
|
{ |
1370 |
|
|
size_t size, vectorSize; |
1371 |
|
|
JSScript *script; |
1372 |
|
|
uint8 *cursor; |
1373 |
|
|
|
1374 |
|
|
size = sizeof(JSScript) + |
1375 |
|
|
sizeof(JSAtom *) * natoms + |
1376 |
|
|
length * sizeof(jsbytecode) + |
1377 |
|
|
nsrcnotes * sizeof(jssrcnote); |
1378 |
|
|
if (nobjects != 0) |
1379 |
|
|
size += sizeof(JSObjectArray) + nobjects * sizeof(JSObject *); |
1380 |
|
|
if (nupvars != 0) |
1381 |
|
|
size += sizeof(JSUpvarArray) + nupvars * sizeof(uint32); |
1382 |
|
|
if (nregexps != 0) |
1383 |
|
|
size += sizeof(JSObjectArray) + nregexps * sizeof(JSObject *); |
1384 |
|
|
if (ntrynotes != 0) |
1385 |
|
|
size += sizeof(JSTryNoteArray) + ntrynotes * sizeof(JSTryNote); |
1386 |
|
|
|
1387 |
|
|
script = (JSScript *) JS_malloc(cx, size); |
1388 |
|
|
if (!script) |
1389 |
|
|
return NULL; |
1390 |
|
|
memset(script, 0, sizeof(JSScript)); |
1391 |
|
|
script->length = length; |
1392 |
|
|
script->version = cx->version; |
1393 |
|
|
|
1394 |
|
|
cursor = (uint8 *)script + sizeof(JSScript); |
1395 |
|
|
if (nobjects != 0) { |
1396 |
|
|
script->objectsOffset = (uint8)(cursor - (uint8 *)script); |
1397 |
|
|
cursor += sizeof(JSObjectArray); |
1398 |
|
|
} |
1399 |
|
|
if (nupvars != 0) { |
1400 |
|
|
script->upvarsOffset = (uint8)(cursor - (uint8 *)script); |
1401 |
|
|
cursor += sizeof(JSUpvarArray); |
1402 |
|
|
} |
1403 |
|
|
if (nregexps != 0) { |
1404 |
|
|
script->regexpsOffset = (uint8)(cursor - (uint8 *)script); |
1405 |
|
|
cursor += sizeof(JSObjectArray); |
1406 |
|
|
} |
1407 |
|
|
if (ntrynotes != 0) { |
1408 |
|
|
script->trynotesOffset = (uint8)(cursor - (uint8 *)script); |
1409 |
|
|
cursor += sizeof(JSTryNoteArray); |
1410 |
|
|
} |
1411 |
|
|
|
1412 |
|
|
if (natoms != 0) { |
1413 |
|
|
script->atomMap.length = natoms; |
1414 |
|
|
script->atomMap.vector = (JSAtom **)cursor; |
1415 |
|
|
vectorSize = natoms * sizeof(script->atomMap.vector[0]); |
1416 |
|
|
|
1417 |
|
|
/* |
1418 |
|
|
* Clear object map's vector so the GC tracing can run when not yet |
1419 |
|
|
* all atoms are copied to the array. |
1420 |
|
|
*/ |
1421 |
|
|
memset(cursor, 0, vectorSize); |
1422 |
|
|
cursor += vectorSize; |
1423 |
|
|
} |
1424 |
|
|
|
1425 |
|
|
if (nobjects != 0) { |
1426 |
|
|
JS_SCRIPT_OBJECTS(script)->length = nobjects; |
1427 |
|
|
JS_SCRIPT_OBJECTS(script)->vector = (JSObject **)cursor; |
1428 |
|
|
vectorSize = nobjects * sizeof(JS_SCRIPT_OBJECTS(script)->vector[0]); |
1429 |
|
|
memset(cursor, 0, vectorSize); |
1430 |
|
|
cursor += vectorSize; |
1431 |
|
|
} |
1432 |
|
|
|
1433 |
|
|
if (nupvars != 0) { |
1434 |
|
|
JS_SCRIPT_UPVARS(script)->length = nupvars; |
1435 |
|
|
JS_SCRIPT_UPVARS(script)->vector = (uint32 *)cursor; |
1436 |
|
|
vectorSize = nupvars * sizeof(JS_SCRIPT_UPVARS(script)->vector[0]); |
1437 |
|
|
memset(cursor, 0, vectorSize); |
1438 |
|
|
cursor += vectorSize; |
1439 |
|
|
} |
1440 |
|
|
|
1441 |
|
|
if (nregexps != 0) { |
1442 |
|
|
JS_SCRIPT_REGEXPS(script)->length = nregexps; |
1443 |
|
|
JS_SCRIPT_REGEXPS(script)->vector = (JSObject **)cursor; |
1444 |
|
|
vectorSize = nregexps * sizeof(JS_SCRIPT_REGEXPS(script)->vector[0]); |
1445 |
|
|
memset(cursor, 0, vectorSize); |
1446 |
|
|
cursor += vectorSize; |
1447 |
|
|
} |
1448 |
|
|
|
1449 |
|
|
if (ntrynotes != 0) { |
1450 |
|
|
JS_SCRIPT_TRYNOTES(script)->length = ntrynotes; |
1451 |
|
|
JS_SCRIPT_TRYNOTES(script)->vector = (JSTryNote *)cursor; |
1452 |
|
|
vectorSize = ntrynotes * sizeof(JS_SCRIPT_TRYNOTES(script)->vector[0]); |
1453 |
|
|
#ifdef DEBUG |
1454 |
|
|
memset(cursor, 0, vectorSize); |
1455 |
|
|
#endif |
1456 |
|
|
cursor += vectorSize; |
1457 |
|
|
} |
1458 |
|
|
|
1459 |
|
|
script->code = script->main = (jsbytecode *)cursor; |
1460 |
|
|
JS_ASSERT(cursor + |
1461 |
|
|
length * sizeof(jsbytecode) + |
1462 |
|
|
nsrcnotes * sizeof(jssrcnote) == |
1463 |
|
|
(uint8 *)script + size); |
1464 |
|
|
|
1465 |
|
|
#ifdef CHECK_SCRIPT_OWNER |
1466 |
|
|
script->owner = cx->thread; |
1467 |
|
|
#endif |
1468 |
|
|
return script; |
1469 |
|
|
} |
1470 |
|
|
|
1471 |
|
|
JSScript * |
1472 |
|
|
js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg) |
1473 |
|
|
{ |
1474 |
|
|
uint32 mainLength, prologLength, nsrcnotes, nfixed; |
1475 |
|
|
JSScript *script; |
1476 |
|
|
const char *filename; |
1477 |
|
|
JSFunction *fun; |
1478 |
|
|
|
1479 |
|
|
/* The counts of indexed things must be checked during code generation. */ |
1480 |
|
|
JS_ASSERT(cg->atomList.count <= INDEX_LIMIT); |
1481 |
|
|
JS_ASSERT(cg->objectList.length <= INDEX_LIMIT); |
1482 |
|
|
JS_ASSERT(cg->regexpList.length <= INDEX_LIMIT); |
1483 |
|
|
|
1484 |
|
|
mainLength = CG_OFFSET(cg); |
1485 |
|
|
prologLength = CG_PROLOG_OFFSET(cg); |
1486 |
|
|
CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes); |
1487 |
|
|
script = js_NewScript(cx, prologLength + mainLength, nsrcnotes, |
1488 |
|
|
cg->atomList.count, cg->objectList.length, |
1489 |
|
|
cg->upvarList.count, cg->regexpList.length, |
1490 |
|
|
cg->ntrynotes); |
1491 |
|
|
if (!script) |
1492 |
|
|
return NULL; |
1493 |
|
|
|
1494 |
|
|
/* Now that we have script, error control flow must go to label bad. */ |
1495 |
|
|
script->main += prologLength; |
1496 |
|
|
memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode)); |
1497 |
|
|
memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode)); |
1498 |
siliconforks |
460 |
nfixed = (cg->flags & TCF_IN_FUNCTION) |
1499 |
|
|
? cg->fun->u.i.nvars |
1500 |
|
|
: cg->ngvars + cg->regexpList.length; |
1501 |
siliconforks |
332 |
JS_ASSERT(nfixed < SLOTNO_LIMIT); |
1502 |
|
|
script->nfixed = (uint16) nfixed; |
1503 |
|
|
js_InitAtomMap(cx, &script->atomMap, &cg->atomList); |
1504 |
|
|
|
1505 |
siliconforks |
460 |
filename = cg->compiler->tokenStream.filename; |
1506 |
siliconforks |
332 |
if (filename) { |
1507 |
|
|
script->filename = js_SaveScriptFilename(cx, filename); |
1508 |
|
|
if (!script->filename) |
1509 |
|
|
goto bad; |
1510 |
|
|
} |
1511 |
|
|
script->lineno = cg->firstLine; |
1512 |
|
|
if (script->nfixed + cg->maxStackDepth >= JS_BIT(16)) { |
1513 |
|
|
js_ReportCompileErrorNumber(cx, CG_TS(cg), NULL, JSREPORT_ERROR, |
1514 |
|
|
JSMSG_NEED_DIET, "script"); |
1515 |
|
|
goto bad; |
1516 |
|
|
} |
1517 |
|
|
script->nslots = script->nfixed + cg->maxStackDepth; |
1518 |
siliconforks |
460 |
script->staticLevel = cg->staticLevel; |
1519 |
|
|
script->principals = cg->compiler->principals; |
1520 |
siliconforks |
332 |
if (script->principals) |
1521 |
|
|
JSPRINCIPALS_HOLD(cx, script->principals); |
1522 |
|
|
|
1523 |
|
|
if (!js_FinishTakingSrcNotes(cx, cg, SCRIPT_NOTES(script))) |
1524 |
|
|
goto bad; |
1525 |
|
|
if (cg->ntrynotes != 0) |
1526 |
|
|
js_FinishTakingTryNotes(cg, JS_SCRIPT_TRYNOTES(script)); |
1527 |
|
|
if (cg->objectList.length != 0) |
1528 |
siliconforks |
460 |
cg->objectList.finish(JS_SCRIPT_OBJECTS(script)); |
1529 |
siliconforks |
332 |
if (cg->regexpList.length != 0) |
1530 |
siliconforks |
460 |
cg->regexpList.finish(JS_SCRIPT_REGEXPS(script)); |
1531 |
|
|
if (cg->flags & TCF_NO_SCRIPT_RVAL) |
1532 |
siliconforks |
332 |
script->flags |= JSSF_NO_SCRIPT_RVAL; |
1533 |
|
|
|
1534 |
|
|
if (cg->upvarList.count != 0) { |
1535 |
|
|
JS_ASSERT(cg->upvarList.count <= cg->upvarMap.length); |
1536 |
|
|
memcpy(JS_SCRIPT_UPVARS(script)->vector, cg->upvarMap.vector, |
1537 |
|
|
cg->upvarList.count * sizeof(uint32)); |
1538 |
siliconforks |
460 |
cg->upvarList.clear(); |
1539 |
siliconforks |
332 |
JS_free(cx, cg->upvarMap.vector); |
1540 |
|
|
cg->upvarMap.vector = NULL; |
1541 |
|
|
} |
1542 |
|
|
|
1543 |
|
|
/* |
1544 |
|
|
* We initialize fun->u.script to be the script constructed above |
1545 |
|
|
* so that the debugger has a valid FUN_SCRIPT(fun). |
1546 |
|
|
*/ |
1547 |
|
|
fun = NULL; |
1548 |
siliconforks |
460 |
if (cg->flags & TCF_IN_FUNCTION) { |
1549 |
|
|
fun = cg->fun; |
1550 |
siliconforks |
332 |
JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun)); |
1551 |
|
|
JS_ASSERT_IF(script->upvarsOffset != 0, |
1552 |
|
|
JS_SCRIPT_UPVARS(script)->length == fun->u.i.nupvars); |
1553 |
|
|
|
1554 |
|
|
js_FreezeLocalNames(cx, fun); |
1555 |
|
|
fun->u.i.script = script; |
1556 |
|
|
#ifdef CHECK_SCRIPT_OWNER |
1557 |
|
|
script->owner = NULL; |
1558 |
|
|
#endif |
1559 |
siliconforks |
460 |
if (cg->flags & TCF_FUN_HEAVYWEIGHT) |
1560 |
siliconforks |
332 |
fun->flags |= JSFUN_HEAVYWEIGHT; |
1561 |
|
|
} |
1562 |
|
|
|
1563 |
|
|
/* Tell the debugger about this compiled script. */ |
1564 |
|
|
js_CallNewScriptHook(cx, script, fun); |
1565 |
|
|
return script; |
1566 |
|
|
|
1567 |
|
|
bad: |
1568 |
|
|
js_DestroyScript(cx, script); |
1569 |
|
|
return NULL; |
1570 |
|
|
} |
1571 |
|
|
|
1572 |
|
|
JS_FRIEND_API(void) |
1573 |
|
|
js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun) |
1574 |
|
|
{ |
1575 |
|
|
JSNewScriptHook hook; |
1576 |
|
|
|
1577 |
|
|
hook = cx->debugHooks->newScriptHook; |
1578 |
|
|
if (hook) { |
1579 |
|
|
JS_KEEP_ATOMS(cx->runtime); |
1580 |
|
|
hook(cx, script->filename, script->lineno, script, fun, |
1581 |
|
|
cx->debugHooks->newScriptHookData); |
1582 |
|
|
JS_UNKEEP_ATOMS(cx->runtime); |
1583 |
|
|
} |
1584 |
|
|
} |
1585 |
|
|
|
1586 |
|
|
JS_FRIEND_API(void) |
1587 |
|
|
js_CallDestroyScriptHook(JSContext *cx, JSScript *script) |
1588 |
|
|
{ |
1589 |
|
|
JSDestroyScriptHook hook; |
1590 |
|
|
|
1591 |
|
|
hook = cx->debugHooks->destroyScriptHook; |
1592 |
|
|
if (hook) |
1593 |
|
|
hook(cx, script, cx->debugHooks->destroyScriptHookData); |
1594 |
|
|
} |
1595 |
|
|
|
1596 |
|
|
void |
1597 |
|
|
js_DestroyScript(JSContext *cx, JSScript *script) |
1598 |
|
|
{ |
1599 |
|
|
js_CallDestroyScriptHook(cx, script); |
1600 |
|
|
JS_ClearScriptTraps(cx, script); |
1601 |
|
|
|
1602 |
|
|
if (script->principals) |
1603 |
|
|
JSPRINCIPALS_DROP(cx, script->principals); |
1604 |
|
|
|
1605 |
|
|
if (JS_GSN_CACHE(cx).code == script->code) |
1606 |
siliconforks |
460 |
JS_PURGE_GSN_CACHE(cx); |
1607 |
siliconforks |
332 |
|
1608 |
|
|
/* |
1609 |
|
|
* The GC flushes all property caches, so no need to purge just the |
1610 |
|
|
* entries for this script. |
1611 |
|
|
* |
1612 |
siliconforks |
460 |
* JS_THREADSAFE note: js_PurgePropertyCacheForScript purges only the |
1613 |
siliconforks |
332 |
* current thread's property cache, so a script not owned by a function |
1614 |
|
|
* or object, which hands off lifetime management for that script to the |
1615 |
|
|
* GC, must be used by only one thread over its lifetime. |
1616 |
|
|
* |
1617 |
|
|
* This should be an API-compatible change, since a script is never safe |
1618 |
|
|
* against premature GC if shared among threads without a rooted object |
1619 |
|
|
* wrapping it to protect the script's mapped atoms against GC. We use |
1620 |
|
|
* script->owner to enforce this requirement via assertions. |
1621 |
|
|
*/ |
1622 |
|
|
#ifdef CHECK_SCRIPT_OWNER |
1623 |
|
|
JS_ASSERT_IF(cx->runtime->gcRunning, !script->owner); |
1624 |
|
|
#endif |
1625 |
|
|
|
1626 |
|
|
if (!cx->runtime->gcRunning) { |
1627 |
siliconforks |
460 |
JSStackFrame *fp = js_GetTopStackFrame(cx); |
1628 |
|
|
|
1629 |
|
|
if (!(fp && (fp->flags & JSFRAME_EVAL))) { |
1630 |
siliconforks |
332 |
#ifdef CHECK_SCRIPT_OWNER |
1631 |
|
|
JS_ASSERT(script->owner == cx->thread); |
1632 |
|
|
#endif |
1633 |
siliconforks |
460 |
js_PurgePropertyCacheForScript(cx, script); |
1634 |
|
|
#ifdef JS_TRACER |
1635 |
|
|
js_PurgeScriptFragments(cx, script); |
1636 |
|
|
#endif |
1637 |
siliconforks |
332 |
} |
1638 |
|
|
} |
1639 |
|
|
|
1640 |
|
|
JS_free(cx, script); |
1641 |
|
|
} |
1642 |
|
|
|
1643 |
|
|
void |
1644 |
|
|
js_TraceScript(JSTracer *trc, JSScript *script) |
1645 |
|
|
{ |
1646 |
|
|
JSAtomMap *map; |
1647 |
|
|
uintN i, length; |
1648 |
|
|
JSAtom **vector; |
1649 |
|
|
jsval v; |
1650 |
|
|
JSObjectArray *objarray; |
1651 |
|
|
|
1652 |
|
|
map = &script->atomMap; |
1653 |
|
|
length = map->length; |
1654 |
|
|
vector = map->vector; |
1655 |
|
|
for (i = 0; i < length; i++) { |
1656 |
|
|
v = ATOM_KEY(vector[i]); |
1657 |
|
|
if (JSVAL_IS_TRACEABLE(v)) { |
1658 |
|
|
JS_SET_TRACING_INDEX(trc, "atomMap", i); |
1659 |
|
|
JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v)); |
1660 |
|
|
} |
1661 |
|
|
} |
1662 |
|
|
|
1663 |
|
|
if (script->objectsOffset != 0) { |
1664 |
|
|
objarray = JS_SCRIPT_OBJECTS(script); |
1665 |
|
|
i = objarray->length; |
1666 |
|
|
do { |
1667 |
|
|
--i; |
1668 |
|
|
if (objarray->vector[i]) { |
1669 |
|
|
JS_SET_TRACING_INDEX(trc, "objects", i); |
1670 |
|
|
JS_CallTracer(trc, objarray->vector[i], JSTRACE_OBJECT); |
1671 |
|
|
} |
1672 |
|
|
} while (i != 0); |
1673 |
|
|
} |
1674 |
|
|
|
1675 |
|
|
if (script->regexpsOffset != 0) { |
1676 |
|
|
objarray = JS_SCRIPT_REGEXPS(script); |
1677 |
|
|
i = objarray->length; |
1678 |
|
|
do { |
1679 |
|
|
--i; |
1680 |
|
|
if (objarray->vector[i]) { |
1681 |
|
|
JS_SET_TRACING_INDEX(trc, "regexps", i); |
1682 |
|
|
JS_CallTracer(trc, objarray->vector[i], JSTRACE_OBJECT); |
1683 |
|
|
} |
1684 |
|
|
} while (i != 0); |
1685 |
|
|
} |
1686 |
|
|
|
1687 |
|
|
if (script->u.object) { |
1688 |
|
|
JS_SET_TRACING_NAME(trc, "object"); |
1689 |
|
|
JS_CallTracer(trc, script->u.object, JSTRACE_OBJECT); |
1690 |
|
|
} |
1691 |
|
|
|
1692 |
|
|
if (IS_GC_MARKING_TRACER(trc) && script->filename) |
1693 |
|
|
js_MarkScriptFilename(script->filename); |
1694 |
|
|
} |
1695 |
|
|
|
1696 |
|
|
typedef struct GSNCacheEntry { |
1697 |
|
|
JSDHashEntryHdr hdr; |
1698 |
|
|
jsbytecode *pc; |
1699 |
|
|
jssrcnote *sn; |
1700 |
|
|
} GSNCacheEntry; |
1701 |
|
|
|
1702 |
|
|
#define GSN_CACHE_THRESHOLD 100 |
1703 |
|
|
|
1704 |
siliconforks |
460 |
void |
1705 |
|
|
js_PurgeGSNCache(JSGSNCache *cache) |
1706 |
|
|
{ |
1707 |
|
|
cache->code = NULL; |
1708 |
|
|
if (cache->table.ops) { |
1709 |
|
|
JS_DHashTableFinish(&cache->table); |
1710 |
|
|
cache->table.ops = NULL; |
1711 |
|
|
} |
1712 |
|
|
GSN_CACHE_METER(cache, purges); |
1713 |
|
|
} |
1714 |
|
|
|
1715 |
siliconforks |
332 |
jssrcnote * |
1716 |
|
|
js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc) |
1717 |
|
|
{ |
1718 |
|
|
ptrdiff_t target, offset; |
1719 |
|
|
GSNCacheEntry *entry; |
1720 |
|
|
jssrcnote *sn, *result; |
1721 |
|
|
uintN nsrcnotes; |
1722 |
|
|
|
1723 |
|
|
|
1724 |
|
|
target = PTRDIFF(pc, script->code, jsbytecode); |
1725 |
|
|
if ((uint32)target >= script->length) |
1726 |
|
|
return NULL; |
1727 |
|
|
|
1728 |
|
|
if (JS_GSN_CACHE(cx).code == script->code) { |
1729 |
|
|
JS_METER_GSN_CACHE(cx, hits); |
1730 |
|
|
entry = (GSNCacheEntry *) |
1731 |
|
|
JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc, |
1732 |
|
|
JS_DHASH_LOOKUP); |
1733 |
|
|
return entry->sn; |
1734 |
|
|
} |
1735 |
|
|
|
1736 |
|
|
JS_METER_GSN_CACHE(cx, misses); |
1737 |
|
|
offset = 0; |
1738 |
|
|
for (sn = SCRIPT_NOTES(script); ; sn = SN_NEXT(sn)) { |
1739 |
|
|
if (SN_IS_TERMINATOR(sn)) { |
1740 |
|
|
result = NULL; |
1741 |
|
|
break; |
1742 |
|
|
} |
1743 |
|
|
offset += SN_DELTA(sn); |
1744 |
|
|
if (offset == target && SN_IS_GETTABLE(sn)) { |
1745 |
|
|
result = sn; |
1746 |
|
|
break; |
1747 |
|
|
} |
1748 |
|
|
} |
1749 |
|
|
|
1750 |
|
|
if (JS_GSN_CACHE(cx).code != script->code && |
1751 |
|
|
script->length >= GSN_CACHE_THRESHOLD) { |
1752 |
siliconforks |
460 |
JS_PURGE_GSN_CACHE(cx); |
1753 |
siliconforks |
332 |
nsrcnotes = 0; |
1754 |
|
|
for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); |
1755 |
|
|
sn = SN_NEXT(sn)) { |
1756 |
|
|
if (SN_IS_GETTABLE(sn)) |
1757 |
|
|
++nsrcnotes; |
1758 |
|
|
} |
1759 |
|
|
if (!JS_DHashTableInit(&JS_GSN_CACHE(cx).table, JS_DHashGetStubOps(), |
1760 |
|
|
NULL, sizeof(GSNCacheEntry), |
1761 |
|
|
JS_DHASH_DEFAULT_CAPACITY(nsrcnotes))) { |
1762 |
|
|
JS_GSN_CACHE(cx).table.ops = NULL; |
1763 |
|
|
} else { |
1764 |
|
|
pc = script->code; |
1765 |
|
|
for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); |
1766 |
|
|
sn = SN_NEXT(sn)) { |
1767 |
|
|
pc += SN_DELTA(sn); |
1768 |
|
|
if (SN_IS_GETTABLE(sn)) { |
1769 |
|
|
entry = (GSNCacheEntry *) |
1770 |
|
|
JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc, |
1771 |
|
|
JS_DHASH_ADD); |
1772 |
|
|
entry->pc = pc; |
1773 |
|
|
entry->sn = sn; |
1774 |
|
|
} |
1775 |
|
|
} |
1776 |
|
|
JS_GSN_CACHE(cx).code = script->code; |
1777 |
|
|
JS_METER_GSN_CACHE(cx, fills); |
1778 |
|
|
} |
1779 |
|
|
} |
1780 |
|
|
|
1781 |
|
|
return result; |
1782 |
|
|
} |
1783 |
|
|
|
1784 |
|
|
uintN |
1785 |
siliconforks |
399 |
js_FramePCToLineNumber(JSContext *cx, JSStackFrame *fp) |
1786 |
|
|
{ |
1787 |
|
|
return js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : fp->regs->pc); |
1788 |
|
|
} |
1789 |
|
|
|
1790 |
|
|
uintN |
1791 |
siliconforks |
332 |
js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) |
1792 |
|
|
{ |
1793 |
siliconforks |
460 |
JSOp op; |
1794 |
siliconforks |
332 |
JSFunction *fun; |
1795 |
|
|
uintN lineno; |
1796 |
|
|
ptrdiff_t offset, target; |
1797 |
|
|
jssrcnote *sn; |
1798 |
|
|
JSSrcNoteType type; |
1799 |
|
|
|
1800 |
|
|
/* Cope with JSStackFrame.pc value prior to entering js_Interpret. */ |
1801 |
|
|
if (!pc) |
1802 |
|
|
return 0; |
1803 |
|
|
|
1804 |
|
|
/* |
1805 |
|
|
* Special case: function definition needs no line number note because |
1806 |
|
|
* the function's script contains its starting line number. |
1807 |
|
|
*/ |
1808 |
siliconforks |
460 |
op = js_GetOpcode(cx, script, pc); |
1809 |
|
|
if (js_CodeSpec[op].format & JOF_INDEXBASE) |
1810 |
|
|
pc += js_CodeSpec[op].length; |
1811 |
siliconforks |
332 |
if (*pc == JSOP_DEFFUN) { |
1812 |
|
|
GET_FUNCTION_FROM_BYTECODE(script, pc, 0, fun); |
1813 |
|
|
return fun->u.i.script->lineno; |
1814 |
|
|
} |
1815 |
|
|
|
1816 |
|
|
/* |
1817 |
|
|
* General case: walk through source notes accumulating their deltas, |
1818 |
|
|
* keeping track of line-number notes, until we pass the note for pc's |
1819 |
|
|
* offset within script->code. |
1820 |
|
|
*/ |
1821 |
|
|
lineno = script->lineno; |
1822 |
|
|
offset = 0; |
1823 |
|
|
target = PTRDIFF(pc, script->code, jsbytecode); |
1824 |
|
|
for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { |
1825 |
|
|
offset += SN_DELTA(sn); |
1826 |
|
|
type = (JSSrcNoteType) SN_TYPE(sn); |
1827 |
|
|
if (type == SRC_SETLINE) { |
1828 |
|
|
if (offset <= target) |
1829 |
|
|
lineno = (uintN) js_GetSrcNoteOffset(sn, 0); |
1830 |
|
|
} else if (type == SRC_NEWLINE) { |
1831 |
|
|
if (offset <= target) |
1832 |
|
|
lineno++; |
1833 |
|
|
} |
1834 |
|
|
if (offset > target) |
1835 |
|
|
break; |
1836 |
|
|
} |
1837 |
|
|
return lineno; |
1838 |
|
|
} |
1839 |
|
|
|
1840 |
|
|
/* The line number limit is the same as the jssrcnote offset limit. */ |
1841 |
|
|
#define SN_LINE_LIMIT (SN_3BYTE_OFFSET_FLAG << 16) |
1842 |
|
|
|
1843 |
|
|
jsbytecode * |
1844 |
|
|
js_LineNumberToPC(JSScript *script, uintN target) |
1845 |
|
|
{ |
1846 |
|
|
ptrdiff_t offset, best; |
1847 |
|
|
uintN lineno, bestdiff, diff; |
1848 |
|
|
jssrcnote *sn; |
1849 |
|
|
JSSrcNoteType type; |
1850 |
|
|
|
1851 |
|
|
offset = 0; |
1852 |
|
|
best = -1; |
1853 |
|
|
lineno = script->lineno; |
1854 |
|
|
bestdiff = SN_LINE_LIMIT; |
1855 |
|
|
for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { |
1856 |
|
|
/* |
1857 |
|
|
* Exact-match only if offset is not in the prolog; otherwise use |
1858 |
|
|
* nearest greater-or-equal line number match. |
1859 |
|
|
*/ |
1860 |
|
|
if (lineno == target && script->code + offset >= script->main) |
1861 |
|
|
goto out; |
1862 |
|
|
if (lineno >= target) { |
1863 |
|
|
diff = lineno - target; |
1864 |
|
|
if (diff < bestdiff) { |
1865 |
|
|
bestdiff = diff; |
1866 |
|
|
best = offset; |
1867 |
|
|
} |
1868 |
|
|
} |
1869 |
|
|
offset += SN_DELTA(sn); |
1870 |
|
|
type = (JSSrcNoteType) SN_TYPE(sn); |
1871 |
|
|
if (type == SRC_SETLINE) { |
1872 |
|
|
lineno = (uintN) js_GetSrcNoteOffset(sn, 0); |
1873 |
|
|
} else if (type == SRC_NEWLINE) { |
1874 |
|
|
lineno++; |
1875 |
|
|
} |
1876 |
|
|
} |
1877 |
|
|
if (best >= 0) |
1878 |
|
|
offset = best; |
1879 |
|
|
out: |
1880 |
|
|
return script->code + offset; |
1881 |
|
|
} |
1882 |
|
|
|
1883 |
|
|
JS_FRIEND_API(uintN) |
1884 |
|
|
js_GetScriptLineExtent(JSScript *script) |
1885 |
|
|
{ |
1886 |
|
|
uintN lineno; |
1887 |
|
|
jssrcnote *sn; |
1888 |
|
|
JSSrcNoteType type; |
1889 |
|
|
|
1890 |
|
|
lineno = script->lineno; |
1891 |
|
|
for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { |
1892 |
|
|
type = (JSSrcNoteType) SN_TYPE(sn); |
1893 |
|
|
if (type == SRC_SETLINE) { |
1894 |
|
|
lineno = (uintN) js_GetSrcNoteOffset(sn, 0); |
1895 |
|
|
} else if (type == SRC_NEWLINE) { |
1896 |
|
|
lineno++; |
1897 |
|
|
} |
1898 |
|
|
} |
1899 |
|
|
return 1 + lineno - script->lineno; |
1900 |
|
|
} |
1901 |
|
|
|
1902 |
|
|
#if JS_HAS_GENERATORS |
1903 |
|
|
|
1904 |
|
|
JSBool |
1905 |
|
|
js_IsInsideTryWithFinally(JSScript *script, jsbytecode *pc) |
1906 |
|
|
{ |
1907 |
|
|
JSTryNoteArray *tarray; |
1908 |
|
|
JSTryNote *tn, *tnlimit; |
1909 |
|
|
uint32 off; |
1910 |
|
|
|
1911 |
|
|
JS_ASSERT(script->code <= pc); |
1912 |
|
|
JS_ASSERT(pc < script->code + script->length); |
1913 |
|
|
|
1914 |
|
|
if (!script->trynotesOffset != 0) |
1915 |
|
|
return JS_FALSE; |
1916 |
|
|
tarray = JS_SCRIPT_TRYNOTES(script); |
1917 |
|
|
JS_ASSERT(tarray->length != 0); |
1918 |
|
|
|
1919 |
|
|
tn = tarray->vector; |
1920 |
|
|
tnlimit = tn + tarray->length; |
1921 |
|
|
off = (uint32)(pc - script->main); |
1922 |
|
|
do { |
1923 |
|
|
if (off - tn->start < tn->length) { |
1924 |
siliconforks |
399 |
if (tn->kind == JSTRY_FINALLY) |
1925 |
siliconforks |
332 |
return JS_TRUE; |
1926 |
siliconforks |
399 |
JS_ASSERT(tn->kind == JSTRY_CATCH); |
1927 |
siliconforks |
332 |
} |
1928 |
|
|
} while (++tn != tnlimit); |
1929 |
|
|
return JS_FALSE; |
1930 |
|
|
} |
1931 |
|
|
|
1932 |
|
|
#endif |