|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 /* |
|
8 * JS script operations. |
|
9 */ |
|
10 |
|
11 #include "jsscriptinlines.h" |
|
12 |
|
13 #include "mozilla/DebugOnly.h" |
|
14 #include "mozilla/MathAlgorithms.h" |
|
15 #include "mozilla/MemoryReporting.h" |
|
16 #include "mozilla/PodOperations.h" |
|
17 |
|
18 #include <string.h> |
|
19 |
|
20 #include "jsapi.h" |
|
21 #include "jsatom.h" |
|
22 #include "jscntxt.h" |
|
23 #include "jsfun.h" |
|
24 #include "jsgc.h" |
|
25 #include "jsobj.h" |
|
26 #include "jsopcode.h" |
|
27 #include "jsprf.h" |
|
28 #include "jstypes.h" |
|
29 #include "jsutil.h" |
|
30 #include "jswrapper.h" |
|
31 |
|
32 #include "frontend/BytecodeCompiler.h" |
|
33 #include "frontend/BytecodeEmitter.h" |
|
34 #include "frontend/SharedContext.h" |
|
35 #include "gc/Marking.h" |
|
36 #include "jit/BaselineJIT.h" |
|
37 #include "jit/IonCode.h" |
|
38 #include "js/MemoryMetrics.h" |
|
39 #include "js/OldDebugAPI.h" |
|
40 #include "js/Utility.h" |
|
41 #include "vm/ArgumentsObject.h" |
|
42 #include "vm/Compression.h" |
|
43 #include "vm/Debugger.h" |
|
44 #include "vm/Opcodes.h" |
|
45 #include "vm/SelfHosting.h" |
|
46 #include "vm/Shape.h" |
|
47 #include "vm/Xdr.h" |
|
48 |
|
49 #include "jsfuninlines.h" |
|
50 #include "jsinferinlines.h" |
|
51 #include "jsobjinlines.h" |
|
52 |
|
53 #include "vm/ScopeObject-inl.h" |
|
54 #include "vm/Stack-inl.h" |
|
55 |
|
56 using namespace js; |
|
57 using namespace js::gc; |
|
58 using namespace js::frontend; |
|
59 |
|
60 using mozilla::PodCopy; |
|
61 using mozilla::PodZero; |
|
62 using mozilla::RotateLeft; |
|
63 |
|
64 typedef Rooted<GlobalObject *> RootedGlobalObject; |
|
65 |
|
66 /* static */ uint32_t |
|
67 Bindings::argumentsVarIndex(ExclusiveContext *cx, InternalBindingsHandle bindings) |
|
68 { |
|
69 HandlePropertyName arguments = cx->names().arguments; |
|
70 BindingIter bi(bindings); |
|
71 while (bi->name() != arguments) |
|
72 bi++; |
|
73 return bi.frameIndex(); |
|
74 } |
|
75 |
|
76 bool |
|
77 Bindings::initWithTemporaryStorage(ExclusiveContext *cx, InternalBindingsHandle self, |
|
78 unsigned numArgs, uint32_t numVars, |
|
79 Binding *bindingArray, uint32_t numBlockScoped) |
|
80 { |
|
81 JS_ASSERT(!self->callObjShape_); |
|
82 JS_ASSERT(self->bindingArrayAndFlag_ == TEMPORARY_STORAGE_BIT); |
|
83 JS_ASSERT(!(uintptr_t(bindingArray) & TEMPORARY_STORAGE_BIT)); |
|
84 JS_ASSERT(numArgs <= ARGC_LIMIT); |
|
85 JS_ASSERT(numVars <= LOCALNO_LIMIT); |
|
86 JS_ASSERT(numBlockScoped <= LOCALNO_LIMIT); |
|
87 JS_ASSERT(numVars <= LOCALNO_LIMIT - numBlockScoped); |
|
88 JS_ASSERT(UINT32_MAX - numArgs >= numVars + numBlockScoped); |
|
89 |
|
90 self->bindingArrayAndFlag_ = uintptr_t(bindingArray) | TEMPORARY_STORAGE_BIT; |
|
91 self->numArgs_ = numArgs; |
|
92 self->numVars_ = numVars; |
|
93 self->numBlockScoped_ = numBlockScoped; |
|
94 |
|
95 // Get the initial shape to use when creating CallObjects for this script. |
|
96 // After creation, a CallObject's shape may change completely (via direct eval() or |
|
97 // other operations that mutate the lexical scope). However, since the |
|
98 // lexical bindings added to the initial shape are permanent and the |
|
99 // allocKind/nfixed of a CallObject cannot change, one may assume that the |
|
100 // slot location (whether in the fixed or dynamic slots) of a variable is |
|
101 // the same as in the initial shape. (This is assumed by the interpreter and |
|
102 // JITs when interpreting/compiling aliasedvar ops.) |
|
103 |
|
104 // Since unaliased variables are, by definition, only accessed by local |
|
105 // operations and never through the scope chain, only give shapes to |
|
106 // aliased variables. While the debugger may observe any scope object at |
|
107 // any time, such accesses are mediated by DebugScopeProxy (see |
|
108 // DebugScopeProxy::handleUnaliasedAccess). |
|
109 uint32_t nslots = CallObject::RESERVED_SLOTS; |
|
110 for (BindingIter bi(self); bi; bi++) { |
|
111 if (bi->aliased()) |
|
112 nslots++; |
|
113 } |
|
114 |
|
115 // Put as many of nslots inline into the object header as possible. |
|
116 uint32_t nfixed = gc::GetGCKindSlots(gc::GetGCObjectKind(nslots)); |
|
117 |
|
118 // Start with the empty shape and then append one shape per aliased binding. |
|
119 RootedShape shape(cx, |
|
120 EmptyShape::getInitialShape(cx, &CallObject::class_, nullptr, nullptr, nullptr, |
|
121 nfixed, BaseShape::VAROBJ | BaseShape::DELEGATE)); |
|
122 if (!shape) |
|
123 return false; |
|
124 |
|
125 #ifdef DEBUG |
|
126 HashSet<PropertyName *> added(cx); |
|
127 if (!added.init()) |
|
128 return false; |
|
129 #endif |
|
130 |
|
131 uint32_t slot = CallObject::RESERVED_SLOTS; |
|
132 for (BindingIter bi(self); bi; bi++) { |
|
133 if (!bi->aliased()) |
|
134 continue; |
|
135 |
|
136 #ifdef DEBUG |
|
137 // The caller ensures no duplicate aliased names. |
|
138 JS_ASSERT(!added.has(bi->name())); |
|
139 if (!added.put(bi->name())) |
|
140 return false; |
|
141 #endif |
|
142 |
|
143 StackBaseShape stackBase(cx, &CallObject::class_, nullptr, nullptr, |
|
144 BaseShape::VAROBJ | BaseShape::DELEGATE); |
|
145 |
|
146 UnownedBaseShape *base = BaseShape::getUnowned(cx, stackBase); |
|
147 if (!base) |
|
148 return false; |
|
149 |
|
150 unsigned attrs = JSPROP_PERMANENT | |
|
151 JSPROP_ENUMERATE | |
|
152 (bi->kind() == Binding::CONSTANT ? JSPROP_READONLY : 0); |
|
153 StackShape child(base, NameToId(bi->name()), slot, attrs, 0); |
|
154 |
|
155 shape = cx->compartment()->propertyTree.getChild(cx, shape, child); |
|
156 if (!shape) |
|
157 return false; |
|
158 |
|
159 JS_ASSERT(slot < nslots); |
|
160 slot++; |
|
161 } |
|
162 JS_ASSERT(slot == nslots); |
|
163 |
|
164 JS_ASSERT(!shape->inDictionary()); |
|
165 self->callObjShape_.init(shape); |
|
166 return true; |
|
167 } |
|
168 |
|
169 uint8_t * |
|
170 Bindings::switchToScriptStorage(Binding *newBindingArray) |
|
171 { |
|
172 JS_ASSERT(bindingArrayUsingTemporaryStorage()); |
|
173 JS_ASSERT(!(uintptr_t(newBindingArray) & TEMPORARY_STORAGE_BIT)); |
|
174 |
|
175 if (count() > 0) |
|
176 PodCopy(newBindingArray, bindingArray(), count()); |
|
177 bindingArrayAndFlag_ = uintptr_t(newBindingArray); |
|
178 return reinterpret_cast<uint8_t *>(newBindingArray + count()); |
|
179 } |
|
180 |
|
181 bool |
|
182 Bindings::clone(JSContext *cx, InternalBindingsHandle self, |
|
183 uint8_t *dstScriptData, HandleScript srcScript) |
|
184 { |
|
185 /* The clone has the same bindingArray_ offset as 'src'. */ |
|
186 Bindings &src = srcScript->bindings; |
|
187 ptrdiff_t off = (uint8_t *)src.bindingArray() - srcScript->data; |
|
188 JS_ASSERT(off >= 0); |
|
189 JS_ASSERT(size_t(off) <= srcScript->dataSize()); |
|
190 Binding *dstPackedBindings = (Binding *)(dstScriptData + off); |
|
191 |
|
192 /* |
|
193 * Since atoms are shareable throughout the runtime, we can simply copy |
|
194 * the source's bindingArray directly. |
|
195 */ |
|
196 if (!initWithTemporaryStorage(cx, self, src.numArgs(), src.numVars(), src.bindingArray(), |
|
197 src.numBlockScoped())) |
|
198 return false; |
|
199 self->switchToScriptStorage(dstPackedBindings); |
|
200 return true; |
|
201 } |
|
202 |
|
203 /* static */ Bindings |
|
204 GCMethods<Bindings>::initial() |
|
205 { |
|
206 return Bindings(); |
|
207 } |
|
208 |
|
209 template<XDRMode mode> |
|
210 static bool |
|
211 XDRScriptBindings(XDRState<mode> *xdr, LifoAllocScope &las, unsigned numArgs, uint32_t numVars, |
|
212 HandleScript script, unsigned numBlockScoped) |
|
213 { |
|
214 JSContext *cx = xdr->cx(); |
|
215 |
|
216 if (mode == XDR_ENCODE) { |
|
217 for (BindingIter bi(script); bi; bi++) { |
|
218 RootedAtom atom(cx, bi->name()); |
|
219 if (!XDRAtom(xdr, &atom)) |
|
220 return false; |
|
221 } |
|
222 |
|
223 for (BindingIter bi(script); bi; bi++) { |
|
224 uint8_t u8 = (uint8_t(bi->kind()) << 1) | uint8_t(bi->aliased()); |
|
225 if (!xdr->codeUint8(&u8)) |
|
226 return false; |
|
227 } |
|
228 } else { |
|
229 uint32_t nameCount = numArgs + numVars; |
|
230 |
|
231 AutoValueVector atoms(cx); |
|
232 if (!atoms.resize(nameCount)) |
|
233 return false; |
|
234 for (uint32_t i = 0; i < nameCount; i++) { |
|
235 RootedAtom atom(cx); |
|
236 if (!XDRAtom(xdr, &atom)) |
|
237 return false; |
|
238 atoms[i] = StringValue(atom); |
|
239 } |
|
240 |
|
241 Binding *bindingArray = las.alloc().newArrayUninitialized<Binding>(nameCount); |
|
242 if (!bindingArray) |
|
243 return false; |
|
244 for (uint32_t i = 0; i < nameCount; i++) { |
|
245 uint8_t u8; |
|
246 if (!xdr->codeUint8(&u8)) |
|
247 return false; |
|
248 |
|
249 PropertyName *name = atoms[i].toString()->asAtom().asPropertyName(); |
|
250 Binding::Kind kind = Binding::Kind(u8 >> 1); |
|
251 bool aliased = bool(u8 & 1); |
|
252 |
|
253 bindingArray[i] = Binding(name, kind, aliased); |
|
254 } |
|
255 |
|
256 InternalBindingsHandle bindings(script, &script->bindings); |
|
257 if (!Bindings::initWithTemporaryStorage(cx, bindings, numArgs, numVars, bindingArray, |
|
258 numBlockScoped)) |
|
259 return false; |
|
260 } |
|
261 |
|
262 return true; |
|
263 } |
|
264 |
|
265 bool |
|
266 Bindings::bindingIsAliased(uint32_t bindingIndex) |
|
267 { |
|
268 JS_ASSERT(bindingIndex < count()); |
|
269 return bindingArray()[bindingIndex].aliased(); |
|
270 } |
|
271 |
|
272 void |
|
273 Bindings::trace(JSTracer *trc) |
|
274 { |
|
275 if (callObjShape_) |
|
276 MarkShape(trc, &callObjShape_, "callObjShape"); |
|
277 |
|
278 /* |
|
279 * As the comment in Bindings explains, bindingsArray may point into freed |
|
280 * storage when bindingArrayUsingTemporaryStorage so we don't mark it. |
|
281 * Note: during compilation, atoms are already kept alive by gcKeepAtoms. |
|
282 */ |
|
283 if (bindingArrayUsingTemporaryStorage()) |
|
284 return; |
|
285 |
|
286 for (Binding *b = bindingArray(), *end = b + count(); b != end; b++) { |
|
287 PropertyName *name = b->name(); |
|
288 MarkStringUnbarriered(trc, &name, "bindingArray"); |
|
289 } |
|
290 } |
|
291 |
|
292 bool |
|
293 js::FillBindingVector(HandleScript fromScript, BindingVector *vec) |
|
294 { |
|
295 for (BindingIter bi(fromScript); bi; bi++) { |
|
296 if (!vec->append(*bi)) |
|
297 return false; |
|
298 } |
|
299 |
|
300 return true; |
|
301 } |
|
302 |
|
303 template<XDRMode mode> |
|
304 bool |
|
305 js::XDRScriptConst(XDRState<mode> *xdr, MutableHandleValue vp) |
|
306 { |
|
307 JSContext *cx = xdr->cx(); |
|
308 |
|
309 /* |
|
310 * A script constant can be an arbitrary primitive value as they are used |
|
311 * to implement JSOP_LOOKUPSWITCH. But they cannot be objects, see |
|
312 * bug 407186. |
|
313 */ |
|
314 enum ConstTag { |
|
315 SCRIPT_INT = 0, |
|
316 SCRIPT_DOUBLE = 1, |
|
317 SCRIPT_ATOM = 2, |
|
318 SCRIPT_TRUE = 3, |
|
319 SCRIPT_FALSE = 4, |
|
320 SCRIPT_NULL = 5, |
|
321 SCRIPT_OBJECT = 6, |
|
322 SCRIPT_VOID = 7, |
|
323 SCRIPT_HOLE = 8 |
|
324 }; |
|
325 |
|
326 uint32_t tag; |
|
327 if (mode == XDR_ENCODE) { |
|
328 if (vp.isInt32()) { |
|
329 tag = SCRIPT_INT; |
|
330 } else if (vp.isDouble()) { |
|
331 tag = SCRIPT_DOUBLE; |
|
332 } else if (vp.isString()) { |
|
333 tag = SCRIPT_ATOM; |
|
334 } else if (vp.isTrue()) { |
|
335 tag = SCRIPT_TRUE; |
|
336 } else if (vp.isFalse()) { |
|
337 tag = SCRIPT_FALSE; |
|
338 } else if (vp.isNull()) { |
|
339 tag = SCRIPT_NULL; |
|
340 } else if (vp.isObject()) { |
|
341 tag = SCRIPT_OBJECT; |
|
342 } else if (vp.isMagic(JS_ELEMENTS_HOLE)) { |
|
343 tag = SCRIPT_HOLE; |
|
344 } else { |
|
345 JS_ASSERT(vp.isUndefined()); |
|
346 tag = SCRIPT_VOID; |
|
347 } |
|
348 } |
|
349 |
|
350 if (!xdr->codeUint32(&tag)) |
|
351 return false; |
|
352 |
|
353 switch (tag) { |
|
354 case SCRIPT_INT: { |
|
355 uint32_t i; |
|
356 if (mode == XDR_ENCODE) |
|
357 i = uint32_t(vp.toInt32()); |
|
358 if (!xdr->codeUint32(&i)) |
|
359 return false; |
|
360 if (mode == XDR_DECODE) |
|
361 vp.set(Int32Value(int32_t(i))); |
|
362 break; |
|
363 } |
|
364 case SCRIPT_DOUBLE: { |
|
365 double d; |
|
366 if (mode == XDR_ENCODE) |
|
367 d = vp.toDouble(); |
|
368 if (!xdr->codeDouble(&d)) |
|
369 return false; |
|
370 if (mode == XDR_DECODE) |
|
371 vp.set(DoubleValue(d)); |
|
372 break; |
|
373 } |
|
374 case SCRIPT_ATOM: { |
|
375 RootedAtom atom(cx); |
|
376 if (mode == XDR_ENCODE) |
|
377 atom = &vp.toString()->asAtom(); |
|
378 if (!XDRAtom(xdr, &atom)) |
|
379 return false; |
|
380 if (mode == XDR_DECODE) |
|
381 vp.set(StringValue(atom)); |
|
382 break; |
|
383 } |
|
384 case SCRIPT_TRUE: |
|
385 if (mode == XDR_DECODE) |
|
386 vp.set(BooleanValue(true)); |
|
387 break; |
|
388 case SCRIPT_FALSE: |
|
389 if (mode == XDR_DECODE) |
|
390 vp.set(BooleanValue(false)); |
|
391 break; |
|
392 case SCRIPT_NULL: |
|
393 if (mode == XDR_DECODE) |
|
394 vp.set(NullValue()); |
|
395 break; |
|
396 case SCRIPT_OBJECT: { |
|
397 RootedObject obj(cx); |
|
398 if (mode == XDR_ENCODE) |
|
399 obj = &vp.toObject(); |
|
400 |
|
401 if (!XDRObjectLiteral(xdr, &obj)) |
|
402 return false; |
|
403 |
|
404 if (mode == XDR_DECODE) |
|
405 vp.setObject(*obj); |
|
406 break; |
|
407 } |
|
408 case SCRIPT_VOID: |
|
409 if (mode == XDR_DECODE) |
|
410 vp.set(UndefinedValue()); |
|
411 break; |
|
412 case SCRIPT_HOLE: |
|
413 if (mode == XDR_DECODE) |
|
414 vp.setMagic(JS_ELEMENTS_HOLE); |
|
415 break; |
|
416 } |
|
417 return true; |
|
418 } |
|
419 |
|
420 template bool |
|
421 js::XDRScriptConst(XDRState<XDR_ENCODE> *, MutableHandleValue); |
|
422 |
|
423 template bool |
|
424 js::XDRScriptConst(XDRState<XDR_DECODE> *, MutableHandleValue); |
|
425 |
|
426 // Code LazyScript's free variables. |
|
427 template<XDRMode mode> |
|
428 static bool |
|
429 XDRLazyFreeVariables(XDRState<mode> *xdr, MutableHandle<LazyScript *> lazy) |
|
430 { |
|
431 JSContext *cx = xdr->cx(); |
|
432 RootedAtom atom(cx); |
|
433 HeapPtrAtom *freeVariables = lazy->freeVariables(); |
|
434 size_t numFreeVariables = lazy->numFreeVariables(); |
|
435 for (size_t i = 0; i < numFreeVariables; i++) { |
|
436 if (mode == XDR_ENCODE) |
|
437 atom = freeVariables[i]; |
|
438 |
|
439 if (!XDRAtom(xdr, &atom)) |
|
440 return false; |
|
441 |
|
442 if (mode == XDR_DECODE) |
|
443 freeVariables[i] = atom; |
|
444 } |
|
445 |
|
446 return true; |
|
447 } |
|
448 |
|
449 // Code the missing part needed to re-create a LazyScript from a JSScript. |
|
450 template<XDRMode mode> |
|
451 static bool |
|
452 XDRRelazificationInfo(XDRState<mode> *xdr, HandleFunction fun, HandleScript script, |
|
453 MutableHandle<LazyScript *> lazy) |
|
454 { |
|
455 MOZ_ASSERT_IF(mode == XDR_ENCODE, script->isRelazifiable() && script->maybeLazyScript()); |
|
456 MOZ_ASSERT_IF(mode == XDR_ENCODE, !lazy->numInnerFunctions()); |
|
457 |
|
458 JSContext *cx = xdr->cx(); |
|
459 |
|
460 uint64_t packedFields; |
|
461 { |
|
462 uint32_t begin = script->sourceStart(); |
|
463 uint32_t end = script->sourceEnd(); |
|
464 uint32_t lineno = script->lineno(); |
|
465 uint32_t column = script->column(); |
|
466 |
|
467 if (mode == XDR_ENCODE) { |
|
468 packedFields = lazy->packedFields(); |
|
469 MOZ_ASSERT(begin == lazy->begin()); |
|
470 MOZ_ASSERT(end == lazy->end()); |
|
471 MOZ_ASSERT(lineno == lazy->lineno()); |
|
472 MOZ_ASSERT(column == lazy->column()); |
|
473 } |
|
474 |
|
475 if (!xdr->codeUint64(&packedFields)) |
|
476 return false; |
|
477 |
|
478 if (mode == XDR_DECODE) { |
|
479 lazy.set(LazyScript::Create(cx, fun, packedFields, begin, end, lineno, column)); |
|
480 |
|
481 // As opposed to XDRLazyScript, we need to restore the runtime bits |
|
482 // of the script, as we are trying to match the fact this function |
|
483 // has already been parsed and that it would need to be re-lazified. |
|
484 lazy->initRuntimeFields(packedFields); |
|
485 } |
|
486 } |
|
487 |
|
488 // Code free variables. |
|
489 if (!XDRLazyFreeVariables(xdr, lazy)) |
|
490 return false; |
|
491 |
|
492 return true; |
|
493 } |
|
494 |
|
495 static inline uint32_t |
|
496 FindScopeObjectIndex(JSScript *script, NestedScopeObject &scope) |
|
497 { |
|
498 ObjectArray *objects = script->objects(); |
|
499 HeapPtrObject *vector = objects->vector; |
|
500 unsigned length = objects->length; |
|
501 for (unsigned i = 0; i < length; ++i) { |
|
502 if (vector[i] == &scope) |
|
503 return i; |
|
504 } |
|
505 |
|
506 MOZ_ASSUME_UNREACHABLE("Scope not found"); |
|
507 } |
|
508 |
|
509 static bool |
|
510 SaveSharedScriptData(ExclusiveContext *, Handle<JSScript *>, SharedScriptData *, uint32_t); |
|
511 |
|
512 enum XDRClassKind { |
|
513 CK_BlockObject = 0, |
|
514 CK_WithObject = 1, |
|
515 CK_JSFunction = 2, |
|
516 CK_JSObject = 3 |
|
517 }; |
|
518 |
|
519 template<XDRMode mode> |
|
520 bool |
|
521 js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript, |
|
522 HandleFunction fun, MutableHandleScript scriptp) |
|
523 { |
|
524 /* NB: Keep this in sync with CloneScript. */ |
|
525 |
|
526 enum ScriptBits { |
|
527 NoScriptRval, |
|
528 SavedCallerFun, |
|
529 Strict, |
|
530 ContainsDynamicNameAccess, |
|
531 FunHasExtensibleScope, |
|
532 FunNeedsDeclEnvObject, |
|
533 FunHasAnyAliasedFormal, |
|
534 ArgumentsHasVarBinding, |
|
535 NeedsArgsObj, |
|
536 IsGeneratorExp, |
|
537 IsLegacyGenerator, |
|
538 IsStarGenerator, |
|
539 OwnSource, |
|
540 ExplicitUseStrict, |
|
541 SelfHosted, |
|
542 IsCompileAndGo, |
|
543 HasSingleton, |
|
544 TreatAsRunOnce, |
|
545 HasLazyScript |
|
546 }; |
|
547 |
|
548 uint32_t length, lineno, column, nslots, staticLevel; |
|
549 uint32_t natoms, nsrcnotes, i; |
|
550 uint32_t nconsts, nobjects, nregexps, ntrynotes, nblockscopes; |
|
551 uint32_t prologLength, version; |
|
552 uint32_t funLength = 0; |
|
553 uint32_t nTypeSets = 0; |
|
554 uint32_t scriptBits = 0; |
|
555 |
|
556 JSContext *cx = xdr->cx(); |
|
557 RootedScript script(cx); |
|
558 natoms = nsrcnotes = 0; |
|
559 nconsts = nobjects = nregexps = ntrynotes = nblockscopes = 0; |
|
560 |
|
561 /* XDR arguments and vars. */ |
|
562 uint16_t nargs = 0; |
|
563 uint16_t nblocklocals = 0; |
|
564 uint32_t nvars = 0; |
|
565 if (mode == XDR_ENCODE) { |
|
566 script = scriptp.get(); |
|
567 JS_ASSERT_IF(enclosingScript, enclosingScript->compartment() == script->compartment()); |
|
568 |
|
569 nargs = script->bindings.numArgs(); |
|
570 nblocklocals = script->bindings.numBlockScoped(); |
|
571 nvars = script->bindings.numVars(); |
|
572 } |
|
573 if (!xdr->codeUint16(&nargs)) |
|
574 return false; |
|
575 if (!xdr->codeUint16(&nblocklocals)) |
|
576 return false; |
|
577 if (!xdr->codeUint32(&nvars)) |
|
578 return false; |
|
579 |
|
580 if (mode == XDR_ENCODE) |
|
581 length = script->length(); |
|
582 if (!xdr->codeUint32(&length)) |
|
583 return false; |
|
584 |
|
585 if (mode == XDR_ENCODE) { |
|
586 prologLength = script->mainOffset(); |
|
587 JS_ASSERT(script->getVersion() != JSVERSION_UNKNOWN); |
|
588 version = script->getVersion(); |
|
589 lineno = script->lineno(); |
|
590 column = script->column(); |
|
591 nslots = script->nslots(); |
|
592 staticLevel = script->staticLevel(); |
|
593 natoms = script->natoms(); |
|
594 |
|
595 nsrcnotes = script->numNotes(); |
|
596 |
|
597 if (script->hasConsts()) |
|
598 nconsts = script->consts()->length; |
|
599 if (script->hasObjects()) |
|
600 nobjects = script->objects()->length; |
|
601 if (script->hasRegexps()) |
|
602 nregexps = script->regexps()->length; |
|
603 if (script->hasTrynotes()) |
|
604 ntrynotes = script->trynotes()->length; |
|
605 if (script->hasBlockScopes()) |
|
606 nblockscopes = script->blockScopes()->length; |
|
607 |
|
608 nTypeSets = script->nTypeSets(); |
|
609 funLength = script->funLength(); |
|
610 |
|
611 if (script->noScriptRval()) |
|
612 scriptBits |= (1 << NoScriptRval); |
|
613 if (script->savedCallerFun()) |
|
614 scriptBits |= (1 << SavedCallerFun); |
|
615 if (script->strict()) |
|
616 scriptBits |= (1 << Strict); |
|
617 if (script->explicitUseStrict()) |
|
618 scriptBits |= (1 << ExplicitUseStrict); |
|
619 if (script->selfHosted()) |
|
620 scriptBits |= (1 << SelfHosted); |
|
621 if (script->bindingsAccessedDynamically()) |
|
622 scriptBits |= (1 << ContainsDynamicNameAccess); |
|
623 if (script->funHasExtensibleScope()) |
|
624 scriptBits |= (1 << FunHasExtensibleScope); |
|
625 if (script->funNeedsDeclEnvObject()) |
|
626 scriptBits |= (1 << FunNeedsDeclEnvObject); |
|
627 if (script->funHasAnyAliasedFormal()) |
|
628 scriptBits |= (1 << FunHasAnyAliasedFormal); |
|
629 if (script->argumentsHasVarBinding()) |
|
630 scriptBits |= (1 << ArgumentsHasVarBinding); |
|
631 if (script->analyzedArgsUsage() && script->needsArgsObj()) |
|
632 scriptBits |= (1 << NeedsArgsObj); |
|
633 if (!enclosingScript || enclosingScript->scriptSource() != script->scriptSource()) |
|
634 scriptBits |= (1 << OwnSource); |
|
635 if (script->isGeneratorExp()) |
|
636 scriptBits |= (1 << IsGeneratorExp); |
|
637 if (script->isLegacyGenerator()) |
|
638 scriptBits |= (1 << IsLegacyGenerator); |
|
639 if (script->isStarGenerator()) |
|
640 scriptBits |= (1 << IsStarGenerator); |
|
641 if (script->compileAndGo()) |
|
642 scriptBits |= (1 << IsCompileAndGo); |
|
643 if (script->hasSingletons()) |
|
644 scriptBits |= (1 << HasSingleton); |
|
645 if (script->treatAsRunOnce()) |
|
646 scriptBits |= (1 << TreatAsRunOnce); |
|
647 if (script->isRelazifiable()) |
|
648 scriptBits |= (1 << HasLazyScript); |
|
649 } |
|
650 |
|
651 if (!xdr->codeUint32(&prologLength)) |
|
652 return false; |
|
653 if (!xdr->codeUint32(&version)) |
|
654 return false; |
|
655 |
|
656 // To fuse allocations, we need lengths of all embedded arrays early. |
|
657 if (!xdr->codeUint32(&natoms)) |
|
658 return false; |
|
659 if (!xdr->codeUint32(&nsrcnotes)) |
|
660 return false; |
|
661 if (!xdr->codeUint32(&nconsts)) |
|
662 return false; |
|
663 if (!xdr->codeUint32(&nobjects)) |
|
664 return false; |
|
665 if (!xdr->codeUint32(&nregexps)) |
|
666 return false; |
|
667 if (!xdr->codeUint32(&ntrynotes)) |
|
668 return false; |
|
669 if (!xdr->codeUint32(&nblockscopes)) |
|
670 return false; |
|
671 if (!xdr->codeUint32(&nTypeSets)) |
|
672 return false; |
|
673 if (!xdr->codeUint32(&funLength)) |
|
674 return false; |
|
675 if (!xdr->codeUint32(&scriptBits)) |
|
676 return false; |
|
677 |
|
678 if (mode == XDR_DECODE) { |
|
679 JSVersion version_ = JSVersion(version); |
|
680 JS_ASSERT((version_ & VersionFlags::MASK) == unsigned(version_)); |
|
681 |
|
682 // staticLevel is set below. |
|
683 CompileOptions options(cx); |
|
684 options.setVersion(version_) |
|
685 .setNoScriptRval(!!(scriptBits & (1 << NoScriptRval))) |
|
686 .setSelfHostingMode(!!(scriptBits & (1 << SelfHosted))); |
|
687 RootedScriptSource sourceObject(cx); |
|
688 if (scriptBits & (1 << OwnSource)) { |
|
689 ScriptSource *ss = cx->new_<ScriptSource>(); |
|
690 if (!ss) |
|
691 return false; |
|
692 ScriptSourceHolder ssHolder(ss); |
|
693 |
|
694 /* |
|
695 * We use this CompileOptions only to initialize the |
|
696 * ScriptSourceObject. Most CompileOptions fields aren't used by |
|
697 * ScriptSourceObject, and those that are (element; elementAttributeName) |
|
698 * aren't preserved by XDR. So this can be simple. |
|
699 */ |
|
700 CompileOptions options(cx); |
|
701 options.setOriginPrincipals(xdr->originPrincipals()); |
|
702 ss->initFromOptions(cx, options); |
|
703 sourceObject = ScriptSourceObject::create(cx, ss, options); |
|
704 if (!sourceObject) |
|
705 return false; |
|
706 } else { |
|
707 JS_ASSERT(enclosingScript); |
|
708 // When decoding, all the scripts and the script source object |
|
709 // are in the same compartment, so the script's source object |
|
710 // should never be a cross-compartment wrapper. |
|
711 JS_ASSERT(enclosingScript->sourceObject()->is<ScriptSourceObject>()); |
|
712 sourceObject = &enclosingScript->sourceObject()->as<ScriptSourceObject>(); |
|
713 } |
|
714 script = JSScript::Create(cx, enclosingScope, !!(scriptBits & (1 << SavedCallerFun)), |
|
715 options, /* staticLevel = */ 0, sourceObject, 0, 0); |
|
716 if (!script) |
|
717 return false; |
|
718 } |
|
719 |
|
720 /* JSScript::partiallyInit assumes script->bindings is fully initialized. */ |
|
721 LifoAllocScope las(&cx->tempLifoAlloc()); |
|
722 if (!XDRScriptBindings(xdr, las, nargs, nvars, script, nblocklocals)) |
|
723 return false; |
|
724 |
|
725 if (mode == XDR_DECODE) { |
|
726 if (!JSScript::partiallyInit(cx, script, nconsts, nobjects, nregexps, ntrynotes, |
|
727 nblockscopes, nTypeSets)) |
|
728 { |
|
729 return false; |
|
730 } |
|
731 |
|
732 JS_ASSERT(!script->mainOffset()); |
|
733 script->mainOffset_ = prologLength; |
|
734 script->setLength(length); |
|
735 script->funLength_ = funLength; |
|
736 |
|
737 scriptp.set(script); |
|
738 |
|
739 if (scriptBits & (1 << Strict)) |
|
740 script->strict_ = true; |
|
741 if (scriptBits & (1 << ExplicitUseStrict)) |
|
742 script->explicitUseStrict_ = true; |
|
743 if (scriptBits & (1 << ContainsDynamicNameAccess)) |
|
744 script->bindingsAccessedDynamically_ = true; |
|
745 if (scriptBits & (1 << FunHasExtensibleScope)) |
|
746 script->funHasExtensibleScope_ = true; |
|
747 if (scriptBits & (1 << FunNeedsDeclEnvObject)) |
|
748 script->funNeedsDeclEnvObject_ = true; |
|
749 if (scriptBits & (1 << FunHasAnyAliasedFormal)) |
|
750 script->funHasAnyAliasedFormal_ = true; |
|
751 if (scriptBits & (1 << ArgumentsHasVarBinding)) |
|
752 script->setArgumentsHasVarBinding(); |
|
753 if (scriptBits & (1 << NeedsArgsObj)) |
|
754 script->setNeedsArgsObj(true); |
|
755 if (scriptBits & (1 << IsGeneratorExp)) |
|
756 script->isGeneratorExp_ = true; |
|
757 if (scriptBits & (1 << IsCompileAndGo)) |
|
758 script->compileAndGo_ = true; |
|
759 if (scriptBits & (1 << HasSingleton)) |
|
760 script->hasSingletons_ = true; |
|
761 if (scriptBits & (1 << TreatAsRunOnce)) |
|
762 script->treatAsRunOnce_ = true; |
|
763 |
|
764 if (scriptBits & (1 << IsLegacyGenerator)) { |
|
765 JS_ASSERT(!(scriptBits & (1 << IsStarGenerator))); |
|
766 script->setGeneratorKind(LegacyGenerator); |
|
767 } else if (scriptBits & (1 << IsStarGenerator)) |
|
768 script->setGeneratorKind(StarGenerator); |
|
769 } |
|
770 |
|
771 JS_STATIC_ASSERT(sizeof(jsbytecode) == 1); |
|
772 JS_STATIC_ASSERT(sizeof(jssrcnote) == 1); |
|
773 |
|
774 if (scriptBits & (1 << OwnSource)) { |
|
775 if (!script->scriptSource()->performXDR<mode>(xdr)) |
|
776 return false; |
|
777 } |
|
778 if (!xdr->codeUint32(&script->sourceStart_)) |
|
779 return false; |
|
780 if (!xdr->codeUint32(&script->sourceEnd_)) |
|
781 return false; |
|
782 |
|
783 if (!xdr->codeUint32(&lineno) || |
|
784 !xdr->codeUint32(&column) || |
|
785 !xdr->codeUint32(&nslots) || |
|
786 !xdr->codeUint32(&staticLevel)) |
|
787 { |
|
788 return false; |
|
789 } |
|
790 |
|
791 if (mode == XDR_DECODE) { |
|
792 script->lineno_ = lineno; |
|
793 script->column_ = column; |
|
794 script->nslots_ = nslots; |
|
795 script->staticLevel_ = staticLevel; |
|
796 } |
|
797 |
|
798 jsbytecode *code = script->code(); |
|
799 SharedScriptData *ssd; |
|
800 if (mode == XDR_DECODE) { |
|
801 ssd = SharedScriptData::new_(cx, length, nsrcnotes, natoms); |
|
802 if (!ssd) |
|
803 return false; |
|
804 code = ssd->data; |
|
805 if (natoms != 0) { |
|
806 script->natoms_ = natoms; |
|
807 script->atoms = ssd->atoms(); |
|
808 } |
|
809 } |
|
810 |
|
811 if (!xdr->codeBytes(code, length) || !xdr->codeBytes(code + length, nsrcnotes)) { |
|
812 if (mode == XDR_DECODE) |
|
813 js_free(ssd); |
|
814 return false; |
|
815 } |
|
816 |
|
817 for (i = 0; i != natoms; ++i) { |
|
818 if (mode == XDR_DECODE) { |
|
819 RootedAtom tmp(cx); |
|
820 if (!XDRAtom(xdr, &tmp)) |
|
821 return false; |
|
822 script->atoms[i].init(tmp); |
|
823 } else { |
|
824 RootedAtom tmp(cx, script->atoms[i]); |
|
825 if (!XDRAtom(xdr, &tmp)) |
|
826 return false; |
|
827 } |
|
828 } |
|
829 |
|
830 if (mode == XDR_DECODE) { |
|
831 if (!SaveSharedScriptData(cx, script, ssd, nsrcnotes)) |
|
832 return false; |
|
833 } |
|
834 |
|
835 if (nconsts) { |
|
836 HeapValue *vector = script->consts()->vector; |
|
837 RootedValue val(cx); |
|
838 for (i = 0; i != nconsts; ++i) { |
|
839 if (mode == XDR_ENCODE) |
|
840 val = vector[i]; |
|
841 if (!XDRScriptConst(xdr, &val)) |
|
842 return false; |
|
843 if (mode == XDR_DECODE) |
|
844 vector[i].init(val); |
|
845 } |
|
846 } |
|
847 |
|
848 /* |
|
849 * Here looping from 0-to-length to xdr objects is essential to ensure that |
|
850 * all references to enclosing blocks (via FindScopeObjectIndex below) happen |
|
851 * after the enclosing block has been XDR'd. |
|
852 */ |
|
853 for (i = 0; i != nobjects; ++i) { |
|
854 HeapPtr<JSObject> *objp = &script->objects()->vector[i]; |
|
855 XDRClassKind classk; |
|
856 |
|
857 if (mode == XDR_ENCODE) { |
|
858 JSObject *obj = *objp; |
|
859 if (obj->is<BlockObject>()) |
|
860 classk = CK_BlockObject; |
|
861 else if (obj->is<StaticWithObject>()) |
|
862 classk = CK_WithObject; |
|
863 else if (obj->is<JSFunction>()) |
|
864 classk = CK_JSFunction; |
|
865 else if (obj->is<JSObject>() || obj->is<ArrayObject>()) |
|
866 classk = CK_JSObject; |
|
867 else |
|
868 MOZ_ASSUME_UNREACHABLE("Cannot encode this class of object."); |
|
869 } |
|
870 |
|
871 if (!xdr->codeEnum32(&classk)) |
|
872 return false; |
|
873 |
|
874 switch (classk) { |
|
875 case CK_BlockObject: |
|
876 case CK_WithObject: { |
|
877 /* Code the nested block's enclosing scope. */ |
|
878 uint32_t enclosingStaticScopeIndex = 0; |
|
879 if (mode == XDR_ENCODE) { |
|
880 NestedScopeObject &scope = (*objp)->as<NestedScopeObject>(); |
|
881 if (NestedScopeObject *enclosing = scope.enclosingNestedScope()) |
|
882 enclosingStaticScopeIndex = FindScopeObjectIndex(script, *enclosing); |
|
883 else |
|
884 enclosingStaticScopeIndex = UINT32_MAX; |
|
885 } |
|
886 if (!xdr->codeUint32(&enclosingStaticScopeIndex)) |
|
887 return false; |
|
888 Rooted<JSObject*> enclosingStaticScope(cx); |
|
889 if (mode == XDR_DECODE) { |
|
890 if (enclosingStaticScopeIndex != UINT32_MAX) { |
|
891 JS_ASSERT(enclosingStaticScopeIndex < i); |
|
892 enclosingStaticScope = script->objects()->vector[enclosingStaticScopeIndex]; |
|
893 } else { |
|
894 enclosingStaticScope = fun; |
|
895 } |
|
896 } |
|
897 |
|
898 if (classk == CK_BlockObject) { |
|
899 Rooted<StaticBlockObject*> tmp(cx, static_cast<StaticBlockObject *>(objp->get())); |
|
900 if (!XDRStaticBlockObject(xdr, enclosingStaticScope, tmp.address())) |
|
901 return false; |
|
902 *objp = tmp; |
|
903 } else { |
|
904 Rooted<StaticWithObject*> tmp(cx, static_cast<StaticWithObject *>(objp->get())); |
|
905 if (!XDRStaticWithObject(xdr, enclosingStaticScope, tmp.address())) |
|
906 return false; |
|
907 *objp = tmp; |
|
908 } |
|
909 break; |
|
910 } |
|
911 |
|
912 case CK_JSFunction: { |
|
913 /* Code the nested function's enclosing scope. */ |
|
914 uint32_t funEnclosingScopeIndex = 0; |
|
915 RootedObject funEnclosingScope(cx); |
|
916 if (mode == XDR_ENCODE) { |
|
917 RootedFunction function(cx, &(*objp)->as<JSFunction>()); |
|
918 |
|
919 if (function->isInterpretedLazy()) |
|
920 funEnclosingScope = function->lazyScript()->enclosingScope(); |
|
921 else |
|
922 funEnclosingScope = function->nonLazyScript()->enclosingStaticScope(); |
|
923 |
|
924 StaticScopeIter<NoGC> ssi(funEnclosingScope); |
|
925 if (ssi.done() || ssi.type() == StaticScopeIter<NoGC>::FUNCTION) { |
|
926 JS_ASSERT(ssi.done() == !fun); |
|
927 funEnclosingScopeIndex = UINT32_MAX; |
|
928 } else if (ssi.type() == StaticScopeIter<NoGC>::BLOCK) { |
|
929 funEnclosingScopeIndex = FindScopeObjectIndex(script, ssi.block()); |
|
930 JS_ASSERT(funEnclosingScopeIndex < i); |
|
931 } else { |
|
932 funEnclosingScopeIndex = FindScopeObjectIndex(script, ssi.staticWith()); |
|
933 JS_ASSERT(funEnclosingScopeIndex < i); |
|
934 } |
|
935 } |
|
936 |
|
937 if (!xdr->codeUint32(&funEnclosingScopeIndex)) |
|
938 return false; |
|
939 |
|
940 if (mode == XDR_DECODE) { |
|
941 if (funEnclosingScopeIndex == UINT32_MAX) { |
|
942 funEnclosingScope = fun; |
|
943 } else { |
|
944 JS_ASSERT(funEnclosingScopeIndex < i); |
|
945 funEnclosingScope = script->objects()->vector[funEnclosingScopeIndex]; |
|
946 } |
|
947 } |
|
948 |
|
949 // Code nested function and script. |
|
950 RootedObject tmp(cx, *objp); |
|
951 if (!XDRInterpretedFunction(xdr, funEnclosingScope, script, &tmp)) |
|
952 return false; |
|
953 *objp = tmp; |
|
954 break; |
|
955 } |
|
956 |
|
957 case CK_JSObject: { |
|
958 /* Code object literal. */ |
|
959 RootedObject tmp(cx, *objp); |
|
960 if (!XDRObjectLiteral(xdr, &tmp)) |
|
961 return false; |
|
962 *objp = tmp; |
|
963 break; |
|
964 } |
|
965 |
|
966 default: { |
|
967 MOZ_ASSERT(false, "Unknown class kind."); |
|
968 return false; |
|
969 } |
|
970 } |
|
971 } |
|
972 |
|
973 for (i = 0; i != nregexps; ++i) { |
|
974 if (!XDRScriptRegExpObject(xdr, &script->regexps()->vector[i])) |
|
975 return false; |
|
976 } |
|
977 |
|
978 if (ntrynotes != 0) { |
|
979 JSTryNote *tnfirst = script->trynotes()->vector; |
|
980 JS_ASSERT(script->trynotes()->length == ntrynotes); |
|
981 JSTryNote *tn = tnfirst + ntrynotes; |
|
982 do { |
|
983 --tn; |
|
984 if (!xdr->codeUint8(&tn->kind) || |
|
985 !xdr->codeUint32(&tn->stackDepth) || |
|
986 !xdr->codeUint32(&tn->start) || |
|
987 !xdr->codeUint32(&tn->length)) { |
|
988 return false; |
|
989 } |
|
990 } while (tn != tnfirst); |
|
991 } |
|
992 |
|
993 for (i = 0; i < nblockscopes; ++i) { |
|
994 BlockScopeNote *note = &script->blockScopes()->vector[i]; |
|
995 if (!xdr->codeUint32(¬e->index) || |
|
996 !xdr->codeUint32(¬e->start) || |
|
997 !xdr->codeUint32(¬e->length) || |
|
998 !xdr->codeUint32(¬e->parent)) |
|
999 { |
|
1000 return false; |
|
1001 } |
|
1002 } |
|
1003 |
|
1004 if (scriptBits & (1 << HasLazyScript)) { |
|
1005 Rooted<LazyScript *> lazy(cx); |
|
1006 if (mode == XDR_ENCODE) |
|
1007 lazy = script->maybeLazyScript(); |
|
1008 |
|
1009 if (!XDRRelazificationInfo(xdr, fun, script, &lazy)) |
|
1010 return false; |
|
1011 |
|
1012 if (mode == XDR_DECODE) |
|
1013 script->setLazyScript(lazy); |
|
1014 } |
|
1015 |
|
1016 if (mode == XDR_DECODE) { |
|
1017 scriptp.set(script); |
|
1018 |
|
1019 /* see BytecodeEmitter::tellDebuggerAboutCompiledScript */ |
|
1020 CallNewScriptHook(cx, script, fun); |
|
1021 if (!fun) { |
|
1022 RootedGlobalObject global(cx, script->compileAndGo() ? &script->global() : nullptr); |
|
1023 Debugger::onNewScript(cx, script, global); |
|
1024 } |
|
1025 } |
|
1026 |
|
1027 return true; |
|
1028 } |
|
1029 |
|
1030 template bool |
|
1031 js::XDRScript(XDRState<XDR_ENCODE> *, HandleObject, HandleScript, HandleFunction, |
|
1032 MutableHandleScript); |
|
1033 |
|
1034 template bool |
|
1035 js::XDRScript(XDRState<XDR_DECODE> *, HandleObject, HandleScript, HandleFunction, |
|
1036 MutableHandleScript); |
|
1037 |
|
1038 template<XDRMode mode> |
|
1039 bool |
|
1040 js::XDRLazyScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript, |
|
1041 HandleFunction fun, MutableHandle<LazyScript *> lazy) |
|
1042 { |
|
1043 JSContext *cx = xdr->cx(); |
|
1044 |
|
1045 { |
|
1046 uint32_t begin; |
|
1047 uint32_t end; |
|
1048 uint32_t lineno; |
|
1049 uint32_t column; |
|
1050 uint64_t packedFields; |
|
1051 |
|
1052 if (mode == XDR_ENCODE) { |
|
1053 MOZ_ASSERT(!lazy->maybeScript()); |
|
1054 MOZ_ASSERT(fun == lazy->functionNonDelazifying()); |
|
1055 |
|
1056 begin = lazy->begin(); |
|
1057 end = lazy->end(); |
|
1058 lineno = lazy->lineno(); |
|
1059 column = lazy->column(); |
|
1060 packedFields = lazy->packedFields(); |
|
1061 } |
|
1062 |
|
1063 if (!xdr->codeUint32(&begin) || !xdr->codeUint32(&end) || |
|
1064 !xdr->codeUint32(&lineno) || !xdr->codeUint32(&column) || |
|
1065 !xdr->codeUint64(&packedFields)) |
|
1066 { |
|
1067 return false; |
|
1068 } |
|
1069 |
|
1070 if (mode == XDR_DECODE) |
|
1071 lazy.set(LazyScript::Create(cx, fun, packedFields, begin, end, lineno, column)); |
|
1072 } |
|
1073 |
|
1074 // Code free variables. |
|
1075 if (!XDRLazyFreeVariables(xdr, lazy)) |
|
1076 return false; |
|
1077 |
|
1078 // Code inner functions. |
|
1079 { |
|
1080 RootedObject func(cx); |
|
1081 HeapPtrFunction *innerFunctions = lazy->innerFunctions(); |
|
1082 size_t numInnerFunctions = lazy->numInnerFunctions(); |
|
1083 for (size_t i = 0; i < numInnerFunctions; i++) { |
|
1084 if (mode == XDR_ENCODE) |
|
1085 func = innerFunctions[i]; |
|
1086 |
|
1087 if (!XDRInterpretedFunction(xdr, fun, enclosingScript, &func)) |
|
1088 return false; |
|
1089 |
|
1090 if (mode == XDR_DECODE) |
|
1091 innerFunctions[i] = &func->as<JSFunction>(); |
|
1092 } |
|
1093 } |
|
1094 |
|
1095 if (mode == XDR_DECODE) { |
|
1096 JS_ASSERT(!lazy->sourceObject()); |
|
1097 ScriptSourceObject *sourceObject = &enclosingScript->scriptSourceUnwrap(); |
|
1098 |
|
1099 // Set the enclosing scope of the lazy function, this would later be |
|
1100 // used to define the environment when the function would be used. |
|
1101 lazy->setParent(enclosingScope, sourceObject); |
|
1102 } |
|
1103 |
|
1104 return true; |
|
1105 } |
|
1106 |
|
1107 template bool |
|
1108 js::XDRLazyScript(XDRState<XDR_ENCODE> *, HandleObject, HandleScript, |
|
1109 HandleFunction, MutableHandle<LazyScript *>); |
|
1110 |
|
1111 template bool |
|
1112 js::XDRLazyScript(XDRState<XDR_DECODE> *, HandleObject, HandleScript, |
|
1113 HandleFunction, MutableHandle<LazyScript *>); |
|
1114 |
|
1115 void |
|
1116 JSScript::setSourceObject(JSObject *object) |
|
1117 { |
|
1118 JS_ASSERT(compartment() == object->compartment()); |
|
1119 sourceObject_ = object; |
|
1120 } |
|
1121 |
|
1122 js::ScriptSourceObject & |
|
1123 JSScript::scriptSourceUnwrap() const { |
|
1124 return UncheckedUnwrap(sourceObject())->as<ScriptSourceObject>(); |
|
1125 } |
|
1126 |
|
1127 js::ScriptSource * |
|
1128 JSScript::scriptSource() const { |
|
1129 return scriptSourceUnwrap().source(); |
|
1130 } |
|
1131 |
|
1132 bool |
|
1133 JSScript::initScriptCounts(JSContext *cx) |
|
1134 { |
|
1135 JS_ASSERT(!hasScriptCounts()); |
|
1136 |
|
1137 size_t n = 0; |
|
1138 |
|
1139 for (jsbytecode *pc = code(); pc < codeEnd(); pc += GetBytecodeLength(pc)) |
|
1140 n += PCCounts::numCounts(JSOp(*pc)); |
|
1141 |
|
1142 size_t bytes = (length() * sizeof(PCCounts)) + (n * sizeof(double)); |
|
1143 char *base = (char *) cx->calloc_(bytes); |
|
1144 if (!base) |
|
1145 return false; |
|
1146 |
|
1147 /* Create compartment's scriptCountsMap if necessary. */ |
|
1148 ScriptCountsMap *map = compartment()->scriptCountsMap; |
|
1149 if (!map) { |
|
1150 map = cx->new_<ScriptCountsMap>(); |
|
1151 if (!map || !map->init()) { |
|
1152 js_free(base); |
|
1153 js_delete(map); |
|
1154 return false; |
|
1155 } |
|
1156 compartment()->scriptCountsMap = map; |
|
1157 } |
|
1158 |
|
1159 char *cursor = base; |
|
1160 |
|
1161 ScriptCounts scriptCounts; |
|
1162 scriptCounts.pcCountsVector = (PCCounts *) cursor; |
|
1163 cursor += length() * sizeof(PCCounts); |
|
1164 |
|
1165 for (jsbytecode *pc = code(); pc < codeEnd(); pc += GetBytecodeLength(pc)) { |
|
1166 JS_ASSERT(uintptr_t(cursor) % sizeof(double) == 0); |
|
1167 scriptCounts.pcCountsVector[pcToOffset(pc)].counts = (double *) cursor; |
|
1168 size_t capacity = PCCounts::numCounts(JSOp(*pc)); |
|
1169 #ifdef DEBUG |
|
1170 scriptCounts.pcCountsVector[pcToOffset(pc)].capacity = capacity; |
|
1171 #endif |
|
1172 cursor += capacity * sizeof(double); |
|
1173 } |
|
1174 |
|
1175 if (!map->putNew(this, scriptCounts)) { |
|
1176 js_free(base); |
|
1177 return false; |
|
1178 } |
|
1179 hasScriptCounts_ = true; // safe to set this; we can't fail after this point |
|
1180 |
|
1181 JS_ASSERT(size_t(cursor - base) == bytes); |
|
1182 |
|
1183 /* Enable interrupts in any interpreter frames running on this script. */ |
|
1184 for (ActivationIterator iter(cx->runtime()); !iter.done(); ++iter) { |
|
1185 if (iter->isInterpreter()) |
|
1186 iter->asInterpreter()->enableInterruptsIfRunning(this); |
|
1187 } |
|
1188 |
|
1189 return true; |
|
1190 } |
|
1191 |
|
1192 static inline ScriptCountsMap::Ptr GetScriptCountsMapEntry(JSScript *script) |
|
1193 { |
|
1194 JS_ASSERT(script->hasScriptCounts()); |
|
1195 ScriptCountsMap *map = script->compartment()->scriptCountsMap; |
|
1196 ScriptCountsMap::Ptr p = map->lookup(script); |
|
1197 JS_ASSERT(p); |
|
1198 return p; |
|
1199 } |
|
1200 |
|
1201 js::PCCounts |
|
1202 JSScript::getPCCounts(jsbytecode *pc) { |
|
1203 JS_ASSERT(containsPC(pc)); |
|
1204 ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this); |
|
1205 return p->value().pcCountsVector[pcToOffset(pc)]; |
|
1206 } |
|
1207 |
|
1208 void |
|
1209 JSScript::addIonCounts(jit::IonScriptCounts *ionCounts) |
|
1210 { |
|
1211 ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this); |
|
1212 if (p->value().ionCounts) |
|
1213 ionCounts->setPrevious(p->value().ionCounts); |
|
1214 p->value().ionCounts = ionCounts; |
|
1215 } |
|
1216 |
|
1217 jit::IonScriptCounts * |
|
1218 JSScript::getIonCounts() |
|
1219 { |
|
1220 ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this); |
|
1221 return p->value().ionCounts; |
|
1222 } |
|
1223 |
|
1224 ScriptCounts |
|
1225 JSScript::releaseScriptCounts() |
|
1226 { |
|
1227 ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this); |
|
1228 ScriptCounts counts = p->value(); |
|
1229 compartment()->scriptCountsMap->remove(p); |
|
1230 hasScriptCounts_ = false; |
|
1231 return counts; |
|
1232 } |
|
1233 |
|
1234 void |
|
1235 JSScript::destroyScriptCounts(FreeOp *fop) |
|
1236 { |
|
1237 if (hasScriptCounts()) { |
|
1238 ScriptCounts scriptCounts = releaseScriptCounts(); |
|
1239 scriptCounts.destroy(fop); |
|
1240 } |
|
1241 } |
|
1242 |
|
1243 void |
|
1244 ScriptSourceObject::setSource(ScriptSource *source) |
|
1245 { |
|
1246 if (source) |
|
1247 source->incref(); |
|
1248 if (this->source()) |
|
1249 this->source()->decref(); |
|
1250 setReservedSlot(SOURCE_SLOT, PrivateValue(source)); |
|
1251 } |
|
1252 |
|
1253 JSObject * |
|
1254 ScriptSourceObject::element() const |
|
1255 { |
|
1256 return getReservedSlot(ELEMENT_SLOT).toObjectOrNull(); |
|
1257 } |
|
1258 |
|
1259 void |
|
1260 ScriptSourceObject::initElement(HandleObject element) |
|
1261 { |
|
1262 JS_ASSERT(getReservedSlot(ELEMENT_SLOT).isNull()); |
|
1263 setReservedSlot(ELEMENT_SLOT, ObjectOrNullValue(element)); |
|
1264 } |
|
1265 |
|
1266 const Value & |
|
1267 ScriptSourceObject::elementAttributeName() const |
|
1268 { |
|
1269 const Value &prop = getReservedSlot(ELEMENT_PROPERTY_SLOT); |
|
1270 JS_ASSERT(prop.isUndefined() || prop.isString()); |
|
1271 return prop; |
|
1272 } |
|
1273 |
|
1274 void |
|
1275 ScriptSourceObject::initIntroductionScript(JSScript *script) |
|
1276 { |
|
1277 JS_ASSERT(!getReservedSlot(INTRODUCTION_SCRIPT_SLOT).toPrivate()); |
|
1278 |
|
1279 // There is no equivalent of cross-compartment wrappers for scripts. If |
|
1280 // the introduction script would be in a different compartment from the |
|
1281 // compiled code, we would be creating a cross-compartment script |
|
1282 // reference, which would be bogus. In that case, just don't bother to |
|
1283 // retain the introduction script. |
|
1284 if (script && script->compartment() == compartment()) |
|
1285 setReservedSlot(INTRODUCTION_SCRIPT_SLOT, PrivateValue(script)); |
|
1286 } |
|
1287 |
|
1288 void |
|
1289 ScriptSourceObject::trace(JSTracer *trc, JSObject *obj) |
|
1290 { |
|
1291 ScriptSourceObject *sso = static_cast<ScriptSourceObject *>(obj); |
|
1292 |
|
1293 if (JSScript *script = sso->introductionScript()) { |
|
1294 MarkScriptUnbarriered(trc, &script, "ScriptSourceObject introductionScript"); |
|
1295 sso->setReservedSlot(INTRODUCTION_SCRIPT_SLOT, PrivateValue(script)); |
|
1296 } |
|
1297 } |
|
1298 |
|
1299 void |
|
1300 ScriptSourceObject::finalize(FreeOp *fop, JSObject *obj) |
|
1301 { |
|
1302 // ScriptSource::setSource automatically takes care of the refcount |
|
1303 obj->as<ScriptSourceObject>().setSource(nullptr); |
|
1304 } |
|
1305 |
|
1306 const Class ScriptSourceObject::class_ = { |
|
1307 "ScriptSource", |
|
1308 JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | |
|
1309 JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_IS_ANONYMOUS, |
|
1310 JS_PropertyStub, /* addProperty */ |
|
1311 JS_DeletePropertyStub, /* delProperty */ |
|
1312 JS_PropertyStub, /* getProperty */ |
|
1313 JS_StrictPropertyStub, /* setProperty */ |
|
1314 JS_EnumerateStub, |
|
1315 JS_ResolveStub, |
|
1316 JS_ConvertStub, |
|
1317 finalize, |
|
1318 nullptr, /* call */ |
|
1319 nullptr, /* hasInstance */ |
|
1320 nullptr, /* construct */ |
|
1321 trace |
|
1322 }; |
|
1323 |
|
1324 ScriptSourceObject * |
|
1325 ScriptSourceObject::create(ExclusiveContext *cx, ScriptSource *source, |
|
1326 const ReadOnlyCompileOptions &options) |
|
1327 { |
|
1328 RootedObject object(cx, NewObjectWithGivenProto(cx, &class_, nullptr, cx->global())); |
|
1329 if (!object) |
|
1330 return nullptr; |
|
1331 RootedScriptSource sourceObject(cx, &object->as<ScriptSourceObject>()); |
|
1332 |
|
1333 source->incref(); |
|
1334 sourceObject->initSlot(SOURCE_SLOT, PrivateValue(source)); |
|
1335 sourceObject->initSlot(ELEMENT_SLOT, ObjectOrNullValue(options.element())); |
|
1336 if (options.elementAttributeName()) |
|
1337 sourceObject->initSlot(ELEMENT_PROPERTY_SLOT, StringValue(options.elementAttributeName())); |
|
1338 else |
|
1339 sourceObject->initSlot(ELEMENT_PROPERTY_SLOT, UndefinedValue()); |
|
1340 |
|
1341 sourceObject->initSlot(INTRODUCTION_SCRIPT_SLOT, PrivateValue(nullptr)); |
|
1342 sourceObject->initIntroductionScript(options.introductionScript()); |
|
1343 |
|
1344 return sourceObject; |
|
1345 } |
|
1346 |
|
1347 static const unsigned char emptySource[] = ""; |
|
1348 |
|
1349 /* Adjust the amount of memory this script source uses for source data, |
|
1350 reallocating if needed. */ |
|
1351 bool |
|
1352 ScriptSource::adjustDataSize(size_t nbytes) |
|
1353 { |
|
1354 // Allocating 0 bytes has undefined behavior, so special-case it. |
|
1355 if (nbytes == 0) { |
|
1356 if (data.compressed != emptySource) |
|
1357 js_free(data.compressed); |
|
1358 data.compressed = const_cast<unsigned char *>(emptySource); |
|
1359 return true; |
|
1360 } |
|
1361 |
|
1362 // |data.compressed| can be nullptr. |
|
1363 void *buf = js_realloc(data.compressed, nbytes); |
|
1364 if (!buf && data.compressed != emptySource) |
|
1365 js_free(data.compressed); |
|
1366 data.compressed = static_cast<unsigned char *>(buf); |
|
1367 return !!data.compressed; |
|
1368 } |
|
1369 |
|
1370 /* static */ bool |
|
1371 JSScript::loadSource(JSContext *cx, ScriptSource *ss, bool *worked) |
|
1372 { |
|
1373 JS_ASSERT(!ss->hasSourceData()); |
|
1374 *worked = false; |
|
1375 if (!cx->runtime()->sourceHook || !ss->sourceRetrievable()) |
|
1376 return true; |
|
1377 jschar *src = nullptr; |
|
1378 size_t length; |
|
1379 if (!cx->runtime()->sourceHook->load(cx, ss->filename(), &src, &length)) |
|
1380 return false; |
|
1381 if (!src) |
|
1382 return true; |
|
1383 ss->setSource(src, length); |
|
1384 *worked = true; |
|
1385 return true; |
|
1386 } |
|
1387 |
|
1388 JSFlatString * |
|
1389 JSScript::sourceData(JSContext *cx) |
|
1390 { |
|
1391 JS_ASSERT(scriptSource()->hasSourceData()); |
|
1392 return scriptSource()->substring(cx, sourceStart(), sourceEnd()); |
|
1393 } |
|
1394 |
|
1395 SourceDataCache::AutoHoldEntry::AutoHoldEntry() |
|
1396 : cache_(nullptr), source_(nullptr), charsToFree_(nullptr) |
|
1397 { |
|
1398 } |
|
1399 |
|
1400 void |
|
1401 SourceDataCache::AutoHoldEntry::holdEntry(SourceDataCache *cache, ScriptSource *source) |
|
1402 { |
|
1403 // Initialise the holder for a specific cache and script source. This will |
|
1404 // hold on to the cached source chars in the event that the cache is purged. |
|
1405 JS_ASSERT(!cache_ && !source_ && !charsToFree_); |
|
1406 cache_ = cache; |
|
1407 source_ = source; |
|
1408 } |
|
1409 |
|
1410 void |
|
1411 SourceDataCache::AutoHoldEntry::deferDelete(const jschar *chars) |
|
1412 { |
|
1413 // Take ownership of source chars now the cache is being purged. Remove our |
|
1414 // reference to the ScriptSource which might soon be destroyed. |
|
1415 JS_ASSERT(cache_ && source_ && !charsToFree_); |
|
1416 cache_ = nullptr; |
|
1417 source_ = nullptr; |
|
1418 charsToFree_ = chars; |
|
1419 } |
|
1420 |
|
1421 SourceDataCache::AutoHoldEntry::~AutoHoldEntry() |
|
1422 { |
|
1423 // The holder is going out of scope. If it has taken ownership of cached |
|
1424 // chars then delete them, otherwise unregister ourself with the cache. |
|
1425 if (charsToFree_) { |
|
1426 JS_ASSERT(!cache_ && !source_); |
|
1427 js_free(const_cast<jschar *>(charsToFree_)); |
|
1428 } else if (cache_) { |
|
1429 JS_ASSERT(source_); |
|
1430 cache_->releaseEntry(*this); |
|
1431 } |
|
1432 } |
|
1433 |
|
1434 void |
|
1435 SourceDataCache::holdEntry(AutoHoldEntry &holder, ScriptSource *ss) |
|
1436 { |
|
1437 JS_ASSERT(!holder_); |
|
1438 holder.holdEntry(this, ss); |
|
1439 holder_ = &holder; |
|
1440 } |
|
1441 |
|
1442 void |
|
1443 SourceDataCache::releaseEntry(AutoHoldEntry &holder) |
|
1444 { |
|
1445 JS_ASSERT(holder_ == &holder); |
|
1446 holder_ = nullptr; |
|
1447 } |
|
1448 |
|
1449 const jschar * |
|
1450 SourceDataCache::lookup(ScriptSource *ss, AutoHoldEntry &holder) |
|
1451 { |
|
1452 JS_ASSERT(!holder_); |
|
1453 if (!map_) |
|
1454 return nullptr; |
|
1455 if (Map::Ptr p = map_->lookup(ss)) { |
|
1456 holdEntry(holder, ss); |
|
1457 return p->value(); |
|
1458 } |
|
1459 return nullptr; |
|
1460 } |
|
1461 |
|
1462 bool |
|
1463 SourceDataCache::put(ScriptSource *ss, const jschar *str, AutoHoldEntry &holder) |
|
1464 { |
|
1465 JS_ASSERT(!holder_); |
|
1466 |
|
1467 if (!map_) { |
|
1468 map_ = js_new<Map>(); |
|
1469 if (!map_) |
|
1470 return false; |
|
1471 |
|
1472 if (!map_->init()) { |
|
1473 js_delete(map_); |
|
1474 map_ = nullptr; |
|
1475 return false; |
|
1476 } |
|
1477 } |
|
1478 |
|
1479 if (!map_->put(ss, str)) |
|
1480 return false; |
|
1481 |
|
1482 holdEntry(holder, ss); |
|
1483 return true; |
|
1484 } |
|
1485 |
|
1486 void |
|
1487 SourceDataCache::purge() |
|
1488 { |
|
1489 if (!map_) |
|
1490 return; |
|
1491 |
|
1492 for (Map::Range r = map_->all(); !r.empty(); r.popFront()) { |
|
1493 const jschar *chars = r.front().value(); |
|
1494 if (holder_ && r.front().key() == holder_->source()) { |
|
1495 holder_->deferDelete(chars); |
|
1496 holder_ = nullptr; |
|
1497 } else { |
|
1498 js_free(const_cast<jschar*>(chars)); |
|
1499 } |
|
1500 } |
|
1501 |
|
1502 js_delete(map_); |
|
1503 map_ = nullptr; |
|
1504 } |
|
1505 |
|
1506 size_t |
|
1507 SourceDataCache::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) |
|
1508 { |
|
1509 size_t n = 0; |
|
1510 if (map_ && !map_->empty()) { |
|
1511 n += map_->sizeOfIncludingThis(mallocSizeOf); |
|
1512 for (Map::Range r = map_->all(); !r.empty(); r.popFront()) { |
|
1513 const jschar *v = r.front().value(); |
|
1514 n += mallocSizeOf(v); |
|
1515 } |
|
1516 } |
|
1517 return n; |
|
1518 } |
|
1519 |
|
1520 const jschar * |
|
1521 ScriptSource::chars(JSContext *cx, SourceDataCache::AutoHoldEntry &holder) |
|
1522 { |
|
1523 if (const jschar *chars = getOffThreadCompressionChars(cx)) |
|
1524 return chars; |
|
1525 JS_ASSERT(ready()); |
|
1526 |
|
1527 #ifdef USE_ZLIB |
|
1528 if (compressed()) { |
|
1529 if (const jschar *decompressed = cx->runtime()->sourceDataCache.lookup(this, holder)) |
|
1530 return decompressed; |
|
1531 |
|
1532 const size_t nbytes = sizeof(jschar) * (length_ + 1); |
|
1533 jschar *decompressed = static_cast<jschar *>(js_malloc(nbytes)); |
|
1534 if (!decompressed) |
|
1535 return nullptr; |
|
1536 |
|
1537 if (!DecompressString(data.compressed, compressedLength_, |
|
1538 reinterpret_cast<unsigned char *>(decompressed), nbytes)) { |
|
1539 JS_ReportOutOfMemory(cx); |
|
1540 js_free(decompressed); |
|
1541 return nullptr; |
|
1542 } |
|
1543 |
|
1544 decompressed[length_] = 0; |
|
1545 |
|
1546 if (!cx->runtime()->sourceDataCache.put(this, decompressed, holder)) { |
|
1547 JS_ReportOutOfMemory(cx); |
|
1548 js_free(decompressed); |
|
1549 return nullptr; |
|
1550 } |
|
1551 |
|
1552 return decompressed; |
|
1553 } |
|
1554 #endif |
|
1555 return data.source; |
|
1556 } |
|
1557 |
|
1558 JSFlatString * |
|
1559 ScriptSource::substring(JSContext *cx, uint32_t start, uint32_t stop) |
|
1560 { |
|
1561 JS_ASSERT(start <= stop); |
|
1562 SourceDataCache::AutoHoldEntry holder; |
|
1563 const jschar *chars = this->chars(cx, holder); |
|
1564 if (!chars) |
|
1565 return nullptr; |
|
1566 return js_NewStringCopyN<CanGC>(cx, chars + start, stop - start); |
|
1567 } |
|
1568 |
|
1569 bool |
|
1570 ScriptSource::setSourceCopy(ExclusiveContext *cx, SourceBufferHolder &srcBuf, |
|
1571 bool argumentsNotIncluded, SourceCompressionTask *task) |
|
1572 { |
|
1573 JS_ASSERT(!hasSourceData()); |
|
1574 length_ = srcBuf.length(); |
|
1575 argumentsNotIncluded_ = argumentsNotIncluded; |
|
1576 |
|
1577 // There are several cases where source compression is not a good idea: |
|
1578 // - If the script is tiny, then compression will save little or no space. |
|
1579 // - If the script is enormous, then decompression can take seconds. With |
|
1580 // lazy parsing, decompression is not uncommon, so this can significantly |
|
1581 // increase latency. |
|
1582 // - If there is only one core, then compression will contend with JS |
|
1583 // execution (which hurts benchmarketing). |
|
1584 // - If the source contains a giant string, then parsing will finish much |
|
1585 // faster than compression which increases latency (this case is handled |
|
1586 // in Parser::stringLiteral). |
|
1587 // |
|
1588 // Lastly, since the parsing thread will eventually perform a blocking wait |
|
1589 // on the compresion task's worker thread, require that there are at least 2 |
|
1590 // worker threads: |
|
1591 // - If we are on a worker thread, there must be another worker thread to |
|
1592 // execute our compression task. |
|
1593 // - If we are on the main thread, there must be at least two worker |
|
1594 // threads since at most one worker thread can be blocking on the main |
|
1595 // thread (see WorkerThreadState::canStartParseTask) which would cause a |
|
1596 // deadlock if there wasn't a second worker thread that could make |
|
1597 // progress on our compression task. |
|
1598 #ifdef JS_THREADSAFE |
|
1599 bool canCompressOffThread = |
|
1600 WorkerThreadState().cpuCount > 1 && |
|
1601 WorkerThreadState().threadCount >= 2; |
|
1602 #else |
|
1603 bool canCompressOffThread = false; |
|
1604 #endif |
|
1605 const size_t TINY_SCRIPT = 256; |
|
1606 const size_t HUGE_SCRIPT = 5 * 1024 * 1024; |
|
1607 if (TINY_SCRIPT <= srcBuf.length() && srcBuf.length() < HUGE_SCRIPT && canCompressOffThread) { |
|
1608 task->ss = this; |
|
1609 task->chars = srcBuf.get(); |
|
1610 ready_ = false; |
|
1611 if (!StartOffThreadCompression(cx, task)) |
|
1612 return false; |
|
1613 } else if (srcBuf.ownsChars()) { |
|
1614 data.source = srcBuf.take(); |
|
1615 } else { |
|
1616 if (!adjustDataSize(sizeof(jschar) * srcBuf.length())) |
|
1617 return false; |
|
1618 PodCopy(data.source, srcBuf.get(), length_); |
|
1619 } |
|
1620 |
|
1621 return true; |
|
1622 } |
|
1623 |
|
1624 void |
|
1625 ScriptSource::setSource(const jschar *src, size_t length) |
|
1626 { |
|
1627 JS_ASSERT(!hasSourceData()); |
|
1628 length_ = length; |
|
1629 JS_ASSERT(!argumentsNotIncluded_); |
|
1630 data.source = const_cast<jschar *>(src); |
|
1631 } |
|
1632 |
|
1633 bool |
|
1634 SourceCompressionTask::work() |
|
1635 { |
|
1636 // A given compression token can be compressed on any thread, and the ss |
|
1637 // not being ready indicates to other threads that its fields might change |
|
1638 // with no lock held. |
|
1639 JS_ASSERT(!ss->ready()); |
|
1640 |
|
1641 size_t compressedLength = 0; |
|
1642 size_t nbytes = sizeof(jschar) * ss->length_; |
|
1643 |
|
1644 // Memory allocation functions on JSRuntime and JSContext are not |
|
1645 // threadsafe. We have to use the js_* variants. |
|
1646 |
|
1647 #ifdef USE_ZLIB |
|
1648 // Try to keep the maximum memory usage down by only allocating half the |
|
1649 // size of the string, first. |
|
1650 size_t firstSize = nbytes / 2; |
|
1651 if (!ss->adjustDataSize(firstSize)) |
|
1652 return false; |
|
1653 Compressor comp(reinterpret_cast<const unsigned char *>(chars), nbytes); |
|
1654 if (!comp.init()) |
|
1655 return false; |
|
1656 comp.setOutput(ss->data.compressed, firstSize); |
|
1657 bool cont = !abort_; |
|
1658 while (cont) { |
|
1659 switch (comp.compressMore()) { |
|
1660 case Compressor::CONTINUE: |
|
1661 break; |
|
1662 case Compressor::MOREOUTPUT: { |
|
1663 if (comp.outWritten() == nbytes) { |
|
1664 cont = false; |
|
1665 break; |
|
1666 } |
|
1667 |
|
1668 // The compressed output is greater than half the size of the |
|
1669 // original string. Reallocate to the full size. |
|
1670 if (!ss->adjustDataSize(nbytes)) |
|
1671 return false; |
|
1672 comp.setOutput(ss->data.compressed, nbytes); |
|
1673 break; |
|
1674 } |
|
1675 case Compressor::DONE: |
|
1676 cont = false; |
|
1677 break; |
|
1678 case Compressor::OOM: |
|
1679 return false; |
|
1680 } |
|
1681 cont = cont && !abort_; |
|
1682 } |
|
1683 compressedLength = comp.outWritten(); |
|
1684 if (abort_ || compressedLength == nbytes) |
|
1685 compressedLength = 0; |
|
1686 #endif |
|
1687 |
|
1688 if (compressedLength == 0) { |
|
1689 if (!ss->adjustDataSize(nbytes)) |
|
1690 return false; |
|
1691 PodCopy(ss->data.source, chars, ss->length()); |
|
1692 } else { |
|
1693 // Shrink the buffer to the size of the compressed data. Shouldn't fail. |
|
1694 JS_ALWAYS_TRUE(ss->adjustDataSize(compressedLength)); |
|
1695 } |
|
1696 ss->compressedLength_ = compressedLength; |
|
1697 return true; |
|
1698 } |
|
1699 |
|
1700 void |
|
1701 ScriptSource::destroy() |
|
1702 { |
|
1703 JS_ASSERT(ready()); |
|
1704 adjustDataSize(0); |
|
1705 if (introducerFilename_ != filename_) |
|
1706 js_free(introducerFilename_); |
|
1707 js_free(filename_); |
|
1708 js_free(displayURL_); |
|
1709 js_free(sourceMapURL_); |
|
1710 if (originPrincipals_) |
|
1711 JS_DropPrincipals(TlsPerThreadData.get()->runtimeFromMainThread(), originPrincipals_); |
|
1712 ready_ = false; |
|
1713 js_free(this); |
|
1714 } |
|
1715 |
|
1716 void |
|
1717 ScriptSource::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, |
|
1718 JS::ScriptSourceInfo *info) const |
|
1719 { |
|
1720 if (ready() && data.compressed != emptySource) { |
|
1721 if (compressed()) |
|
1722 info->compressed += mallocSizeOf(data.compressed); |
|
1723 else |
|
1724 info->uncompressed += mallocSizeOf(data.source); |
|
1725 } |
|
1726 info->misc += mallocSizeOf(this) + mallocSizeOf(filename_); |
|
1727 info->numScripts++; |
|
1728 } |
|
1729 |
|
1730 template<XDRMode mode> |
|
1731 bool |
|
1732 ScriptSource::performXDR(XDRState<mode> *xdr) |
|
1733 { |
|
1734 uint8_t hasSource = hasSourceData(); |
|
1735 if (!xdr->codeUint8(&hasSource)) |
|
1736 return false; |
|
1737 |
|
1738 uint8_t retrievable = sourceRetrievable_; |
|
1739 if (!xdr->codeUint8(&retrievable)) |
|
1740 return false; |
|
1741 sourceRetrievable_ = retrievable; |
|
1742 |
|
1743 if (hasSource && !sourceRetrievable_) { |
|
1744 // Only set members when we know decoding cannot fail. This prevents the |
|
1745 // script source from being partially initialized. |
|
1746 uint32_t length = length_; |
|
1747 if (!xdr->codeUint32(&length)) |
|
1748 return false; |
|
1749 |
|
1750 uint32_t compressedLength = compressedLength_; |
|
1751 if (!xdr->codeUint32(&compressedLength)) |
|
1752 return false; |
|
1753 |
|
1754 uint8_t argumentsNotIncluded = argumentsNotIncluded_; |
|
1755 if (!xdr->codeUint8(&argumentsNotIncluded)) |
|
1756 return false; |
|
1757 |
|
1758 size_t byteLen = compressedLength ? compressedLength : (length * sizeof(jschar)); |
|
1759 if (mode == XDR_DECODE) { |
|
1760 if (!adjustDataSize(byteLen)) |
|
1761 return false; |
|
1762 } |
|
1763 if (!xdr->codeBytes(data.compressed, byteLen)) { |
|
1764 if (mode == XDR_DECODE) { |
|
1765 js_free(data.compressed); |
|
1766 data.compressed = nullptr; |
|
1767 } |
|
1768 return false; |
|
1769 } |
|
1770 length_ = length; |
|
1771 compressedLength_ = compressedLength; |
|
1772 argumentsNotIncluded_ = argumentsNotIncluded; |
|
1773 } |
|
1774 |
|
1775 uint8_t haveSourceMap = hasSourceMapURL(); |
|
1776 if (!xdr->codeUint8(&haveSourceMap)) |
|
1777 return false; |
|
1778 |
|
1779 if (haveSourceMap) { |
|
1780 uint32_t sourceMapURLLen = (mode == XDR_DECODE) ? 0 : js_strlen(sourceMapURL_); |
|
1781 if (!xdr->codeUint32(&sourceMapURLLen)) |
|
1782 return false; |
|
1783 |
|
1784 if (mode == XDR_DECODE) { |
|
1785 size_t byteLen = (sourceMapURLLen + 1) * sizeof(jschar); |
|
1786 sourceMapURL_ = static_cast<jschar *>(xdr->cx()->malloc_(byteLen)); |
|
1787 if (!sourceMapURL_) |
|
1788 return false; |
|
1789 } |
|
1790 if (!xdr->codeChars(sourceMapURL_, sourceMapURLLen)) { |
|
1791 if (mode == XDR_DECODE) { |
|
1792 js_free(sourceMapURL_); |
|
1793 sourceMapURL_ = nullptr; |
|
1794 } |
|
1795 return false; |
|
1796 } |
|
1797 sourceMapURL_[sourceMapURLLen] = '\0'; |
|
1798 } |
|
1799 |
|
1800 uint8_t haveDisplayURL = hasDisplayURL(); |
|
1801 if (!xdr->codeUint8(&haveDisplayURL)) |
|
1802 return false; |
|
1803 |
|
1804 if (haveDisplayURL) { |
|
1805 uint32_t displayURLLen = (mode == XDR_DECODE) ? 0 : js_strlen(displayURL_); |
|
1806 if (!xdr->codeUint32(&displayURLLen)) |
|
1807 return false; |
|
1808 |
|
1809 if (mode == XDR_DECODE) { |
|
1810 size_t byteLen = (displayURLLen + 1) * sizeof(jschar); |
|
1811 displayURL_ = static_cast<jschar *>(xdr->cx()->malloc_(byteLen)); |
|
1812 if (!displayURL_) |
|
1813 return false; |
|
1814 } |
|
1815 if (!xdr->codeChars(displayURL_, displayURLLen)) { |
|
1816 if (mode == XDR_DECODE) { |
|
1817 js_free(displayURL_); |
|
1818 displayURL_ = nullptr; |
|
1819 } |
|
1820 return false; |
|
1821 } |
|
1822 displayURL_[displayURLLen] = '\0'; |
|
1823 } |
|
1824 |
|
1825 uint8_t haveFilename = !!filename_; |
|
1826 if (!xdr->codeUint8(&haveFilename)) |
|
1827 return false; |
|
1828 |
|
1829 if (haveFilename) { |
|
1830 const char *fn = filename(); |
|
1831 if (!xdr->codeCString(&fn)) |
|
1832 return false; |
|
1833 if (mode == XDR_DECODE && !setFilename(xdr->cx(), fn)) |
|
1834 return false; |
|
1835 } |
|
1836 |
|
1837 if (mode == XDR_DECODE) |
|
1838 ready_ = true; |
|
1839 |
|
1840 return true; |
|
1841 } |
|
1842 |
|
1843 // Format and return a cx->malloc_'ed URL for a generated script like: |
|
1844 // {filename} line {lineno} > {introducer} |
|
1845 // For example: |
|
1846 // foo.js line 7 > eval |
|
1847 // indicating code compiled by the call to 'eval' on line 7 of foo.js. |
|
1848 static char * |
|
1849 FormatIntroducedFilename(ExclusiveContext *cx, const char *filename, unsigned lineno, |
|
1850 const char *introducer) |
|
1851 { |
|
1852 // Compute the length of the string in advance, so we can allocate a |
|
1853 // buffer of the right size on the first shot. |
|
1854 // |
|
1855 // (JS_smprintf would be perfect, as that allocates the result |
|
1856 // dynamically as it formats the string, but it won't allocate from cx, |
|
1857 // and wants us to use a special free function.) |
|
1858 char linenoBuf[15]; |
|
1859 size_t filenameLen = strlen(filename); |
|
1860 size_t linenoLen = JS_snprintf(linenoBuf, 15, "%u", lineno); |
|
1861 size_t introducerLen = strlen(introducer); |
|
1862 size_t len = filenameLen + |
|
1863 6 /* == strlen(" line ") */ + |
|
1864 linenoLen + |
|
1865 3 /* == strlen(" > ") */ + |
|
1866 introducerLen + |
|
1867 1 /* \0 */; |
|
1868 char *formatted = cx->pod_malloc<char>(len); |
|
1869 if (!formatted) |
|
1870 return nullptr; |
|
1871 mozilla::DebugOnly<size_t> checkLen = JS_snprintf(formatted, len, "%s line %s > %s", |
|
1872 filename, linenoBuf, introducer); |
|
1873 JS_ASSERT(checkLen == len - 1); |
|
1874 |
|
1875 return formatted; |
|
1876 } |
|
1877 |
|
1878 bool |
|
1879 ScriptSource::initFromOptions(ExclusiveContext *cx, const ReadOnlyCompileOptions &options) |
|
1880 { |
|
1881 JS_ASSERT(!filename_); |
|
1882 JS_ASSERT(!introducerFilename_); |
|
1883 |
|
1884 originPrincipals_ = options.originPrincipals(cx); |
|
1885 if (originPrincipals_) |
|
1886 JS_HoldPrincipals(originPrincipals_); |
|
1887 |
|
1888 introductionType_ = options.introductionType; |
|
1889 setIntroductionOffset(options.introductionOffset); |
|
1890 |
|
1891 if (options.hasIntroductionInfo) { |
|
1892 JS_ASSERT(options.introductionType != nullptr); |
|
1893 const char *filename = options.filename() ? options.filename() : "<unknown>"; |
|
1894 char *formatted = FormatIntroducedFilename(cx, filename, options.introductionLineno, |
|
1895 options.introductionType); |
|
1896 if (!formatted) |
|
1897 return false; |
|
1898 filename_ = formatted; |
|
1899 } else if (options.filename()) { |
|
1900 if (!setFilename(cx, options.filename())) |
|
1901 return false; |
|
1902 } |
|
1903 |
|
1904 if (options.introducerFilename()) { |
|
1905 introducerFilename_ = js_strdup(cx, options.introducerFilename()); |
|
1906 if (!introducerFilename_) |
|
1907 return false; |
|
1908 } else { |
|
1909 introducerFilename_ = filename_; |
|
1910 } |
|
1911 |
|
1912 return true; |
|
1913 } |
|
1914 |
|
1915 bool |
|
1916 ScriptSource::setFilename(ExclusiveContext *cx, const char *filename) |
|
1917 { |
|
1918 JS_ASSERT(!filename_); |
|
1919 filename_ = js_strdup(cx, filename); |
|
1920 if (!filename_) |
|
1921 return false; |
|
1922 return true; |
|
1923 } |
|
1924 |
|
1925 bool |
|
1926 ScriptSource::setDisplayURL(ExclusiveContext *cx, const jschar *displayURL) |
|
1927 { |
|
1928 JS_ASSERT(displayURL); |
|
1929 if (hasDisplayURL()) { |
|
1930 if (cx->isJSContext() && |
|
1931 !JS_ReportErrorFlagsAndNumber(cx->asJSContext(), JSREPORT_WARNING, |
|
1932 js_GetErrorMessage, nullptr, |
|
1933 JSMSG_ALREADY_HAS_PRAGMA, filename_, |
|
1934 "//# sourceURL")) |
|
1935 { |
|
1936 return false; |
|
1937 } |
|
1938 } |
|
1939 size_t len = js_strlen(displayURL) + 1; |
|
1940 if (len == 1) |
|
1941 return true; |
|
1942 displayURL_ = js_strdup(cx, displayURL); |
|
1943 if (!displayURL_) |
|
1944 return false; |
|
1945 return true; |
|
1946 } |
|
1947 |
|
1948 const jschar * |
|
1949 ScriptSource::displayURL() |
|
1950 { |
|
1951 JS_ASSERT(hasDisplayURL()); |
|
1952 return displayURL_; |
|
1953 } |
|
1954 |
|
1955 bool |
|
1956 ScriptSource::setSourceMapURL(ExclusiveContext *cx, const jschar *sourceMapURL) |
|
1957 { |
|
1958 JS_ASSERT(sourceMapURL); |
|
1959 if (hasSourceMapURL()) { |
|
1960 if (cx->isJSContext() && |
|
1961 !JS_ReportErrorFlagsAndNumber(cx->asJSContext(), JSREPORT_WARNING, |
|
1962 js_GetErrorMessage, nullptr, |
|
1963 JSMSG_ALREADY_HAS_PRAGMA, filename_, |
|
1964 "//# sourceMappingURL")) |
|
1965 { |
|
1966 return false; |
|
1967 } |
|
1968 } |
|
1969 |
|
1970 size_t len = js_strlen(sourceMapURL) + 1; |
|
1971 if (len == 1) |
|
1972 return true; |
|
1973 sourceMapURL_ = js_strdup(cx, sourceMapURL); |
|
1974 if (!sourceMapURL_) |
|
1975 return false; |
|
1976 return true; |
|
1977 } |
|
1978 |
|
1979 const jschar * |
|
1980 ScriptSource::sourceMapURL() |
|
1981 { |
|
1982 JS_ASSERT(hasSourceMapURL()); |
|
1983 return sourceMapURL_; |
|
1984 } |
|
1985 |
|
1986 /* |
|
1987 * Shared script data management. |
|
1988 */ |
|
1989 |
|
1990 SharedScriptData * |
|
1991 js::SharedScriptData::new_(ExclusiveContext *cx, uint32_t codeLength, |
|
1992 uint32_t srcnotesLength, uint32_t natoms) |
|
1993 { |
|
1994 /* |
|
1995 * Ensure the atoms are aligned, as some architectures don't allow unaligned |
|
1996 * access. |
|
1997 */ |
|
1998 const uint32_t pointerSize = sizeof(JSAtom *); |
|
1999 const uint32_t pointerMask = pointerSize - 1; |
|
2000 const uint32_t dataOffset = offsetof(SharedScriptData, data); |
|
2001 uint32_t baseLength = codeLength + srcnotesLength; |
|
2002 uint32_t padding = (pointerSize - ((baseLength + dataOffset) & pointerMask)) & pointerMask; |
|
2003 uint32_t length = baseLength + padding + pointerSize * natoms; |
|
2004 |
|
2005 SharedScriptData *entry = (SharedScriptData *)cx->malloc_(length + dataOffset); |
|
2006 if (!entry) |
|
2007 return nullptr; |
|
2008 |
|
2009 entry->length = length; |
|
2010 entry->natoms = natoms; |
|
2011 entry->marked = false; |
|
2012 memset(entry->data + baseLength, 0, padding); |
|
2013 |
|
2014 /* |
|
2015 * Call constructors to initialize the storage that will be accessed as a |
|
2016 * HeapPtrAtom array via atoms(). |
|
2017 */ |
|
2018 HeapPtrAtom *atoms = entry->atoms(); |
|
2019 JS_ASSERT(reinterpret_cast<uintptr_t>(atoms) % sizeof(JSAtom *) == 0); |
|
2020 for (unsigned i = 0; i < natoms; ++i) |
|
2021 new (&atoms[i]) HeapPtrAtom(); |
|
2022 |
|
2023 return entry; |
|
2024 } |
|
2025 |
|
2026 /* |
|
2027 * Takes ownership of its *ssd parameter and either adds it into the runtime's |
|
2028 * ScriptDataTable or frees it if a matching entry already exists. |
|
2029 * |
|
2030 * Sets the |code| and |atoms| fields on the given JSScript. |
|
2031 */ |
|
2032 static bool |
|
2033 SaveSharedScriptData(ExclusiveContext *cx, Handle<JSScript *> script, SharedScriptData *ssd, |
|
2034 uint32_t nsrcnotes) |
|
2035 { |
|
2036 ASSERT(script != nullptr); |
|
2037 ASSERT(ssd != nullptr); |
|
2038 |
|
2039 AutoLockForExclusiveAccess lock(cx); |
|
2040 |
|
2041 ScriptBytecodeHasher::Lookup l(ssd); |
|
2042 |
|
2043 ScriptDataTable::AddPtr p = cx->scriptDataTable().lookupForAdd(l); |
|
2044 if (p) { |
|
2045 js_free(ssd); |
|
2046 ssd = *p; |
|
2047 } else { |
|
2048 if (!cx->scriptDataTable().add(p, ssd)) { |
|
2049 script->setCode(nullptr); |
|
2050 script->atoms = nullptr; |
|
2051 js_free(ssd); |
|
2052 js_ReportOutOfMemory(cx); |
|
2053 return false; |
|
2054 } |
|
2055 } |
|
2056 |
|
2057 #ifdef JSGC_INCREMENTAL |
|
2058 /* |
|
2059 * During the IGC we need to ensure that bytecode is marked whenever it is |
|
2060 * accessed even if the bytecode was already in the table: at this point |
|
2061 * old scripts or exceptions pointing to the bytecode may no longer be |
|
2062 * reachable. This is effectively a read barrier. |
|
2063 */ |
|
2064 if (cx->isJSContext()) { |
|
2065 JSRuntime *rt = cx->asJSContext()->runtime(); |
|
2066 if (JS::IsIncrementalGCInProgress(rt) && rt->gcIsFull) |
|
2067 ssd->marked = true; |
|
2068 } |
|
2069 #endif |
|
2070 |
|
2071 script->setCode(ssd->data); |
|
2072 script->atoms = ssd->atoms(); |
|
2073 return true; |
|
2074 } |
|
2075 |
|
2076 static inline void |
|
2077 MarkScriptData(JSRuntime *rt, const jsbytecode *bytecode) |
|
2078 { |
|
2079 /* |
|
2080 * As an invariant, a ScriptBytecodeEntry should not be 'marked' outside of |
|
2081 * a GC. Since SweepScriptBytecodes is only called during a full gc, |
|
2082 * to preserve this invariant, only mark during a full gc. |
|
2083 */ |
|
2084 if (rt->gcIsFull) |
|
2085 SharedScriptData::fromBytecode(bytecode)->marked = true; |
|
2086 } |
|
2087 |
|
2088 void |
|
2089 js::UnmarkScriptData(JSRuntime *rt) |
|
2090 { |
|
2091 JS_ASSERT(rt->gcIsFull); |
|
2092 ScriptDataTable &table = rt->scriptDataTable(); |
|
2093 for (ScriptDataTable::Enum e(table); !e.empty(); e.popFront()) { |
|
2094 SharedScriptData *entry = e.front(); |
|
2095 entry->marked = false; |
|
2096 } |
|
2097 } |
|
2098 |
|
2099 void |
|
2100 js::SweepScriptData(JSRuntime *rt) |
|
2101 { |
|
2102 JS_ASSERT(rt->gcIsFull); |
|
2103 ScriptDataTable &table = rt->scriptDataTable(); |
|
2104 |
|
2105 if (rt->keepAtoms()) |
|
2106 return; |
|
2107 |
|
2108 for (ScriptDataTable::Enum e(table); !e.empty(); e.popFront()) { |
|
2109 SharedScriptData *entry = e.front(); |
|
2110 if (!entry->marked) { |
|
2111 js_free(entry); |
|
2112 e.removeFront(); |
|
2113 } |
|
2114 } |
|
2115 } |
|
2116 |
|
2117 void |
|
2118 js::FreeScriptData(JSRuntime *rt) |
|
2119 { |
|
2120 ScriptDataTable &table = rt->scriptDataTable(); |
|
2121 if (!table.initialized()) |
|
2122 return; |
|
2123 |
|
2124 for (ScriptDataTable::Enum e(table); !e.empty(); e.popFront()) |
|
2125 js_free(e.front()); |
|
2126 |
|
2127 table.clear(); |
|
2128 } |
|
2129 |
|
2130 /* |
|
2131 * JSScript::data and SharedScriptData::data have complex, |
|
2132 * manually-controlled, memory layouts. |
|
2133 * |
|
2134 * JSScript::data begins with some optional array headers. They are optional |
|
2135 * because they often aren't needed, i.e. the corresponding arrays often have |
|
2136 * zero elements. Each header has a bit in JSScript::hasArrayBits that |
|
2137 * indicates if it's present within |data|; from this the offset of each |
|
2138 * present array header can be computed. Each header has an accessor function |
|
2139 * in JSScript that encapsulates this offset computation. |
|
2140 * |
|
2141 * Array type Array elements Accessor |
|
2142 * ---------- -------------- -------- |
|
2143 * ConstArray Consts consts() |
|
2144 * ObjectArray Objects objects() |
|
2145 * ObjectArray Regexps regexps() |
|
2146 * TryNoteArray Try notes trynotes() |
|
2147 * BlockScopeArray Scope notes blockScopes() |
|
2148 * |
|
2149 * Then are the elements of several arrays. |
|
2150 * - Most of these arrays have headers listed above (if present). For each of |
|
2151 * these, the array pointer and the array length is stored in the header. |
|
2152 * - The remaining arrays have pointers and lengths that are stored directly in |
|
2153 * JSScript. This is because, unlike the others, they are nearly always |
|
2154 * non-zero length and so the optional-header space optimization isn't |
|
2155 * worthwhile. |
|
2156 * |
|
2157 * Array elements Pointed to by Length |
|
2158 * -------------- ------------- ------ |
|
2159 * Consts consts()->vector consts()->length |
|
2160 * Objects objects()->vector objects()->length |
|
2161 * Regexps regexps()->vector regexps()->length |
|
2162 * Try notes trynotes()->vector trynotes()->length |
|
2163 * Scope notes blockScopes()->vector blockScopes()->length |
|
2164 * |
|
2165 * IMPORTANT: This layout has two key properties. |
|
2166 * - It ensures that everything has sufficient alignment; in particular, the |
|
2167 * consts() elements need jsval alignment. |
|
2168 * - It ensures there are no gaps between elements, which saves space and makes |
|
2169 * manual layout easy. In particular, in the second part, arrays with larger |
|
2170 * elements precede arrays with smaller elements. |
|
2171 * |
|
2172 * SharedScriptData::data contains data that can be shared within a |
|
2173 * runtime. These items' layout is manually controlled to make it easier to |
|
2174 * manage both during (temporary) allocation and during matching against |
|
2175 * existing entries in the runtime. As the jsbytecode has to come first to |
|
2176 * enable lookup by bytecode identity, SharedScriptData::data, the atoms part |
|
2177 * has to manually be aligned sufficiently by adding padding after the notes |
|
2178 * part. |
|
2179 * |
|
2180 * Array elements Pointed to by Length |
|
2181 * -------------- ------------- ------ |
|
2182 * jsbytecode code length |
|
2183 * jsscrnote notes() numNotes() |
|
2184 * Atoms atoms natoms |
|
2185 * |
|
2186 * The following static assertions check JSScript::data's alignment properties. |
|
2187 */ |
|
2188 |
|
2189 #define KEEPS_JSVAL_ALIGNMENT(T) \ |
|
2190 (JS_ALIGNMENT_OF(jsval) % JS_ALIGNMENT_OF(T) == 0 && \ |
|
2191 sizeof(T) % sizeof(jsval) == 0) |
|
2192 |
|
2193 #define HAS_JSVAL_ALIGNMENT(T) \ |
|
2194 (JS_ALIGNMENT_OF(jsval) == JS_ALIGNMENT_OF(T) && \ |
|
2195 sizeof(T) == sizeof(jsval)) |
|
2196 |
|
2197 #define NO_PADDING_BETWEEN_ENTRIES(T1, T2) \ |
|
2198 (JS_ALIGNMENT_OF(T1) % JS_ALIGNMENT_OF(T2) == 0) |
|
2199 |
|
2200 /* |
|
2201 * These assertions ensure that there is no padding between the array headers, |
|
2202 * and also that the consts() elements (which follow immediately afterward) are |
|
2203 * jsval-aligned. (There is an assumption that |data| itself is jsval-aligned; |
|
2204 * we check this below). |
|
2205 */ |
|
2206 JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(ConstArray)); |
|
2207 JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(ObjectArray)); /* there are two of these */ |
|
2208 JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(TryNoteArray)); |
|
2209 JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(BlockScopeArray)); |
|
2210 |
|
2211 /* These assertions ensure there is no padding required between array elements. */ |
|
2212 JS_STATIC_ASSERT(HAS_JSVAL_ALIGNMENT(HeapValue)); |
|
2213 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapValue, HeapPtrObject)); |
|
2214 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapPtrObject, HeapPtrObject)); |
|
2215 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapPtrObject, JSTryNote)); |
|
2216 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(JSTryNote, uint32_t)); |
|
2217 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(uint32_t, uint32_t)); |
|
2218 |
|
2219 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapValue, BlockScopeNote)); |
|
2220 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(BlockScopeNote, BlockScopeNote)); |
|
2221 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(JSTryNote, BlockScopeNote)); |
|
2222 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapPtrObject, BlockScopeNote)); |
|
2223 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(BlockScopeNote, uint32_t)); |
|
2224 |
|
2225 static inline size_t |
|
2226 ScriptDataSize(uint32_t nbindings, uint32_t nconsts, uint32_t nobjects, uint32_t nregexps, |
|
2227 uint32_t ntrynotes, uint32_t nblockscopes) |
|
2228 { |
|
2229 size_t size = 0; |
|
2230 |
|
2231 if (nconsts != 0) |
|
2232 size += sizeof(ConstArray) + nconsts * sizeof(Value); |
|
2233 if (nobjects != 0) |
|
2234 size += sizeof(ObjectArray) + nobjects * sizeof(JSObject *); |
|
2235 if (nregexps != 0) |
|
2236 size += sizeof(ObjectArray) + nregexps * sizeof(JSObject *); |
|
2237 if (ntrynotes != 0) |
|
2238 size += sizeof(TryNoteArray) + ntrynotes * sizeof(JSTryNote); |
|
2239 if (nblockscopes != 0) |
|
2240 size += sizeof(BlockScopeArray) + nblockscopes * sizeof(BlockScopeNote); |
|
2241 |
|
2242 if (nbindings != 0) { |
|
2243 // Make sure bindings are sufficiently aligned. |
|
2244 size = JS_ROUNDUP(size, JS_ALIGNMENT_OF(Binding)) + nbindings * sizeof(Binding); |
|
2245 } |
|
2246 |
|
2247 return size; |
|
2248 } |
|
2249 |
|
2250 void |
|
2251 JSScript::initCompartment(ExclusiveContext *cx) |
|
2252 { |
|
2253 compartment_ = cx->compartment_; |
|
2254 } |
|
2255 |
|
2256 JSScript * |
|
2257 JSScript::Create(ExclusiveContext *cx, HandleObject enclosingScope, bool savedCallerFun, |
|
2258 const ReadOnlyCompileOptions &options, unsigned staticLevel, |
|
2259 HandleObject sourceObject, uint32_t bufStart, uint32_t bufEnd) |
|
2260 { |
|
2261 JS_ASSERT(bufStart <= bufEnd); |
|
2262 |
|
2263 RootedScript script(cx, js_NewGCScript(cx)); |
|
2264 if (!script) |
|
2265 return nullptr; |
|
2266 |
|
2267 PodZero(script.get()); |
|
2268 new (&script->bindings) Bindings; |
|
2269 |
|
2270 script->enclosingScopeOrOriginalFunction_ = enclosingScope; |
|
2271 script->savedCallerFun_ = savedCallerFun; |
|
2272 script->initCompartment(cx); |
|
2273 |
|
2274 script->compileAndGo_ = options.compileAndGo; |
|
2275 script->selfHosted_ = options.selfHostingMode; |
|
2276 script->noScriptRval_ = options.noScriptRval; |
|
2277 |
|
2278 script->version = options.version; |
|
2279 JS_ASSERT(script->getVersion() == options.version); // assert that no overflow occurred |
|
2280 |
|
2281 // This is an unsigned-to-uint16_t conversion, test for too-high values. |
|
2282 // In practice, recursion in Parser and/or BytecodeEmitter will blow the |
|
2283 // stack if we nest functions more than a few hundred deep, so this will |
|
2284 // never trigger. Oh well. |
|
2285 if (staticLevel > UINT16_MAX) { |
|
2286 if (cx->isJSContext()) { |
|
2287 JS_ReportErrorNumber(cx->asJSContext(), |
|
2288 js_GetErrorMessage, nullptr, JSMSG_TOO_DEEP, js_function_str); |
|
2289 } |
|
2290 return nullptr; |
|
2291 } |
|
2292 script->staticLevel_ = uint16_t(staticLevel); |
|
2293 |
|
2294 script->setSourceObject(sourceObject); |
|
2295 script->sourceStart_ = bufStart; |
|
2296 script->sourceEnd_ = bufEnd; |
|
2297 |
|
2298 return script; |
|
2299 } |
|
2300 |
|
2301 static inline uint8_t * |
|
2302 AllocScriptData(ExclusiveContext *cx, size_t size) |
|
2303 { |
|
2304 uint8_t *data = static_cast<uint8_t *>(cx->calloc_(JS_ROUNDUP(size, sizeof(Value)))); |
|
2305 if (!data) |
|
2306 return nullptr; |
|
2307 |
|
2308 // All script data is optional, so size might be 0. In that case, we don't care about alignment. |
|
2309 JS_ASSERT(size == 0 || size_t(data) % sizeof(Value) == 0); |
|
2310 return data; |
|
2311 } |
|
2312 |
|
2313 /* static */ bool |
|
2314 JSScript::partiallyInit(ExclusiveContext *cx, HandleScript script, uint32_t nconsts, |
|
2315 uint32_t nobjects, uint32_t nregexps, uint32_t ntrynotes, |
|
2316 uint32_t nblockscopes, uint32_t nTypeSets) |
|
2317 { |
|
2318 size_t size = ScriptDataSize(script->bindings.count(), nconsts, nobjects, nregexps, ntrynotes, |
|
2319 nblockscopes); |
|
2320 if (size > 0) { |
|
2321 script->data = AllocScriptData(cx, size); |
|
2322 if (!script->data) |
|
2323 return false; |
|
2324 } else { |
|
2325 script->data = nullptr; |
|
2326 } |
|
2327 script->dataSize_ = size; |
|
2328 |
|
2329 JS_ASSERT(nTypeSets <= UINT16_MAX); |
|
2330 script->nTypeSets_ = uint16_t(nTypeSets); |
|
2331 |
|
2332 uint8_t *cursor = script->data; |
|
2333 if (nconsts != 0) { |
|
2334 script->setHasArray(CONSTS); |
|
2335 cursor += sizeof(ConstArray); |
|
2336 } |
|
2337 if (nobjects != 0) { |
|
2338 script->setHasArray(OBJECTS); |
|
2339 cursor += sizeof(ObjectArray); |
|
2340 } |
|
2341 if (nregexps != 0) { |
|
2342 script->setHasArray(REGEXPS); |
|
2343 cursor += sizeof(ObjectArray); |
|
2344 } |
|
2345 if (ntrynotes != 0) { |
|
2346 script->setHasArray(TRYNOTES); |
|
2347 cursor += sizeof(TryNoteArray); |
|
2348 } |
|
2349 if (nblockscopes != 0) { |
|
2350 script->setHasArray(BLOCK_SCOPES); |
|
2351 cursor += sizeof(BlockScopeArray); |
|
2352 } |
|
2353 |
|
2354 if (nconsts != 0) { |
|
2355 JS_ASSERT(reinterpret_cast<uintptr_t>(cursor) % sizeof(jsval) == 0); |
|
2356 script->consts()->length = nconsts; |
|
2357 script->consts()->vector = (HeapValue *)cursor; |
|
2358 cursor += nconsts * sizeof(script->consts()->vector[0]); |
|
2359 } |
|
2360 |
|
2361 if (nobjects != 0) { |
|
2362 script->objects()->length = nobjects; |
|
2363 script->objects()->vector = (HeapPtr<JSObject> *)cursor; |
|
2364 cursor += nobjects * sizeof(script->objects()->vector[0]); |
|
2365 } |
|
2366 |
|
2367 if (nregexps != 0) { |
|
2368 script->regexps()->length = nregexps; |
|
2369 script->regexps()->vector = (HeapPtr<JSObject> *)cursor; |
|
2370 cursor += nregexps * sizeof(script->regexps()->vector[0]); |
|
2371 } |
|
2372 |
|
2373 if (ntrynotes != 0) { |
|
2374 script->trynotes()->length = ntrynotes; |
|
2375 script->trynotes()->vector = reinterpret_cast<JSTryNote *>(cursor); |
|
2376 size_t vectorSize = ntrynotes * sizeof(script->trynotes()->vector[0]); |
|
2377 #ifdef DEBUG |
|
2378 memset(cursor, 0, vectorSize); |
|
2379 #endif |
|
2380 cursor += vectorSize; |
|
2381 } |
|
2382 |
|
2383 if (nblockscopes != 0) { |
|
2384 script->blockScopes()->length = nblockscopes; |
|
2385 script->blockScopes()->vector = reinterpret_cast<BlockScopeNote *>(cursor); |
|
2386 size_t vectorSize = nblockscopes * sizeof(script->blockScopes()->vector[0]); |
|
2387 #ifdef DEBUG |
|
2388 memset(cursor, 0, vectorSize); |
|
2389 #endif |
|
2390 cursor += vectorSize; |
|
2391 } |
|
2392 |
|
2393 if (script->bindings.count() != 0) { |
|
2394 // Make sure bindings are sufficiently aligned. |
|
2395 cursor = reinterpret_cast<uint8_t*> |
|
2396 (JS_ROUNDUP(reinterpret_cast<uintptr_t>(cursor), JS_ALIGNMENT_OF(Binding))); |
|
2397 } |
|
2398 cursor = script->bindings.switchToScriptStorage(reinterpret_cast<Binding *>(cursor)); |
|
2399 |
|
2400 JS_ASSERT(cursor == script->data + size); |
|
2401 return true; |
|
2402 } |
|
2403 |
|
2404 /* static */ bool |
|
2405 JSScript::fullyInitTrivial(ExclusiveContext *cx, Handle<JSScript*> script) |
|
2406 { |
|
2407 if (!partiallyInit(cx, script, 0, 0, 0, 0, 0, 0)) |
|
2408 return false; |
|
2409 |
|
2410 SharedScriptData *ssd = SharedScriptData::new_(cx, 1, 1, 0); |
|
2411 if (!ssd) |
|
2412 return false; |
|
2413 |
|
2414 ssd->data[0] = JSOP_RETRVAL; |
|
2415 ssd->data[1] = SRC_NULL; |
|
2416 script->setLength(1); |
|
2417 return SaveSharedScriptData(cx, script, ssd, 1); |
|
2418 } |
|
2419 |
|
2420 /* static */ bool |
|
2421 JSScript::fullyInitFromEmitter(ExclusiveContext *cx, HandleScript script, BytecodeEmitter *bce) |
|
2422 { |
|
2423 /* The counts of indexed things must be checked during code generation. */ |
|
2424 JS_ASSERT(bce->atomIndices->count() <= INDEX_LIMIT); |
|
2425 JS_ASSERT(bce->objectList.length <= INDEX_LIMIT); |
|
2426 JS_ASSERT(bce->regexpList.length <= INDEX_LIMIT); |
|
2427 |
|
2428 uint32_t mainLength = bce->offset(); |
|
2429 uint32_t prologLength = bce->prologOffset(); |
|
2430 uint32_t nsrcnotes; |
|
2431 if (!FinishTakingSrcNotes(cx, bce, &nsrcnotes)) |
|
2432 return false; |
|
2433 uint32_t natoms = bce->atomIndices->count(); |
|
2434 if (!partiallyInit(cx, script, |
|
2435 bce->constList.length(), bce->objectList.length, bce->regexpList.length, |
|
2436 bce->tryNoteList.length(), bce->blockScopeList.length(), bce->typesetCount)) |
|
2437 { |
|
2438 return false; |
|
2439 } |
|
2440 |
|
2441 JS_ASSERT(script->mainOffset() == 0); |
|
2442 script->mainOffset_ = prologLength; |
|
2443 |
|
2444 script->lineno_ = bce->firstLine; |
|
2445 |
|
2446 script->setLength(prologLength + mainLength); |
|
2447 script->natoms_ = natoms; |
|
2448 SharedScriptData *ssd = SharedScriptData::new_(cx, script->length(), nsrcnotes, natoms); |
|
2449 if (!ssd) |
|
2450 return false; |
|
2451 |
|
2452 jsbytecode *code = ssd->data; |
|
2453 PodCopy<jsbytecode>(code, bce->prolog.code.begin(), prologLength); |
|
2454 PodCopy<jsbytecode>(code + prologLength, bce->code().begin(), mainLength); |
|
2455 CopySrcNotes(bce, (jssrcnote *)(code + script->length()), nsrcnotes); |
|
2456 InitAtomMap(bce->atomIndices.getMap(), ssd->atoms()); |
|
2457 |
|
2458 if (!SaveSharedScriptData(cx, script, ssd, nsrcnotes)) |
|
2459 return false; |
|
2460 |
|
2461 FunctionBox *funbox = bce->sc->isFunctionBox() ? bce->sc->asFunctionBox() : nullptr; |
|
2462 |
|
2463 if (bce->constList.length() != 0) |
|
2464 bce->constList.finish(script->consts()); |
|
2465 if (bce->objectList.length != 0) |
|
2466 bce->objectList.finish(script->objects()); |
|
2467 if (bce->regexpList.length != 0) |
|
2468 bce->regexpList.finish(script->regexps()); |
|
2469 if (bce->tryNoteList.length() != 0) |
|
2470 bce->tryNoteList.finish(script->trynotes()); |
|
2471 if (bce->blockScopeList.length() != 0) |
|
2472 bce->blockScopeList.finish(script->blockScopes()); |
|
2473 script->strict_ = bce->sc->strict; |
|
2474 script->explicitUseStrict_ = bce->sc->hasExplicitUseStrict(); |
|
2475 script->bindingsAccessedDynamically_ = bce->sc->bindingsAccessedDynamically(); |
|
2476 script->funHasExtensibleScope_ = funbox ? funbox->hasExtensibleScope() : false; |
|
2477 script->funNeedsDeclEnvObject_ = funbox ? funbox->needsDeclEnvObject() : false; |
|
2478 script->hasSingletons_ = bce->hasSingletons; |
|
2479 |
|
2480 if (funbox) { |
|
2481 if (funbox->argumentsHasLocalBinding()) { |
|
2482 // This must precede the script->bindings.transfer() call below |
|
2483 script->setArgumentsHasVarBinding(); |
|
2484 if (funbox->definitelyNeedsArgsObj()) |
|
2485 script->setNeedsArgsObj(true); |
|
2486 } else { |
|
2487 JS_ASSERT(!funbox->definitelyNeedsArgsObj()); |
|
2488 } |
|
2489 |
|
2490 script->funLength_ = funbox->length; |
|
2491 } |
|
2492 |
|
2493 RootedFunction fun(cx, nullptr); |
|
2494 if (funbox) { |
|
2495 JS_ASSERT(!bce->script->noScriptRval()); |
|
2496 script->isGeneratorExp_ = funbox->inGenexpLambda; |
|
2497 script->setGeneratorKind(funbox->generatorKind()); |
|
2498 script->setFunction(funbox->function()); |
|
2499 } |
|
2500 |
|
2501 // The call to nfixed() depends on the above setFunction() call. |
|
2502 if (UINT32_MAX - script->nfixed() < bce->maxStackDepth) { |
|
2503 bce->reportError(nullptr, JSMSG_NEED_DIET, "script"); |
|
2504 return false; |
|
2505 } |
|
2506 script->nslots_ = script->nfixed() + bce->maxStackDepth; |
|
2507 |
|
2508 for (unsigned i = 0, n = script->bindings.numArgs(); i < n; ++i) { |
|
2509 if (script->formalIsAliased(i)) { |
|
2510 script->funHasAnyAliasedFormal_ = true; |
|
2511 break; |
|
2512 } |
|
2513 } |
|
2514 |
|
2515 return true; |
|
2516 } |
|
2517 |
|
2518 size_t |
|
2519 JSScript::computedSizeOfData() const |
|
2520 { |
|
2521 return dataSize(); |
|
2522 } |
|
2523 |
|
2524 size_t |
|
2525 JSScript::sizeOfData(mozilla::MallocSizeOf mallocSizeOf) const |
|
2526 { |
|
2527 return mallocSizeOf(data); |
|
2528 } |
|
2529 |
|
2530 size_t |
|
2531 JSScript::sizeOfTypeScript(mozilla::MallocSizeOf mallocSizeOf) const |
|
2532 { |
|
2533 return types->sizeOfIncludingThis(mallocSizeOf); |
|
2534 } |
|
2535 |
|
2536 /* |
|
2537 * Nb: srcnotes are variable-length. This function computes the number of |
|
2538 * srcnote *slots*, which may be greater than the number of srcnotes. |
|
2539 */ |
|
2540 uint32_t |
|
2541 JSScript::numNotes() |
|
2542 { |
|
2543 jssrcnote *sn; |
|
2544 jssrcnote *notes_ = notes(); |
|
2545 for (sn = notes_; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) |
|
2546 continue; |
|
2547 return sn - notes_ + 1; /* +1 for the terminator */ |
|
2548 } |
|
2549 |
|
2550 js::GlobalObject& |
|
2551 JSScript::uninlinedGlobal() const |
|
2552 { |
|
2553 return global(); |
|
2554 } |
|
2555 |
|
2556 void |
|
2557 js::CallNewScriptHook(JSContext *cx, HandleScript script, HandleFunction fun) |
|
2558 { |
|
2559 if (script->selfHosted()) |
|
2560 return; |
|
2561 |
|
2562 JS_ASSERT(!script->isActiveEval()); |
|
2563 if (JSNewScriptHook hook = cx->runtime()->debugHooks.newScriptHook) { |
|
2564 AutoKeepAtoms keepAtoms(cx->perThreadData); |
|
2565 hook(cx, script->filename(), script->lineno(), script, fun, |
|
2566 cx->runtime()->debugHooks.newScriptHookData); |
|
2567 } |
|
2568 } |
|
2569 |
|
2570 void |
|
2571 js::CallDestroyScriptHook(FreeOp *fop, JSScript *script) |
|
2572 { |
|
2573 if (script->selfHosted()) |
|
2574 return; |
|
2575 |
|
2576 // The hook will only call into JS if a GC is not running. |
|
2577 if (JSDestroyScriptHook hook = fop->runtime()->debugHooks.destroyScriptHook) |
|
2578 hook(fop, script, fop->runtime()->debugHooks.destroyScriptHookData); |
|
2579 script->clearTraps(fop); |
|
2580 } |
|
2581 |
|
2582 void |
|
2583 JSScript::finalize(FreeOp *fop) |
|
2584 { |
|
2585 // NOTE: this JSScript may be partially initialized at this point. E.g. we |
|
2586 // may have created it and partially initialized it with |
|
2587 // JSScript::Create(), but not yet finished initializing it with |
|
2588 // fullyInitFromEmitter() or fullyInitTrivial(). |
|
2589 |
|
2590 CallDestroyScriptHook(fop, this); |
|
2591 fop->runtime()->spsProfiler.onScriptFinalized(this); |
|
2592 |
|
2593 if (types) |
|
2594 types->destroy(); |
|
2595 |
|
2596 #ifdef JS_ION |
|
2597 jit::DestroyIonScripts(fop, this); |
|
2598 #endif |
|
2599 |
|
2600 destroyScriptCounts(fop); |
|
2601 destroyDebugScript(fop); |
|
2602 |
|
2603 if (data) { |
|
2604 JS_POISON(data, 0xdb, computedSizeOfData()); |
|
2605 fop->free_(data); |
|
2606 } |
|
2607 |
|
2608 fop->runtime()->lazyScriptCache.remove(this); |
|
2609 } |
|
2610 |
|
2611 static const uint32_t GSN_CACHE_THRESHOLD = 100; |
|
2612 |
|
2613 void |
|
2614 GSNCache::purge() |
|
2615 { |
|
2616 code = nullptr; |
|
2617 if (map.initialized()) |
|
2618 map.finish(); |
|
2619 } |
|
2620 |
|
2621 jssrcnote * |
|
2622 js::GetSrcNote(GSNCache &cache, JSScript *script, jsbytecode *pc) |
|
2623 { |
|
2624 size_t target = pc - script->code(); |
|
2625 if (target >= script->length()) |
|
2626 return nullptr; |
|
2627 |
|
2628 if (cache.code == script->code()) { |
|
2629 JS_ASSERT(cache.map.initialized()); |
|
2630 GSNCache::Map::Ptr p = cache.map.lookup(pc); |
|
2631 return p ? p->value() : nullptr; |
|
2632 } |
|
2633 |
|
2634 size_t offset = 0; |
|
2635 jssrcnote *result; |
|
2636 for (jssrcnote *sn = script->notes(); ; sn = SN_NEXT(sn)) { |
|
2637 if (SN_IS_TERMINATOR(sn)) { |
|
2638 result = nullptr; |
|
2639 break; |
|
2640 } |
|
2641 offset += SN_DELTA(sn); |
|
2642 if (offset == target && SN_IS_GETTABLE(sn)) { |
|
2643 result = sn; |
|
2644 break; |
|
2645 } |
|
2646 } |
|
2647 |
|
2648 if (cache.code != script->code() && script->length() >= GSN_CACHE_THRESHOLD) { |
|
2649 unsigned nsrcnotes = 0; |
|
2650 for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); |
|
2651 sn = SN_NEXT(sn)) { |
|
2652 if (SN_IS_GETTABLE(sn)) |
|
2653 ++nsrcnotes; |
|
2654 } |
|
2655 if (cache.code) { |
|
2656 JS_ASSERT(cache.map.initialized()); |
|
2657 cache.map.finish(); |
|
2658 cache.code = nullptr; |
|
2659 } |
|
2660 if (cache.map.init(nsrcnotes)) { |
|
2661 pc = script->code(); |
|
2662 for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); |
|
2663 sn = SN_NEXT(sn)) { |
|
2664 pc += SN_DELTA(sn); |
|
2665 if (SN_IS_GETTABLE(sn)) |
|
2666 JS_ALWAYS_TRUE(cache.map.put(pc, sn)); |
|
2667 } |
|
2668 cache.code = script->code(); |
|
2669 } |
|
2670 } |
|
2671 |
|
2672 return result; |
|
2673 } |
|
2674 |
|
2675 jssrcnote * |
|
2676 js_GetSrcNote(JSContext *cx, JSScript *script, jsbytecode *pc) |
|
2677 { |
|
2678 return GetSrcNote(cx->runtime()->gsnCache, script, pc); |
|
2679 } |
|
2680 |
|
2681 unsigned |
|
2682 js::PCToLineNumber(unsigned startLine, jssrcnote *notes, jsbytecode *code, jsbytecode *pc, |
|
2683 unsigned *columnp) |
|
2684 { |
|
2685 unsigned lineno = startLine; |
|
2686 unsigned column = 0; |
|
2687 |
|
2688 /* |
|
2689 * Walk through source notes accumulating their deltas, keeping track of |
|
2690 * line-number notes, until we pass the note for pc's offset within |
|
2691 * script->code. |
|
2692 */ |
|
2693 ptrdiff_t offset = 0; |
|
2694 ptrdiff_t target = pc - code; |
|
2695 for (jssrcnote *sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { |
|
2696 offset += SN_DELTA(sn); |
|
2697 SrcNoteType type = (SrcNoteType) SN_TYPE(sn); |
|
2698 if (type == SRC_SETLINE) { |
|
2699 if (offset <= target) |
|
2700 lineno = (unsigned) js_GetSrcNoteOffset(sn, 0); |
|
2701 column = 0; |
|
2702 } else if (type == SRC_NEWLINE) { |
|
2703 if (offset <= target) |
|
2704 lineno++; |
|
2705 column = 0; |
|
2706 } |
|
2707 |
|
2708 if (offset > target) |
|
2709 break; |
|
2710 |
|
2711 if (type == SRC_COLSPAN) { |
|
2712 ptrdiff_t colspan = js_GetSrcNoteOffset(sn, 0); |
|
2713 |
|
2714 if (colspan >= SN_COLSPAN_DOMAIN / 2) |
|
2715 colspan -= SN_COLSPAN_DOMAIN; |
|
2716 JS_ASSERT(ptrdiff_t(column) + colspan >= 0); |
|
2717 column += colspan; |
|
2718 } |
|
2719 } |
|
2720 |
|
2721 if (columnp) |
|
2722 *columnp = column; |
|
2723 |
|
2724 return lineno; |
|
2725 } |
|
2726 |
|
2727 unsigned |
|
2728 js::PCToLineNumber(JSScript *script, jsbytecode *pc, unsigned *columnp) |
|
2729 { |
|
2730 /* Cope with InterpreterFrame.pc value prior to entering Interpret. */ |
|
2731 if (!pc) |
|
2732 return 0; |
|
2733 |
|
2734 return PCToLineNumber(script->lineno(), script->notes(), script->code(), pc, columnp); |
|
2735 } |
|
2736 |
|
2737 jsbytecode * |
|
2738 js_LineNumberToPC(JSScript *script, unsigned target) |
|
2739 { |
|
2740 ptrdiff_t offset = 0; |
|
2741 ptrdiff_t best = -1; |
|
2742 unsigned lineno = script->lineno(); |
|
2743 unsigned bestdiff = SN_MAX_OFFSET; |
|
2744 for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { |
|
2745 /* |
|
2746 * Exact-match only if offset is not in the prolog; otherwise use |
|
2747 * nearest greater-or-equal line number match. |
|
2748 */ |
|
2749 if (lineno == target && offset >= ptrdiff_t(script->mainOffset())) |
|
2750 goto out; |
|
2751 if (lineno >= target) { |
|
2752 unsigned diff = lineno - target; |
|
2753 if (diff < bestdiff) { |
|
2754 bestdiff = diff; |
|
2755 best = offset; |
|
2756 } |
|
2757 } |
|
2758 offset += SN_DELTA(sn); |
|
2759 SrcNoteType type = (SrcNoteType) SN_TYPE(sn); |
|
2760 if (type == SRC_SETLINE) { |
|
2761 lineno = (unsigned) js_GetSrcNoteOffset(sn, 0); |
|
2762 } else if (type == SRC_NEWLINE) { |
|
2763 lineno++; |
|
2764 } |
|
2765 } |
|
2766 if (best >= 0) |
|
2767 offset = best; |
|
2768 out: |
|
2769 return script->offsetToPC(offset); |
|
2770 } |
|
2771 |
|
2772 JS_FRIEND_API(unsigned) |
|
2773 js_GetScriptLineExtent(JSScript *script) |
|
2774 { |
|
2775 unsigned lineno = script->lineno(); |
|
2776 unsigned maxLineNo = lineno; |
|
2777 for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { |
|
2778 SrcNoteType type = (SrcNoteType) SN_TYPE(sn); |
|
2779 if (type == SRC_SETLINE) |
|
2780 lineno = (unsigned) js_GetSrcNoteOffset(sn, 0); |
|
2781 else if (type == SRC_NEWLINE) |
|
2782 lineno++; |
|
2783 |
|
2784 if (maxLineNo < lineno) |
|
2785 maxLineNo = lineno; |
|
2786 } |
|
2787 |
|
2788 return 1 + maxLineNo - script->lineno(); |
|
2789 } |
|
2790 |
|
2791 void |
|
2792 js::DescribeScriptedCallerForCompilation(JSContext *cx, MutableHandleScript maybeScript, |
|
2793 const char **file, unsigned *linenop, |
|
2794 uint32_t *pcOffset, JSPrincipals **origin, |
|
2795 LineOption opt) |
|
2796 { |
|
2797 if (opt == CALLED_FROM_JSOP_EVAL) { |
|
2798 jsbytecode *pc = nullptr; |
|
2799 maybeScript.set(cx->currentScript(&pc)); |
|
2800 JS_ASSERT(JSOp(*pc) == JSOP_EVAL || JSOp(*pc) == JSOP_SPREADEVAL); |
|
2801 JS_ASSERT(*(pc + (JSOp(*pc) == JSOP_EVAL ? JSOP_EVAL_LENGTH |
|
2802 : JSOP_SPREADEVAL_LENGTH)) == JSOP_LINENO); |
|
2803 *file = maybeScript->filename(); |
|
2804 *linenop = GET_UINT16(pc + (JSOp(*pc) == JSOP_EVAL ? JSOP_EVAL_LENGTH |
|
2805 : JSOP_SPREADEVAL_LENGTH)); |
|
2806 *pcOffset = pc - maybeScript->code(); |
|
2807 *origin = maybeScript->originPrincipals(); |
|
2808 return; |
|
2809 } |
|
2810 |
|
2811 NonBuiltinFrameIter iter(cx); |
|
2812 |
|
2813 if (iter.done()) { |
|
2814 maybeScript.set(nullptr); |
|
2815 *file = nullptr; |
|
2816 *linenop = 0; |
|
2817 *pcOffset = 0; |
|
2818 *origin = cx->compartment()->principals; |
|
2819 return; |
|
2820 } |
|
2821 |
|
2822 *file = iter.scriptFilename(); |
|
2823 *linenop = iter.computeLine(); |
|
2824 *origin = iter.originPrincipals(); |
|
2825 |
|
2826 // These values are only used for introducer fields which are debugging |
|
2827 // information and can be safely left null for asm.js frames. |
|
2828 if (iter.hasScript()) { |
|
2829 maybeScript.set(iter.script()); |
|
2830 *pcOffset = iter.pc() - maybeScript->code(); |
|
2831 } else { |
|
2832 maybeScript.set(nullptr); |
|
2833 *pcOffset = 0; |
|
2834 } |
|
2835 } |
|
2836 |
|
2837 template <class T> |
|
2838 static inline T * |
|
2839 Rebase(JSScript *dst, JSScript *src, T *srcp) |
|
2840 { |
|
2841 size_t off = reinterpret_cast<uint8_t *>(srcp) - src->data; |
|
2842 return reinterpret_cast<T *>(dst->data + off); |
|
2843 } |
|
2844 |
|
2845 JSScript * |
|
2846 js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, HandleScript src, |
|
2847 NewObjectKind newKind /* = GenericObject */) |
|
2848 { |
|
2849 /* NB: Keep this in sync with XDRScript. */ |
|
2850 |
|
2851 /* Some embeddings are not careful to use ExposeObjectToActiveJS as needed. */ |
|
2852 JS_ASSERT(!src->sourceObject()->isMarked(gc::GRAY)); |
|
2853 |
|
2854 uint32_t nconsts = src->hasConsts() ? src->consts()->length : 0; |
|
2855 uint32_t nobjects = src->hasObjects() ? src->objects()->length : 0; |
|
2856 uint32_t nregexps = src->hasRegexps() ? src->regexps()->length : 0; |
|
2857 uint32_t ntrynotes = src->hasTrynotes() ? src->trynotes()->length : 0; |
|
2858 uint32_t nblockscopes = src->hasBlockScopes() ? src->blockScopes()->length : 0; |
|
2859 |
|
2860 /* Script data */ |
|
2861 |
|
2862 size_t size = src->dataSize(); |
|
2863 uint8_t *data = AllocScriptData(cx, size); |
|
2864 if (!data) |
|
2865 return nullptr; |
|
2866 |
|
2867 /* Bindings */ |
|
2868 |
|
2869 Rooted<Bindings> bindings(cx); |
|
2870 InternalHandle<Bindings*> bindingsHandle = |
|
2871 InternalHandle<Bindings*>::fromMarkedLocation(bindings.address()); |
|
2872 if (!Bindings::clone(cx, bindingsHandle, data, src)) |
|
2873 return nullptr; |
|
2874 |
|
2875 /* Objects */ |
|
2876 |
|
2877 AutoObjectVector objects(cx); |
|
2878 if (nobjects != 0) { |
|
2879 HeapPtrObject *vector = src->objects()->vector; |
|
2880 for (unsigned i = 0; i < nobjects; i++) { |
|
2881 RootedObject obj(cx, vector[i]); |
|
2882 RootedObject clone(cx); |
|
2883 if (obj->is<NestedScopeObject>()) { |
|
2884 Rooted<NestedScopeObject*> innerBlock(cx, &obj->as<NestedScopeObject>()); |
|
2885 |
|
2886 RootedObject enclosingScope(cx); |
|
2887 if (NestedScopeObject *enclosingBlock = innerBlock->enclosingNestedScope()) |
|
2888 enclosingScope = objects[FindScopeObjectIndex(src, *enclosingBlock)]; |
|
2889 else |
|
2890 enclosingScope = fun; |
|
2891 |
|
2892 clone = CloneNestedScopeObject(cx, enclosingScope, innerBlock); |
|
2893 } else if (obj->is<JSFunction>()) { |
|
2894 RootedFunction innerFun(cx, &obj->as<JSFunction>()); |
|
2895 if (innerFun->isNative()) { |
|
2896 assertSameCompartment(cx, innerFun); |
|
2897 clone = innerFun; |
|
2898 } else { |
|
2899 if (innerFun->isInterpretedLazy()) { |
|
2900 AutoCompartment ac(cx, innerFun); |
|
2901 if (!innerFun->getOrCreateScript(cx)) |
|
2902 return nullptr; |
|
2903 } |
|
2904 RootedObject staticScope(cx, innerFun->nonLazyScript()->enclosingStaticScope()); |
|
2905 StaticScopeIter<CanGC> ssi(cx, staticScope); |
|
2906 RootedObject enclosingScope(cx); |
|
2907 if (ssi.done() || ssi.type() == StaticScopeIter<CanGC>::FUNCTION) |
|
2908 enclosingScope = fun; |
|
2909 else if (ssi.type() == StaticScopeIter<CanGC>::BLOCK) |
|
2910 enclosingScope = objects[FindScopeObjectIndex(src, ssi.block())]; |
|
2911 else |
|
2912 enclosingScope = objects[FindScopeObjectIndex(src, ssi.staticWith())]; |
|
2913 |
|
2914 clone = CloneFunctionAndScript(cx, enclosingScope, innerFun); |
|
2915 } |
|
2916 } else { |
|
2917 /* |
|
2918 * Clone object literals emitted for the JSOP_NEWOBJECT opcode. We only emit that |
|
2919 * instead of the less-optimized JSOP_NEWINIT for self-hosted code or code compiled |
|
2920 * with JSOPTION_COMPILE_N_GO set. As we don't clone the latter type of code, this |
|
2921 * case should only ever be hit when cloning objects from self-hosted code. |
|
2922 */ |
|
2923 clone = CloneObjectLiteral(cx, cx->global(), obj); |
|
2924 } |
|
2925 if (!clone || !objects.append(clone)) |
|
2926 return nullptr; |
|
2927 } |
|
2928 } |
|
2929 |
|
2930 /* RegExps */ |
|
2931 |
|
2932 AutoObjectVector regexps(cx); |
|
2933 for (unsigned i = 0; i < nregexps; i++) { |
|
2934 HeapPtrObject *vector = src->regexps()->vector; |
|
2935 for (unsigned i = 0; i < nregexps; i++) { |
|
2936 JSObject *clone = CloneScriptRegExpObject(cx, vector[i]->as<RegExpObject>()); |
|
2937 if (!clone || !regexps.append(clone)) |
|
2938 return nullptr; |
|
2939 } |
|
2940 } |
|
2941 |
|
2942 /* |
|
2943 * Wrap the script source object as needed. Self-hosted scripts may be |
|
2944 * in another runtime, so lazily create a new script source object to |
|
2945 * use for them. |
|
2946 */ |
|
2947 RootedObject sourceObject(cx); |
|
2948 if (cx->runtime()->isSelfHostingCompartment(src->compartment())) { |
|
2949 if (!cx->compartment()->selfHostingScriptSource) { |
|
2950 CompileOptions options(cx); |
|
2951 FillSelfHostingCompileOptions(options); |
|
2952 |
|
2953 ScriptSourceObject *obj = frontend::CreateScriptSourceObject(cx, options); |
|
2954 if (!obj) |
|
2955 return nullptr; |
|
2956 cx->compartment()->selfHostingScriptSource = obj; |
|
2957 } |
|
2958 sourceObject = cx->compartment()->selfHostingScriptSource; |
|
2959 } else { |
|
2960 sourceObject = src->sourceObject(); |
|
2961 if (!cx->compartment()->wrap(cx, &sourceObject)) |
|
2962 return nullptr; |
|
2963 } |
|
2964 |
|
2965 /* Now that all fallible allocation is complete, create the GC thing. */ |
|
2966 |
|
2967 CompileOptions options(cx); |
|
2968 options.setOriginPrincipals(src->originPrincipals()) |
|
2969 .setCompileAndGo(src->compileAndGo()) |
|
2970 .setSelfHostingMode(src->selfHosted()) |
|
2971 .setNoScriptRval(src->noScriptRval()) |
|
2972 .setVersion(src->getVersion()); |
|
2973 |
|
2974 RootedScript dst(cx, JSScript::Create(cx, enclosingScope, src->savedCallerFun(), |
|
2975 options, src->staticLevel(), |
|
2976 sourceObject, src->sourceStart(), src->sourceEnd())); |
|
2977 if (!dst) { |
|
2978 js_free(data); |
|
2979 return nullptr; |
|
2980 } |
|
2981 |
|
2982 dst->bindings = bindings; |
|
2983 |
|
2984 /* This assignment must occur before all the Rebase calls. */ |
|
2985 dst->data = data; |
|
2986 dst->dataSize_ = size; |
|
2987 memcpy(data, src->data, size); |
|
2988 |
|
2989 /* Script filenames, bytecodes and atoms are runtime-wide. */ |
|
2990 dst->setCode(src->code()); |
|
2991 dst->atoms = src->atoms; |
|
2992 |
|
2993 dst->setLength(src->length()); |
|
2994 dst->lineno_ = src->lineno(); |
|
2995 dst->mainOffset_ = src->mainOffset(); |
|
2996 dst->natoms_ = src->natoms(); |
|
2997 dst->funLength_ = src->funLength(); |
|
2998 dst->nTypeSets_ = src->nTypeSets(); |
|
2999 dst->nslots_ = src->nslots(); |
|
3000 if (src->argumentsHasVarBinding()) { |
|
3001 dst->setArgumentsHasVarBinding(); |
|
3002 if (src->analyzedArgsUsage()) |
|
3003 dst->setNeedsArgsObj(src->needsArgsObj()); |
|
3004 } |
|
3005 dst->cloneHasArray(src); |
|
3006 dst->strict_ = src->strict(); |
|
3007 dst->explicitUseStrict_ = src->explicitUseStrict(); |
|
3008 dst->bindingsAccessedDynamically_ = src->bindingsAccessedDynamically(); |
|
3009 dst->funHasExtensibleScope_ = src->funHasExtensibleScope(); |
|
3010 dst->funNeedsDeclEnvObject_ = src->funNeedsDeclEnvObject(); |
|
3011 dst->funHasAnyAliasedFormal_ = src->funHasAnyAliasedFormal(); |
|
3012 dst->hasSingletons_ = src->hasSingletons(); |
|
3013 dst->treatAsRunOnce_ = src->treatAsRunOnce(); |
|
3014 dst->isGeneratorExp_ = src->isGeneratorExp(); |
|
3015 dst->setGeneratorKind(src->generatorKind()); |
|
3016 |
|
3017 /* Copy over hints. */ |
|
3018 dst->shouldInline_ = src->shouldInline(); |
|
3019 dst->shouldCloneAtCallsite_ = src->shouldCloneAtCallsite(); |
|
3020 dst->isCallsiteClone_ = src->isCallsiteClone(); |
|
3021 |
|
3022 if (nconsts != 0) { |
|
3023 HeapValue *vector = Rebase<HeapValue>(dst, src, src->consts()->vector); |
|
3024 dst->consts()->vector = vector; |
|
3025 for (unsigned i = 0; i < nconsts; ++i) |
|
3026 JS_ASSERT_IF(vector[i].isMarkable(), vector[i].toString()->isAtom()); |
|
3027 } |
|
3028 if (nobjects != 0) { |
|
3029 HeapPtrObject *vector = Rebase<HeapPtr<JSObject> >(dst, src, src->objects()->vector); |
|
3030 dst->objects()->vector = vector; |
|
3031 for (unsigned i = 0; i < nobjects; ++i) |
|
3032 vector[i].init(objects[i]); |
|
3033 } |
|
3034 if (nregexps != 0) { |
|
3035 HeapPtrObject *vector = Rebase<HeapPtr<JSObject> >(dst, src, src->regexps()->vector); |
|
3036 dst->regexps()->vector = vector; |
|
3037 for (unsigned i = 0; i < nregexps; ++i) |
|
3038 vector[i].init(regexps[i]); |
|
3039 } |
|
3040 if (ntrynotes != 0) |
|
3041 dst->trynotes()->vector = Rebase<JSTryNote>(dst, src, src->trynotes()->vector); |
|
3042 if (nblockscopes != 0) |
|
3043 dst->blockScopes()->vector = Rebase<BlockScopeNote>(dst, src, src->blockScopes()->vector); |
|
3044 |
|
3045 return dst; |
|
3046 } |
|
3047 |
|
3048 bool |
|
3049 js::CloneFunctionScript(JSContext *cx, HandleFunction original, HandleFunction clone, |
|
3050 NewObjectKind newKind /* = GenericObject */) |
|
3051 { |
|
3052 JS_ASSERT(clone->isInterpreted()); |
|
3053 |
|
3054 RootedScript script(cx, clone->nonLazyScript()); |
|
3055 JS_ASSERT(script); |
|
3056 JS_ASSERT(script->compartment() == original->compartment()); |
|
3057 JS_ASSERT_IF(script->compartment() != cx->compartment(), |
|
3058 !script->enclosingStaticScope()); |
|
3059 |
|
3060 RootedObject scope(cx, script->enclosingStaticScope()); |
|
3061 |
|
3062 clone->mutableScript().init(nullptr); |
|
3063 |
|
3064 JSScript *cscript = CloneScript(cx, scope, clone, script, newKind); |
|
3065 if (!cscript) |
|
3066 return false; |
|
3067 |
|
3068 clone->setScript(cscript); |
|
3069 cscript->setFunction(clone); |
|
3070 |
|
3071 script = clone->nonLazyScript(); |
|
3072 CallNewScriptHook(cx, script, clone); |
|
3073 RootedGlobalObject global(cx, script->compileAndGo() ? &script->global() : nullptr); |
|
3074 Debugger::onNewScript(cx, script, global); |
|
3075 |
|
3076 return true; |
|
3077 } |
|
3078 |
|
3079 DebugScript * |
|
3080 JSScript::debugScript() |
|
3081 { |
|
3082 JS_ASSERT(hasDebugScript_); |
|
3083 DebugScriptMap *map = compartment()->debugScriptMap; |
|
3084 JS_ASSERT(map); |
|
3085 DebugScriptMap::Ptr p = map->lookup(this); |
|
3086 JS_ASSERT(p); |
|
3087 return p->value(); |
|
3088 } |
|
3089 |
|
3090 DebugScript * |
|
3091 JSScript::releaseDebugScript() |
|
3092 { |
|
3093 JS_ASSERT(hasDebugScript_); |
|
3094 DebugScriptMap *map = compartment()->debugScriptMap; |
|
3095 JS_ASSERT(map); |
|
3096 DebugScriptMap::Ptr p = map->lookup(this); |
|
3097 JS_ASSERT(p); |
|
3098 DebugScript *debug = p->value(); |
|
3099 map->remove(p); |
|
3100 hasDebugScript_ = false; |
|
3101 return debug; |
|
3102 } |
|
3103 |
|
3104 void |
|
3105 JSScript::destroyDebugScript(FreeOp *fop) |
|
3106 { |
|
3107 if (hasDebugScript_) { |
|
3108 for (jsbytecode *pc = code(); pc < codeEnd(); pc++) { |
|
3109 if (BreakpointSite *site = getBreakpointSite(pc)) { |
|
3110 /* Breakpoints are swept before finalization. */ |
|
3111 JS_ASSERT(site->firstBreakpoint() == nullptr); |
|
3112 site->clearTrap(fop, nullptr, nullptr); |
|
3113 JS_ASSERT(getBreakpointSite(pc) == nullptr); |
|
3114 } |
|
3115 } |
|
3116 fop->free_(releaseDebugScript()); |
|
3117 } |
|
3118 } |
|
3119 |
|
3120 bool |
|
3121 JSScript::ensureHasDebugScript(JSContext *cx) |
|
3122 { |
|
3123 if (hasDebugScript_) |
|
3124 return true; |
|
3125 |
|
3126 size_t nbytes = offsetof(DebugScript, breakpoints) + length() * sizeof(BreakpointSite*); |
|
3127 DebugScript *debug = (DebugScript *) cx->calloc_(nbytes); |
|
3128 if (!debug) |
|
3129 return false; |
|
3130 |
|
3131 /* Create compartment's debugScriptMap if necessary. */ |
|
3132 DebugScriptMap *map = compartment()->debugScriptMap; |
|
3133 if (!map) { |
|
3134 map = cx->new_<DebugScriptMap>(); |
|
3135 if (!map || !map->init()) { |
|
3136 js_free(debug); |
|
3137 js_delete(map); |
|
3138 return false; |
|
3139 } |
|
3140 compartment()->debugScriptMap = map; |
|
3141 } |
|
3142 |
|
3143 if (!map->putNew(this, debug)) { |
|
3144 js_free(debug); |
|
3145 return false; |
|
3146 } |
|
3147 hasDebugScript_ = true; // safe to set this; we can't fail after this point |
|
3148 |
|
3149 /* |
|
3150 * Ensure that any Interpret() instances running on this script have |
|
3151 * interrupts enabled. The interrupts must stay enabled until the |
|
3152 * debug state is destroyed. |
|
3153 */ |
|
3154 for (ActivationIterator iter(cx->runtime()); !iter.done(); ++iter) { |
|
3155 if (iter->isInterpreter()) |
|
3156 iter->asInterpreter()->enableInterruptsIfRunning(this); |
|
3157 } |
|
3158 |
|
3159 return true; |
|
3160 } |
|
3161 |
|
3162 void |
|
3163 JSScript::setNewStepMode(FreeOp *fop, uint32_t newValue) |
|
3164 { |
|
3165 DebugScript *debug = debugScript(); |
|
3166 uint32_t prior = debug->stepMode; |
|
3167 debug->stepMode = newValue; |
|
3168 |
|
3169 if (!prior != !newValue) { |
|
3170 #ifdef JS_ION |
|
3171 if (hasBaselineScript()) |
|
3172 baseline->toggleDebugTraps(this, nullptr); |
|
3173 #endif |
|
3174 |
|
3175 if (!stepModeEnabled() && !debug->numSites) |
|
3176 fop->free_(releaseDebugScript()); |
|
3177 } |
|
3178 } |
|
3179 |
|
3180 bool |
|
3181 JSScript::setStepModeFlag(JSContext *cx, bool step) |
|
3182 { |
|
3183 if (!ensureHasDebugScript(cx)) |
|
3184 return false; |
|
3185 |
|
3186 setNewStepMode(cx->runtime()->defaultFreeOp(), |
|
3187 (debugScript()->stepMode & stepCountMask) | |
|
3188 (step ? stepFlagMask : 0)); |
|
3189 return true; |
|
3190 } |
|
3191 |
|
3192 bool |
|
3193 JSScript::incrementStepModeCount(JSContext *cx) |
|
3194 { |
|
3195 assertSameCompartment(cx, this); |
|
3196 MOZ_ASSERT(cx->compartment()->debugMode()); |
|
3197 |
|
3198 if (!ensureHasDebugScript(cx)) |
|
3199 return false; |
|
3200 |
|
3201 DebugScript *debug = debugScript(); |
|
3202 uint32_t count = debug->stepMode & stepCountMask; |
|
3203 MOZ_ASSERT(((count + 1) & stepCountMask) == count + 1); |
|
3204 |
|
3205 setNewStepMode(cx->runtime()->defaultFreeOp(), |
|
3206 (debug->stepMode & stepFlagMask) | |
|
3207 ((count + 1) & stepCountMask)); |
|
3208 return true; |
|
3209 } |
|
3210 |
|
3211 void |
|
3212 JSScript::decrementStepModeCount(FreeOp *fop) |
|
3213 { |
|
3214 DebugScript *debug = debugScript(); |
|
3215 uint32_t count = debug->stepMode & stepCountMask; |
|
3216 |
|
3217 setNewStepMode(fop, |
|
3218 (debug->stepMode & stepFlagMask) | |
|
3219 ((count - 1) & stepCountMask)); |
|
3220 } |
|
3221 |
|
3222 BreakpointSite * |
|
3223 JSScript::getOrCreateBreakpointSite(JSContext *cx, jsbytecode *pc) |
|
3224 { |
|
3225 if (!ensureHasDebugScript(cx)) |
|
3226 return nullptr; |
|
3227 |
|
3228 DebugScript *debug = debugScript(); |
|
3229 BreakpointSite *&site = debug->breakpoints[pcToOffset(pc)]; |
|
3230 |
|
3231 if (!site) { |
|
3232 site = cx->runtime()->new_<BreakpointSite>(this, pc); |
|
3233 if (!site) { |
|
3234 js_ReportOutOfMemory(cx); |
|
3235 return nullptr; |
|
3236 } |
|
3237 debug->numSites++; |
|
3238 } |
|
3239 |
|
3240 return site; |
|
3241 } |
|
3242 |
|
3243 void |
|
3244 JSScript::destroyBreakpointSite(FreeOp *fop, jsbytecode *pc) |
|
3245 { |
|
3246 DebugScript *debug = debugScript(); |
|
3247 BreakpointSite *&site = debug->breakpoints[pcToOffset(pc)]; |
|
3248 JS_ASSERT(site); |
|
3249 |
|
3250 fop->delete_(site); |
|
3251 site = nullptr; |
|
3252 |
|
3253 if (--debug->numSites == 0 && !stepModeEnabled()) |
|
3254 fop->free_(releaseDebugScript()); |
|
3255 } |
|
3256 |
|
3257 void |
|
3258 JSScript::clearBreakpointsIn(FreeOp *fop, js::Debugger *dbg, JSObject *handler) |
|
3259 { |
|
3260 if (!hasAnyBreakpointsOrStepMode()) |
|
3261 return; |
|
3262 |
|
3263 for (jsbytecode *pc = code(); pc < codeEnd(); pc++) { |
|
3264 BreakpointSite *site = getBreakpointSite(pc); |
|
3265 if (site) { |
|
3266 Breakpoint *nextbp; |
|
3267 for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) { |
|
3268 nextbp = bp->nextInSite(); |
|
3269 if ((!dbg || bp->debugger == dbg) && (!handler || bp->getHandler() == handler)) |
|
3270 bp->destroy(fop); |
|
3271 } |
|
3272 } |
|
3273 } |
|
3274 } |
|
3275 |
|
3276 bool |
|
3277 JSScript::hasBreakpointsAt(jsbytecode *pc) |
|
3278 { |
|
3279 BreakpointSite *site = getBreakpointSite(pc); |
|
3280 if (!site) |
|
3281 return false; |
|
3282 |
|
3283 return site->enabledCount > 0 || site->trapHandler; |
|
3284 } |
|
3285 |
|
3286 void |
|
3287 JSScript::clearTraps(FreeOp *fop) |
|
3288 { |
|
3289 if (!hasAnyBreakpointsOrStepMode()) |
|
3290 return; |
|
3291 |
|
3292 for (jsbytecode *pc = code(); pc < codeEnd(); pc++) { |
|
3293 BreakpointSite *site = getBreakpointSite(pc); |
|
3294 if (site) |
|
3295 site->clearTrap(fop); |
|
3296 } |
|
3297 } |
|
3298 |
|
3299 void |
|
3300 JSScript::markChildren(JSTracer *trc) |
|
3301 { |
|
3302 // NOTE: this JSScript may be partially initialized at this point. E.g. we |
|
3303 // may have created it and partially initialized it with |
|
3304 // JSScript::Create(), but not yet finished initializing it with |
|
3305 // fullyInitFromEmitter() or fullyInitTrivial(). |
|
3306 |
|
3307 JS_ASSERT_IF(trc->runtime()->gcStrictCompartmentChecking, zone()->isCollecting()); |
|
3308 |
|
3309 for (uint32_t i = 0; i < natoms(); ++i) { |
|
3310 if (atoms[i]) |
|
3311 MarkString(trc, &atoms[i], "atom"); |
|
3312 } |
|
3313 |
|
3314 if (hasObjects()) { |
|
3315 ObjectArray *objarray = objects(); |
|
3316 MarkObjectRange(trc, objarray->length, objarray->vector, "objects"); |
|
3317 } |
|
3318 |
|
3319 if (hasRegexps()) { |
|
3320 ObjectArray *objarray = regexps(); |
|
3321 MarkObjectRange(trc, objarray->length, objarray->vector, "objects"); |
|
3322 } |
|
3323 |
|
3324 if (hasConsts()) { |
|
3325 ConstArray *constarray = consts(); |
|
3326 MarkValueRange(trc, constarray->length, constarray->vector, "consts"); |
|
3327 } |
|
3328 |
|
3329 if (sourceObject()) { |
|
3330 JS_ASSERT(sourceObject()->compartment() == compartment()); |
|
3331 MarkObject(trc, &sourceObject_, "sourceObject"); |
|
3332 } |
|
3333 |
|
3334 if (functionNonDelazifying()) |
|
3335 MarkObject(trc, &function_, "function"); |
|
3336 |
|
3337 if (enclosingScopeOrOriginalFunction_) |
|
3338 MarkObject(trc, &enclosingScopeOrOriginalFunction_, "enclosing"); |
|
3339 |
|
3340 if (maybeLazyScript()) |
|
3341 MarkLazyScriptUnbarriered(trc, &lazyScript, "lazyScript"); |
|
3342 |
|
3343 if (IS_GC_MARKING_TRACER(trc)) { |
|
3344 compartment()->mark(); |
|
3345 |
|
3346 if (code()) |
|
3347 MarkScriptData(trc->runtime(), code()); |
|
3348 } |
|
3349 |
|
3350 bindings.trace(trc); |
|
3351 |
|
3352 if (hasAnyBreakpointsOrStepMode()) { |
|
3353 for (unsigned i = 0; i < length(); i++) { |
|
3354 BreakpointSite *site = debugScript()->breakpoints[i]; |
|
3355 if (site && site->trapHandler) |
|
3356 MarkValue(trc, &site->trapClosure, "trap closure"); |
|
3357 } |
|
3358 } |
|
3359 |
|
3360 #ifdef JS_ION |
|
3361 jit::TraceIonScripts(trc, this); |
|
3362 #endif |
|
3363 } |
|
3364 |
|
3365 void |
|
3366 LazyScript::markChildren(JSTracer *trc) |
|
3367 { |
|
3368 if (function_) |
|
3369 MarkObject(trc, &function_, "function"); |
|
3370 |
|
3371 if (sourceObject_) |
|
3372 MarkObject(trc, &sourceObject_, "sourceObject"); |
|
3373 |
|
3374 if (enclosingScope_) |
|
3375 MarkObject(trc, &enclosingScope_, "enclosingScope"); |
|
3376 |
|
3377 if (script_) |
|
3378 MarkScript(trc, &script_, "realScript"); |
|
3379 |
|
3380 HeapPtrAtom *freeVariables = this->freeVariables(); |
|
3381 for (size_t i = 0; i < numFreeVariables(); i++) |
|
3382 MarkString(trc, &freeVariables[i], "lazyScriptFreeVariable"); |
|
3383 |
|
3384 HeapPtrFunction *innerFunctions = this->innerFunctions(); |
|
3385 for (size_t i = 0; i < numInnerFunctions(); i++) |
|
3386 MarkObject(trc, &innerFunctions[i], "lazyScriptInnerFunction"); |
|
3387 } |
|
3388 |
|
3389 void |
|
3390 LazyScript::finalize(FreeOp *fop) |
|
3391 { |
|
3392 if (table_) |
|
3393 fop->free_(table_); |
|
3394 } |
|
3395 |
|
3396 NestedScopeObject * |
|
3397 JSScript::getStaticScope(jsbytecode *pc) |
|
3398 { |
|
3399 JS_ASSERT(containsPC(pc)); |
|
3400 |
|
3401 if (!hasBlockScopes()) |
|
3402 return nullptr; |
|
3403 |
|
3404 ptrdiff_t offset = pc - main(); |
|
3405 |
|
3406 if (offset < 0) |
|
3407 return nullptr; |
|
3408 |
|
3409 BlockScopeArray *scopes = blockScopes(); |
|
3410 NestedScopeObject *blockChain = nullptr; |
|
3411 |
|
3412 // Find the innermost block chain using a binary search. |
|
3413 size_t bottom = 0; |
|
3414 size_t top = scopes->length; |
|
3415 |
|
3416 while (bottom < top) { |
|
3417 size_t mid = bottom + (top - bottom) / 2; |
|
3418 const BlockScopeNote *note = &scopes->vector[mid]; |
|
3419 if (note->start <= offset) { |
|
3420 // Block scopes are ordered in the list by their starting offset, and since |
|
3421 // blocks form a tree ones earlier in the list may cover the pc even if |
|
3422 // later blocks end before the pc. This only happens when the earlier block |
|
3423 // is a parent of the later block, so we need to check parents of |mid| in |
|
3424 // the searched range for coverage. |
|
3425 size_t check = mid; |
|
3426 while (check >= bottom) { |
|
3427 const BlockScopeNote *checkNote = &scopes->vector[check]; |
|
3428 JS_ASSERT(checkNote->start <= offset); |
|
3429 if (offset < checkNote->start + checkNote->length) { |
|
3430 // We found a matching block chain but there may be inner ones |
|
3431 // at a higher block chain index than mid. Continue the binary search. |
|
3432 if (checkNote->index == BlockScopeNote::NoBlockScopeIndex) |
|
3433 blockChain = nullptr; |
|
3434 else |
|
3435 blockChain = &getObject(checkNote->index)->as<NestedScopeObject>(); |
|
3436 break; |
|
3437 } |
|
3438 if (checkNote->parent == UINT32_MAX) |
|
3439 break; |
|
3440 check = checkNote->parent; |
|
3441 } |
|
3442 bottom = mid + 1; |
|
3443 } else { |
|
3444 top = mid; |
|
3445 } |
|
3446 } |
|
3447 |
|
3448 return blockChain; |
|
3449 } |
|
3450 |
|
3451 void |
|
3452 JSScript::setArgumentsHasVarBinding() |
|
3453 { |
|
3454 argsHasVarBinding_ = true; |
|
3455 #ifdef JS_ION |
|
3456 needsArgsAnalysis_ = true; |
|
3457 #else |
|
3458 // The arguments analysis is performed by IonBuilder. |
|
3459 needsArgsObj_ = true; |
|
3460 #endif |
|
3461 } |
|
3462 |
|
3463 void |
|
3464 JSScript::setNeedsArgsObj(bool needsArgsObj) |
|
3465 { |
|
3466 JS_ASSERT_IF(needsArgsObj, argumentsHasVarBinding()); |
|
3467 needsArgsAnalysis_ = false; |
|
3468 needsArgsObj_ = needsArgsObj; |
|
3469 } |
|
3470 |
|
3471 void |
|
3472 js::SetFrameArgumentsObject(JSContext *cx, AbstractFramePtr frame, |
|
3473 HandleScript script, JSObject *argsobj) |
|
3474 { |
|
3475 /* |
|
3476 * Replace any optimized arguments in the frame with an explicit arguments |
|
3477 * object. Note that 'arguments' may have already been overwritten. |
|
3478 */ |
|
3479 |
|
3480 InternalBindingsHandle bindings(script, &script->bindings); |
|
3481 const uint32_t var = Bindings::argumentsVarIndex(cx, bindings); |
|
3482 |
|
3483 if (script->varIsAliased(var)) { |
|
3484 /* |
|
3485 * Scan the script to find the slot in the call object that 'arguments' |
|
3486 * is assigned to. |
|
3487 */ |
|
3488 jsbytecode *pc = script->code(); |
|
3489 while (*pc != JSOP_ARGUMENTS) |
|
3490 pc += GetBytecodeLength(pc); |
|
3491 pc += JSOP_ARGUMENTS_LENGTH; |
|
3492 JS_ASSERT(*pc == JSOP_SETALIASEDVAR); |
|
3493 |
|
3494 // Note that here and below, it is insufficient to only check for |
|
3495 // JS_OPTIMIZED_ARGUMENTS, as Ion could have optimized out the |
|
3496 // arguments slot. |
|
3497 if (IsOptimizedPlaceholderMagicValue(frame.callObj().as<ScopeObject>().aliasedVar(pc))) |
|
3498 frame.callObj().as<ScopeObject>().setAliasedVar(cx, pc, cx->names().arguments, ObjectValue(*argsobj)); |
|
3499 } else { |
|
3500 if (IsOptimizedPlaceholderMagicValue(frame.unaliasedLocal(var))) |
|
3501 frame.unaliasedLocal(var) = ObjectValue(*argsobj); |
|
3502 } |
|
3503 } |
|
3504 |
|
3505 /* static */ bool |
|
3506 JSScript::argumentsOptimizationFailed(JSContext *cx, HandleScript script) |
|
3507 { |
|
3508 JS_ASSERT(script->functionNonDelazifying()); |
|
3509 JS_ASSERT(script->analyzedArgsUsage()); |
|
3510 JS_ASSERT(script->argumentsHasVarBinding()); |
|
3511 |
|
3512 /* |
|
3513 * It is possible that the arguments optimization has already failed, |
|
3514 * everything has been fixed up, but there was an outstanding magic value |
|
3515 * on the stack that has just now flowed into an apply. In this case, there |
|
3516 * is nothing to do; GuardFunApplySpeculation will patch in the real |
|
3517 * argsobj. |
|
3518 */ |
|
3519 if (script->needsArgsObj()) |
|
3520 return true; |
|
3521 |
|
3522 JS_ASSERT(!script->isGenerator()); |
|
3523 |
|
3524 script->needsArgsObj_ = true; |
|
3525 |
|
3526 #ifdef JS_ION |
|
3527 /* |
|
3528 * Since we can't invalidate baseline scripts, set a flag that's checked from |
|
3529 * JIT code to indicate the arguments optimization failed and JSOP_ARGUMENTS |
|
3530 * should create an arguments object next time. |
|
3531 */ |
|
3532 if (script->hasBaselineScript()) |
|
3533 script->baselineScript()->setNeedsArgsObj(); |
|
3534 #endif |
|
3535 |
|
3536 /* |
|
3537 * By design, the arguments optimization is only made when there are no |
|
3538 * outstanding cases of MagicValue(JS_OPTIMIZED_ARGUMENTS) at any points |
|
3539 * where the optimization could fail, other than an active invocation of |
|
3540 * 'f.apply(x, arguments)'. Thus, there are no outstanding values of |
|
3541 * MagicValue(JS_OPTIMIZED_ARGUMENTS) on the stack. However, there are |
|
3542 * three things that need fixup: |
|
3543 * - there may be any number of activations of this script that don't have |
|
3544 * an argsObj that now need one. |
|
3545 * - jit code compiled (and possible active on the stack) with the static |
|
3546 * assumption of !script->needsArgsObj(); |
|
3547 * - type inference data for the script assuming script->needsArgsObj |
|
3548 */ |
|
3549 for (AllFramesIter i(cx); !i.done(); ++i) { |
|
3550 /* |
|
3551 * We cannot reliably create an arguments object for Ion activations of |
|
3552 * this script. To maintain the invariant that "script->needsArgsObj |
|
3553 * implies fp->hasArgsObj", the Ion bail mechanism will create an |
|
3554 * arguments object right after restoring the BaselineFrame and before |
|
3555 * entering Baseline code (in jit::FinishBailoutToBaseline). |
|
3556 */ |
|
3557 if (i.isIon()) |
|
3558 continue; |
|
3559 AbstractFramePtr frame = i.abstractFramePtr(); |
|
3560 if (frame.isFunctionFrame() && frame.script() == script) { |
|
3561 ArgumentsObject *argsobj = ArgumentsObject::createExpected(cx, frame); |
|
3562 if (!argsobj) { |
|
3563 /* |
|
3564 * We can't leave stack frames with script->needsArgsObj but no |
|
3565 * arguments object. It is, however, safe to leave frames with |
|
3566 * an arguments object but !script->needsArgsObj. |
|
3567 */ |
|
3568 script->needsArgsObj_ = false; |
|
3569 return false; |
|
3570 } |
|
3571 |
|
3572 SetFrameArgumentsObject(cx, frame, script, argsobj); |
|
3573 } |
|
3574 } |
|
3575 |
|
3576 return true; |
|
3577 } |
|
3578 |
|
3579 bool |
|
3580 JSScript::varIsAliased(uint32_t varSlot) |
|
3581 { |
|
3582 return bindings.bindingIsAliased(bindings.numArgs() + varSlot); |
|
3583 } |
|
3584 |
|
3585 bool |
|
3586 JSScript::formalIsAliased(unsigned argSlot) |
|
3587 { |
|
3588 return bindings.bindingIsAliased(argSlot); |
|
3589 } |
|
3590 |
|
3591 bool |
|
3592 JSScript::formalLivesInArgumentsObject(unsigned argSlot) |
|
3593 { |
|
3594 return argsObjAliasesFormals() && !formalIsAliased(argSlot); |
|
3595 } |
|
3596 |
|
3597 LazyScript::LazyScript(JSFunction *fun, void *table, uint64_t packedFields, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column) |
|
3598 : script_(nullptr), |
|
3599 function_(fun), |
|
3600 enclosingScope_(nullptr), |
|
3601 sourceObject_(nullptr), |
|
3602 table_(table), |
|
3603 packedFields_(packedFields), |
|
3604 begin_(begin), |
|
3605 end_(end), |
|
3606 lineno_(lineno), |
|
3607 column_(column) |
|
3608 { |
|
3609 JS_ASSERT(begin <= end); |
|
3610 } |
|
3611 |
|
3612 void |
|
3613 LazyScript::initScript(JSScript *script) |
|
3614 { |
|
3615 JS_ASSERT(script && !script_); |
|
3616 script_ = script; |
|
3617 } |
|
3618 |
|
3619 void |
|
3620 LazyScript::resetScript() |
|
3621 { |
|
3622 JS_ASSERT(script_); |
|
3623 script_ = nullptr; |
|
3624 } |
|
3625 |
|
3626 void |
|
3627 LazyScript::setParent(JSObject *enclosingScope, ScriptSourceObject *sourceObject) |
|
3628 { |
|
3629 JS_ASSERT(!sourceObject_ && !enclosingScope_); |
|
3630 JS_ASSERT_IF(enclosingScope, function_->compartment() == enclosingScope->compartment()); |
|
3631 JS_ASSERT(function_->compartment() == sourceObject->compartment()); |
|
3632 |
|
3633 enclosingScope_ = enclosingScope; |
|
3634 sourceObject_ = sourceObject; |
|
3635 } |
|
3636 |
|
3637 ScriptSourceObject * |
|
3638 LazyScript::sourceObject() const |
|
3639 { |
|
3640 return sourceObject_ ? &sourceObject_->as<ScriptSourceObject>() : nullptr; |
|
3641 } |
|
3642 |
|
3643 /* static */ LazyScript * |
|
3644 LazyScript::CreateRaw(ExclusiveContext *cx, HandleFunction fun, |
|
3645 uint64_t packedFields, uint32_t begin, uint32_t end, |
|
3646 uint32_t lineno, uint32_t column) |
|
3647 { |
|
3648 union { |
|
3649 PackedView p; |
|
3650 uint64_t packed; |
|
3651 }; |
|
3652 |
|
3653 packed = packedFields; |
|
3654 |
|
3655 // Reset runtime flags to obtain a fresh LazyScript. |
|
3656 p.hasBeenCloned = false; |
|
3657 p.treatAsRunOnce = false; |
|
3658 |
|
3659 size_t bytes = (p.numFreeVariables * sizeof(HeapPtrAtom)) |
|
3660 + (p.numInnerFunctions * sizeof(HeapPtrFunction)); |
|
3661 |
|
3662 ScopedJSFreePtr<void> table(bytes ? cx->malloc_(bytes) : nullptr); |
|
3663 if (bytes && !table) |
|
3664 return nullptr; |
|
3665 |
|
3666 LazyScript *res = js_NewGCLazyScript(cx); |
|
3667 if (!res) |
|
3668 return nullptr; |
|
3669 |
|
3670 cx->compartment()->scheduleDelazificationForDebugMode(); |
|
3671 |
|
3672 return new (res) LazyScript(fun, table.forget(), packed, begin, end, lineno, column); |
|
3673 } |
|
3674 |
|
3675 /* static */ LazyScript * |
|
3676 LazyScript::CreateRaw(ExclusiveContext *cx, HandleFunction fun, |
|
3677 uint32_t numFreeVariables, uint32_t numInnerFunctions, JSVersion version, |
|
3678 uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column) |
|
3679 { |
|
3680 union { |
|
3681 PackedView p; |
|
3682 uint64_t packedFields; |
|
3683 }; |
|
3684 |
|
3685 p.version = version; |
|
3686 p.numFreeVariables = numFreeVariables; |
|
3687 p.numInnerFunctions = numInnerFunctions; |
|
3688 p.generatorKindBits = GeneratorKindAsBits(NotGenerator); |
|
3689 p.strict = false; |
|
3690 p.bindingsAccessedDynamically = false; |
|
3691 p.hasDebuggerStatement = false; |
|
3692 p.directlyInsideEval = false; |
|
3693 p.usesArgumentsAndApply = false; |
|
3694 |
|
3695 LazyScript *res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column); |
|
3696 JS_ASSERT_IF(res, res->version() == version); |
|
3697 return res; |
|
3698 } |
|
3699 |
|
3700 /* static */ LazyScript * |
|
3701 LazyScript::Create(ExclusiveContext *cx, HandleFunction fun, |
|
3702 uint64_t packedFields, uint32_t begin, uint32_t end, |
|
3703 uint32_t lineno, uint32_t column) |
|
3704 { |
|
3705 // Dummy atom which is not a valid property name. |
|
3706 RootedAtom dummyAtom(cx, cx->names().comma); |
|
3707 |
|
3708 // Dummy function which is not a valid function as this is the one which is |
|
3709 // holding this lazy script. |
|
3710 HandleFunction dummyFun = fun; |
|
3711 |
|
3712 LazyScript *res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column); |
|
3713 if (!res) |
|
3714 return nullptr; |
|
3715 |
|
3716 // Fill with dummies, to be GC-safe after the initialization of the free |
|
3717 // variables and inner functions. |
|
3718 size_t i, num; |
|
3719 HeapPtrAtom *variables = res->freeVariables(); |
|
3720 for (i = 0, num = res->numFreeVariables(); i < num; i++) |
|
3721 variables[i].init(dummyAtom); |
|
3722 |
|
3723 HeapPtrFunction *functions = res->innerFunctions(); |
|
3724 for (i = 0, num = res->numInnerFunctions(); i < num; i++) |
|
3725 functions[i].init(dummyFun); |
|
3726 |
|
3727 return res; |
|
3728 } |
|
3729 |
|
3730 void |
|
3731 LazyScript::initRuntimeFields(uint64_t packedFields) |
|
3732 { |
|
3733 union { |
|
3734 PackedView p; |
|
3735 uint64_t packed; |
|
3736 }; |
|
3737 |
|
3738 packed = packedFields; |
|
3739 p_.hasBeenCloned = p.hasBeenCloned; |
|
3740 p_.treatAsRunOnce = p.treatAsRunOnce; |
|
3741 } |
|
3742 |
|
3743 bool |
|
3744 LazyScript::hasUncompiledEnclosingScript() const |
|
3745 { |
|
3746 // It can happen that we created lazy scripts while compiling an enclosing |
|
3747 // script, but we errored out while compiling that script. When we iterate |
|
3748 // over lazy script in a compartment, we might see lazy scripts that never |
|
3749 // escaped to script and should be ignored. |
|
3750 // |
|
3751 // If the enclosing scope is a function with a null script or has a script |
|
3752 // without code, it was not successfully compiled. |
|
3753 |
|
3754 if (!enclosingScope() || !enclosingScope()->is<JSFunction>()) |
|
3755 return false; |
|
3756 |
|
3757 JSFunction &fun = enclosingScope()->as<JSFunction>(); |
|
3758 return fun.isInterpreted() && (!fun.mutableScript() || !fun.nonLazyScript()->code()); |
|
3759 } |
|
3760 |
|
3761 uint32_t |
|
3762 LazyScript::staticLevel(JSContext *cx) const |
|
3763 { |
|
3764 for (StaticScopeIter<NoGC> ssi(enclosingScope()); !ssi.done(); ssi++) { |
|
3765 if (ssi.type() == StaticScopeIter<NoGC>::FUNCTION) |
|
3766 return ssi.funScript()->staticLevel() + 1; |
|
3767 } |
|
3768 return 1; |
|
3769 } |
|
3770 |
|
3771 void |
|
3772 JSScript::updateBaselineOrIonRaw() |
|
3773 { |
|
3774 #ifdef JS_ION |
|
3775 if (hasIonScript()) { |
|
3776 baselineOrIonRaw = ion->method()->raw(); |
|
3777 baselineOrIonSkipArgCheck = ion->method()->raw() + ion->getSkipArgCheckEntryOffset(); |
|
3778 } else if (hasBaselineScript()) { |
|
3779 baselineOrIonRaw = baseline->method()->raw(); |
|
3780 baselineOrIonSkipArgCheck = baseline->method()->raw(); |
|
3781 } else { |
|
3782 baselineOrIonRaw = nullptr; |
|
3783 baselineOrIonSkipArgCheck = nullptr; |
|
3784 } |
|
3785 #endif |
|
3786 } |
|
3787 |
|
3788 bool |
|
3789 JSScript::hasLoops() |
|
3790 { |
|
3791 if (!hasTrynotes()) |
|
3792 return false; |
|
3793 JSTryNote *tn = trynotes()->vector; |
|
3794 JSTryNote *tnlimit = tn + trynotes()->length; |
|
3795 for (; tn < tnlimit; tn++) { |
|
3796 if (tn->kind == JSTRY_ITER || tn->kind == JSTRY_LOOP) |
|
3797 return true; |
|
3798 } |
|
3799 return false; |
|
3800 } |
|
3801 |
|
3802 static inline void |
|
3803 LazyScriptHash(uint32_t lineno, uint32_t column, uint32_t begin, uint32_t end, |
|
3804 HashNumber hashes[3]) |
|
3805 { |
|
3806 HashNumber hash = lineno; |
|
3807 hash = RotateLeft(hash, 4) ^ column; |
|
3808 hash = RotateLeft(hash, 4) ^ begin; |
|
3809 hash = RotateLeft(hash, 4) ^ end; |
|
3810 |
|
3811 hashes[0] = hash; |
|
3812 hashes[1] = RotateLeft(hashes[0], 4) ^ begin; |
|
3813 hashes[2] = RotateLeft(hashes[1], 4) ^ end; |
|
3814 } |
|
3815 |
|
3816 void |
|
3817 LazyScriptHashPolicy::hash(const Lookup &lookup, HashNumber hashes[3]) |
|
3818 { |
|
3819 LazyScript *lazy = lookup.lazy; |
|
3820 LazyScriptHash(lazy->lineno(), lazy->column(), lazy->begin(), lazy->end(), hashes); |
|
3821 } |
|
3822 |
|
3823 void |
|
3824 LazyScriptHashPolicy::hash(JSScript *script, HashNumber hashes[3]) |
|
3825 { |
|
3826 LazyScriptHash(script->lineno(), script->column(), script->sourceStart(), script->sourceEnd(), hashes); |
|
3827 } |
|
3828 |
|
3829 bool |
|
3830 LazyScriptHashPolicy::match(JSScript *script, const Lookup &lookup) |
|
3831 { |
|
3832 JSContext *cx = lookup.cx; |
|
3833 LazyScript *lazy = lookup.lazy; |
|
3834 |
|
3835 // To be a match, the script and lazy script need to have the same line |
|
3836 // and column and to be at the same position within their respective |
|
3837 // source blobs, and to have the same source contents and version. |
|
3838 // |
|
3839 // While the surrounding code in the source may differ, this is |
|
3840 // sufficient to ensure that compiling the lazy script will yield an |
|
3841 // identical result to compiling the original script. |
|
3842 // |
|
3843 // Note that the filenames and origin principals of the lazy script and |
|
3844 // original script can differ. If there is a match, these will be fixed |
|
3845 // up in the resulting clone by the caller. |
|
3846 |
|
3847 if (script->lineno() != lazy->lineno() || |
|
3848 script->column() != lazy->column() || |
|
3849 script->getVersion() != lazy->version() || |
|
3850 script->sourceStart() != lazy->begin() || |
|
3851 script->sourceEnd() != lazy->end()) |
|
3852 { |
|
3853 return false; |
|
3854 } |
|
3855 |
|
3856 SourceDataCache::AutoHoldEntry holder; |
|
3857 |
|
3858 const jschar *scriptChars = script->scriptSource()->chars(cx, holder); |
|
3859 if (!scriptChars) |
|
3860 return false; |
|
3861 |
|
3862 const jschar *lazyChars = lazy->source()->chars(cx, holder); |
|
3863 if (!lazyChars) |
|
3864 return false; |
|
3865 |
|
3866 size_t begin = script->sourceStart(); |
|
3867 size_t length = script->sourceEnd() - begin; |
|
3868 return !memcmp(scriptChars + begin, lazyChars + begin, length); |
|
3869 } |