1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jsscript.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,3869 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* 1.11 + * JS script operations. 1.12 + */ 1.13 + 1.14 +#include "jsscriptinlines.h" 1.15 + 1.16 +#include "mozilla/DebugOnly.h" 1.17 +#include "mozilla/MathAlgorithms.h" 1.18 +#include "mozilla/MemoryReporting.h" 1.19 +#include "mozilla/PodOperations.h" 1.20 + 1.21 +#include <string.h> 1.22 + 1.23 +#include "jsapi.h" 1.24 +#include "jsatom.h" 1.25 +#include "jscntxt.h" 1.26 +#include "jsfun.h" 1.27 +#include "jsgc.h" 1.28 +#include "jsobj.h" 1.29 +#include "jsopcode.h" 1.30 +#include "jsprf.h" 1.31 +#include "jstypes.h" 1.32 +#include "jsutil.h" 1.33 +#include "jswrapper.h" 1.34 + 1.35 +#include "frontend/BytecodeCompiler.h" 1.36 +#include "frontend/BytecodeEmitter.h" 1.37 +#include "frontend/SharedContext.h" 1.38 +#include "gc/Marking.h" 1.39 +#include "jit/BaselineJIT.h" 1.40 +#include "jit/IonCode.h" 1.41 +#include "js/MemoryMetrics.h" 1.42 +#include "js/OldDebugAPI.h" 1.43 +#include "js/Utility.h" 1.44 +#include "vm/ArgumentsObject.h" 1.45 +#include "vm/Compression.h" 1.46 +#include "vm/Debugger.h" 1.47 +#include "vm/Opcodes.h" 1.48 +#include "vm/SelfHosting.h" 1.49 +#include "vm/Shape.h" 1.50 +#include "vm/Xdr.h" 1.51 + 1.52 +#include "jsfuninlines.h" 1.53 +#include "jsinferinlines.h" 1.54 +#include "jsobjinlines.h" 1.55 + 1.56 +#include "vm/ScopeObject-inl.h" 1.57 +#include "vm/Stack-inl.h" 1.58 + 1.59 +using namespace js; 1.60 +using namespace js::gc; 1.61 +using namespace js::frontend; 1.62 + 1.63 +using mozilla::PodCopy; 1.64 +using mozilla::PodZero; 1.65 +using mozilla::RotateLeft; 1.66 + 1.67 +typedef Rooted<GlobalObject *> RootedGlobalObject; 1.68 + 1.69 +/* static */ uint32_t 1.70 +Bindings::argumentsVarIndex(ExclusiveContext *cx, InternalBindingsHandle bindings) 1.71 +{ 1.72 + HandlePropertyName arguments = cx->names().arguments; 1.73 + BindingIter bi(bindings); 1.74 + while (bi->name() != arguments) 1.75 + bi++; 1.76 + return bi.frameIndex(); 1.77 +} 1.78 + 1.79 +bool 1.80 +Bindings::initWithTemporaryStorage(ExclusiveContext *cx, InternalBindingsHandle self, 1.81 + unsigned numArgs, uint32_t numVars, 1.82 + Binding *bindingArray, uint32_t numBlockScoped) 1.83 +{ 1.84 + JS_ASSERT(!self->callObjShape_); 1.85 + JS_ASSERT(self->bindingArrayAndFlag_ == TEMPORARY_STORAGE_BIT); 1.86 + JS_ASSERT(!(uintptr_t(bindingArray) & TEMPORARY_STORAGE_BIT)); 1.87 + JS_ASSERT(numArgs <= ARGC_LIMIT); 1.88 + JS_ASSERT(numVars <= LOCALNO_LIMIT); 1.89 + JS_ASSERT(numBlockScoped <= LOCALNO_LIMIT); 1.90 + JS_ASSERT(numVars <= LOCALNO_LIMIT - numBlockScoped); 1.91 + JS_ASSERT(UINT32_MAX - numArgs >= numVars + numBlockScoped); 1.92 + 1.93 + self->bindingArrayAndFlag_ = uintptr_t(bindingArray) | TEMPORARY_STORAGE_BIT; 1.94 + self->numArgs_ = numArgs; 1.95 + self->numVars_ = numVars; 1.96 + self->numBlockScoped_ = numBlockScoped; 1.97 + 1.98 + // Get the initial shape to use when creating CallObjects for this script. 1.99 + // After creation, a CallObject's shape may change completely (via direct eval() or 1.100 + // other operations that mutate the lexical scope). However, since the 1.101 + // lexical bindings added to the initial shape are permanent and the 1.102 + // allocKind/nfixed of a CallObject cannot change, one may assume that the 1.103 + // slot location (whether in the fixed or dynamic slots) of a variable is 1.104 + // the same as in the initial shape. (This is assumed by the interpreter and 1.105 + // JITs when interpreting/compiling aliasedvar ops.) 1.106 + 1.107 + // Since unaliased variables are, by definition, only accessed by local 1.108 + // operations and never through the scope chain, only give shapes to 1.109 + // aliased variables. While the debugger may observe any scope object at 1.110 + // any time, such accesses are mediated by DebugScopeProxy (see 1.111 + // DebugScopeProxy::handleUnaliasedAccess). 1.112 + uint32_t nslots = CallObject::RESERVED_SLOTS; 1.113 + for (BindingIter bi(self); bi; bi++) { 1.114 + if (bi->aliased()) 1.115 + nslots++; 1.116 + } 1.117 + 1.118 + // Put as many of nslots inline into the object header as possible. 1.119 + uint32_t nfixed = gc::GetGCKindSlots(gc::GetGCObjectKind(nslots)); 1.120 + 1.121 + // Start with the empty shape and then append one shape per aliased binding. 1.122 + RootedShape shape(cx, 1.123 + EmptyShape::getInitialShape(cx, &CallObject::class_, nullptr, nullptr, nullptr, 1.124 + nfixed, BaseShape::VAROBJ | BaseShape::DELEGATE)); 1.125 + if (!shape) 1.126 + return false; 1.127 + 1.128 +#ifdef DEBUG 1.129 + HashSet<PropertyName *> added(cx); 1.130 + if (!added.init()) 1.131 + return false; 1.132 +#endif 1.133 + 1.134 + uint32_t slot = CallObject::RESERVED_SLOTS; 1.135 + for (BindingIter bi(self); bi; bi++) { 1.136 + if (!bi->aliased()) 1.137 + continue; 1.138 + 1.139 +#ifdef DEBUG 1.140 + // The caller ensures no duplicate aliased names. 1.141 + JS_ASSERT(!added.has(bi->name())); 1.142 + if (!added.put(bi->name())) 1.143 + return false; 1.144 +#endif 1.145 + 1.146 + StackBaseShape stackBase(cx, &CallObject::class_, nullptr, nullptr, 1.147 + BaseShape::VAROBJ | BaseShape::DELEGATE); 1.148 + 1.149 + UnownedBaseShape *base = BaseShape::getUnowned(cx, stackBase); 1.150 + if (!base) 1.151 + return false; 1.152 + 1.153 + unsigned attrs = JSPROP_PERMANENT | 1.154 + JSPROP_ENUMERATE | 1.155 + (bi->kind() == Binding::CONSTANT ? JSPROP_READONLY : 0); 1.156 + StackShape child(base, NameToId(bi->name()), slot, attrs, 0); 1.157 + 1.158 + shape = cx->compartment()->propertyTree.getChild(cx, shape, child); 1.159 + if (!shape) 1.160 + return false; 1.161 + 1.162 + JS_ASSERT(slot < nslots); 1.163 + slot++; 1.164 + } 1.165 + JS_ASSERT(slot == nslots); 1.166 + 1.167 + JS_ASSERT(!shape->inDictionary()); 1.168 + self->callObjShape_.init(shape); 1.169 + return true; 1.170 +} 1.171 + 1.172 +uint8_t * 1.173 +Bindings::switchToScriptStorage(Binding *newBindingArray) 1.174 +{ 1.175 + JS_ASSERT(bindingArrayUsingTemporaryStorage()); 1.176 + JS_ASSERT(!(uintptr_t(newBindingArray) & TEMPORARY_STORAGE_BIT)); 1.177 + 1.178 + if (count() > 0) 1.179 + PodCopy(newBindingArray, bindingArray(), count()); 1.180 + bindingArrayAndFlag_ = uintptr_t(newBindingArray); 1.181 + return reinterpret_cast<uint8_t *>(newBindingArray + count()); 1.182 +} 1.183 + 1.184 +bool 1.185 +Bindings::clone(JSContext *cx, InternalBindingsHandle self, 1.186 + uint8_t *dstScriptData, HandleScript srcScript) 1.187 +{ 1.188 + /* The clone has the same bindingArray_ offset as 'src'. */ 1.189 + Bindings &src = srcScript->bindings; 1.190 + ptrdiff_t off = (uint8_t *)src.bindingArray() - srcScript->data; 1.191 + JS_ASSERT(off >= 0); 1.192 + JS_ASSERT(size_t(off) <= srcScript->dataSize()); 1.193 + Binding *dstPackedBindings = (Binding *)(dstScriptData + off); 1.194 + 1.195 + /* 1.196 + * Since atoms are shareable throughout the runtime, we can simply copy 1.197 + * the source's bindingArray directly. 1.198 + */ 1.199 + if (!initWithTemporaryStorage(cx, self, src.numArgs(), src.numVars(), src.bindingArray(), 1.200 + src.numBlockScoped())) 1.201 + return false; 1.202 + self->switchToScriptStorage(dstPackedBindings); 1.203 + return true; 1.204 +} 1.205 + 1.206 +/* static */ Bindings 1.207 +GCMethods<Bindings>::initial() 1.208 +{ 1.209 + return Bindings(); 1.210 +} 1.211 + 1.212 +template<XDRMode mode> 1.213 +static bool 1.214 +XDRScriptBindings(XDRState<mode> *xdr, LifoAllocScope &las, unsigned numArgs, uint32_t numVars, 1.215 + HandleScript script, unsigned numBlockScoped) 1.216 +{ 1.217 + JSContext *cx = xdr->cx(); 1.218 + 1.219 + if (mode == XDR_ENCODE) { 1.220 + for (BindingIter bi(script); bi; bi++) { 1.221 + RootedAtom atom(cx, bi->name()); 1.222 + if (!XDRAtom(xdr, &atom)) 1.223 + return false; 1.224 + } 1.225 + 1.226 + for (BindingIter bi(script); bi; bi++) { 1.227 + uint8_t u8 = (uint8_t(bi->kind()) << 1) | uint8_t(bi->aliased()); 1.228 + if (!xdr->codeUint8(&u8)) 1.229 + return false; 1.230 + } 1.231 + } else { 1.232 + uint32_t nameCount = numArgs + numVars; 1.233 + 1.234 + AutoValueVector atoms(cx); 1.235 + if (!atoms.resize(nameCount)) 1.236 + return false; 1.237 + for (uint32_t i = 0; i < nameCount; i++) { 1.238 + RootedAtom atom(cx); 1.239 + if (!XDRAtom(xdr, &atom)) 1.240 + return false; 1.241 + atoms[i] = StringValue(atom); 1.242 + } 1.243 + 1.244 + Binding *bindingArray = las.alloc().newArrayUninitialized<Binding>(nameCount); 1.245 + if (!bindingArray) 1.246 + return false; 1.247 + for (uint32_t i = 0; i < nameCount; i++) { 1.248 + uint8_t u8; 1.249 + if (!xdr->codeUint8(&u8)) 1.250 + return false; 1.251 + 1.252 + PropertyName *name = atoms[i].toString()->asAtom().asPropertyName(); 1.253 + Binding::Kind kind = Binding::Kind(u8 >> 1); 1.254 + bool aliased = bool(u8 & 1); 1.255 + 1.256 + bindingArray[i] = Binding(name, kind, aliased); 1.257 + } 1.258 + 1.259 + InternalBindingsHandle bindings(script, &script->bindings); 1.260 + if (!Bindings::initWithTemporaryStorage(cx, bindings, numArgs, numVars, bindingArray, 1.261 + numBlockScoped)) 1.262 + return false; 1.263 + } 1.264 + 1.265 + return true; 1.266 +} 1.267 + 1.268 +bool 1.269 +Bindings::bindingIsAliased(uint32_t bindingIndex) 1.270 +{ 1.271 + JS_ASSERT(bindingIndex < count()); 1.272 + return bindingArray()[bindingIndex].aliased(); 1.273 +} 1.274 + 1.275 +void 1.276 +Bindings::trace(JSTracer *trc) 1.277 +{ 1.278 + if (callObjShape_) 1.279 + MarkShape(trc, &callObjShape_, "callObjShape"); 1.280 + 1.281 + /* 1.282 + * As the comment in Bindings explains, bindingsArray may point into freed 1.283 + * storage when bindingArrayUsingTemporaryStorage so we don't mark it. 1.284 + * Note: during compilation, atoms are already kept alive by gcKeepAtoms. 1.285 + */ 1.286 + if (bindingArrayUsingTemporaryStorage()) 1.287 + return; 1.288 + 1.289 + for (Binding *b = bindingArray(), *end = b + count(); b != end; b++) { 1.290 + PropertyName *name = b->name(); 1.291 + MarkStringUnbarriered(trc, &name, "bindingArray"); 1.292 + } 1.293 +} 1.294 + 1.295 +bool 1.296 +js::FillBindingVector(HandleScript fromScript, BindingVector *vec) 1.297 +{ 1.298 + for (BindingIter bi(fromScript); bi; bi++) { 1.299 + if (!vec->append(*bi)) 1.300 + return false; 1.301 + } 1.302 + 1.303 + return true; 1.304 +} 1.305 + 1.306 +template<XDRMode mode> 1.307 +bool 1.308 +js::XDRScriptConst(XDRState<mode> *xdr, MutableHandleValue vp) 1.309 +{ 1.310 + JSContext *cx = xdr->cx(); 1.311 + 1.312 + /* 1.313 + * A script constant can be an arbitrary primitive value as they are used 1.314 + * to implement JSOP_LOOKUPSWITCH. But they cannot be objects, see 1.315 + * bug 407186. 1.316 + */ 1.317 + enum ConstTag { 1.318 + SCRIPT_INT = 0, 1.319 + SCRIPT_DOUBLE = 1, 1.320 + SCRIPT_ATOM = 2, 1.321 + SCRIPT_TRUE = 3, 1.322 + SCRIPT_FALSE = 4, 1.323 + SCRIPT_NULL = 5, 1.324 + SCRIPT_OBJECT = 6, 1.325 + SCRIPT_VOID = 7, 1.326 + SCRIPT_HOLE = 8 1.327 + }; 1.328 + 1.329 + uint32_t tag; 1.330 + if (mode == XDR_ENCODE) { 1.331 + if (vp.isInt32()) { 1.332 + tag = SCRIPT_INT; 1.333 + } else if (vp.isDouble()) { 1.334 + tag = SCRIPT_DOUBLE; 1.335 + } else if (vp.isString()) { 1.336 + tag = SCRIPT_ATOM; 1.337 + } else if (vp.isTrue()) { 1.338 + tag = SCRIPT_TRUE; 1.339 + } else if (vp.isFalse()) { 1.340 + tag = SCRIPT_FALSE; 1.341 + } else if (vp.isNull()) { 1.342 + tag = SCRIPT_NULL; 1.343 + } else if (vp.isObject()) { 1.344 + tag = SCRIPT_OBJECT; 1.345 + } else if (vp.isMagic(JS_ELEMENTS_HOLE)) { 1.346 + tag = SCRIPT_HOLE; 1.347 + } else { 1.348 + JS_ASSERT(vp.isUndefined()); 1.349 + tag = SCRIPT_VOID; 1.350 + } 1.351 + } 1.352 + 1.353 + if (!xdr->codeUint32(&tag)) 1.354 + return false; 1.355 + 1.356 + switch (tag) { 1.357 + case SCRIPT_INT: { 1.358 + uint32_t i; 1.359 + if (mode == XDR_ENCODE) 1.360 + i = uint32_t(vp.toInt32()); 1.361 + if (!xdr->codeUint32(&i)) 1.362 + return false; 1.363 + if (mode == XDR_DECODE) 1.364 + vp.set(Int32Value(int32_t(i))); 1.365 + break; 1.366 + } 1.367 + case SCRIPT_DOUBLE: { 1.368 + double d; 1.369 + if (mode == XDR_ENCODE) 1.370 + d = vp.toDouble(); 1.371 + if (!xdr->codeDouble(&d)) 1.372 + return false; 1.373 + if (mode == XDR_DECODE) 1.374 + vp.set(DoubleValue(d)); 1.375 + break; 1.376 + } 1.377 + case SCRIPT_ATOM: { 1.378 + RootedAtom atom(cx); 1.379 + if (mode == XDR_ENCODE) 1.380 + atom = &vp.toString()->asAtom(); 1.381 + if (!XDRAtom(xdr, &atom)) 1.382 + return false; 1.383 + if (mode == XDR_DECODE) 1.384 + vp.set(StringValue(atom)); 1.385 + break; 1.386 + } 1.387 + case SCRIPT_TRUE: 1.388 + if (mode == XDR_DECODE) 1.389 + vp.set(BooleanValue(true)); 1.390 + break; 1.391 + case SCRIPT_FALSE: 1.392 + if (mode == XDR_DECODE) 1.393 + vp.set(BooleanValue(false)); 1.394 + break; 1.395 + case SCRIPT_NULL: 1.396 + if (mode == XDR_DECODE) 1.397 + vp.set(NullValue()); 1.398 + break; 1.399 + case SCRIPT_OBJECT: { 1.400 + RootedObject obj(cx); 1.401 + if (mode == XDR_ENCODE) 1.402 + obj = &vp.toObject(); 1.403 + 1.404 + if (!XDRObjectLiteral(xdr, &obj)) 1.405 + return false; 1.406 + 1.407 + if (mode == XDR_DECODE) 1.408 + vp.setObject(*obj); 1.409 + break; 1.410 + } 1.411 + case SCRIPT_VOID: 1.412 + if (mode == XDR_DECODE) 1.413 + vp.set(UndefinedValue()); 1.414 + break; 1.415 + case SCRIPT_HOLE: 1.416 + if (mode == XDR_DECODE) 1.417 + vp.setMagic(JS_ELEMENTS_HOLE); 1.418 + break; 1.419 + } 1.420 + return true; 1.421 +} 1.422 + 1.423 +template bool 1.424 +js::XDRScriptConst(XDRState<XDR_ENCODE> *, MutableHandleValue); 1.425 + 1.426 +template bool 1.427 +js::XDRScriptConst(XDRState<XDR_DECODE> *, MutableHandleValue); 1.428 + 1.429 +// Code LazyScript's free variables. 1.430 +template<XDRMode mode> 1.431 +static bool 1.432 +XDRLazyFreeVariables(XDRState<mode> *xdr, MutableHandle<LazyScript *> lazy) 1.433 +{ 1.434 + JSContext *cx = xdr->cx(); 1.435 + RootedAtom atom(cx); 1.436 + HeapPtrAtom *freeVariables = lazy->freeVariables(); 1.437 + size_t numFreeVariables = lazy->numFreeVariables(); 1.438 + for (size_t i = 0; i < numFreeVariables; i++) { 1.439 + if (mode == XDR_ENCODE) 1.440 + atom = freeVariables[i]; 1.441 + 1.442 + if (!XDRAtom(xdr, &atom)) 1.443 + return false; 1.444 + 1.445 + if (mode == XDR_DECODE) 1.446 + freeVariables[i] = atom; 1.447 + } 1.448 + 1.449 + return true; 1.450 +} 1.451 + 1.452 +// Code the missing part needed to re-create a LazyScript from a JSScript. 1.453 +template<XDRMode mode> 1.454 +static bool 1.455 +XDRRelazificationInfo(XDRState<mode> *xdr, HandleFunction fun, HandleScript script, 1.456 + MutableHandle<LazyScript *> lazy) 1.457 +{ 1.458 + MOZ_ASSERT_IF(mode == XDR_ENCODE, script->isRelazifiable() && script->maybeLazyScript()); 1.459 + MOZ_ASSERT_IF(mode == XDR_ENCODE, !lazy->numInnerFunctions()); 1.460 + 1.461 + JSContext *cx = xdr->cx(); 1.462 + 1.463 + uint64_t packedFields; 1.464 + { 1.465 + uint32_t begin = script->sourceStart(); 1.466 + uint32_t end = script->sourceEnd(); 1.467 + uint32_t lineno = script->lineno(); 1.468 + uint32_t column = script->column(); 1.469 + 1.470 + if (mode == XDR_ENCODE) { 1.471 + packedFields = lazy->packedFields(); 1.472 + MOZ_ASSERT(begin == lazy->begin()); 1.473 + MOZ_ASSERT(end == lazy->end()); 1.474 + MOZ_ASSERT(lineno == lazy->lineno()); 1.475 + MOZ_ASSERT(column == lazy->column()); 1.476 + } 1.477 + 1.478 + if (!xdr->codeUint64(&packedFields)) 1.479 + return false; 1.480 + 1.481 + if (mode == XDR_DECODE) { 1.482 + lazy.set(LazyScript::Create(cx, fun, packedFields, begin, end, lineno, column)); 1.483 + 1.484 + // As opposed to XDRLazyScript, we need to restore the runtime bits 1.485 + // of the script, as we are trying to match the fact this function 1.486 + // has already been parsed and that it would need to be re-lazified. 1.487 + lazy->initRuntimeFields(packedFields); 1.488 + } 1.489 + } 1.490 + 1.491 + // Code free variables. 1.492 + if (!XDRLazyFreeVariables(xdr, lazy)) 1.493 + return false; 1.494 + 1.495 + return true; 1.496 +} 1.497 + 1.498 +static inline uint32_t 1.499 +FindScopeObjectIndex(JSScript *script, NestedScopeObject &scope) 1.500 +{ 1.501 + ObjectArray *objects = script->objects(); 1.502 + HeapPtrObject *vector = objects->vector; 1.503 + unsigned length = objects->length; 1.504 + for (unsigned i = 0; i < length; ++i) { 1.505 + if (vector[i] == &scope) 1.506 + return i; 1.507 + } 1.508 + 1.509 + MOZ_ASSUME_UNREACHABLE("Scope not found"); 1.510 +} 1.511 + 1.512 +static bool 1.513 +SaveSharedScriptData(ExclusiveContext *, Handle<JSScript *>, SharedScriptData *, uint32_t); 1.514 + 1.515 +enum XDRClassKind { 1.516 + CK_BlockObject = 0, 1.517 + CK_WithObject = 1, 1.518 + CK_JSFunction = 2, 1.519 + CK_JSObject = 3 1.520 +}; 1.521 + 1.522 +template<XDRMode mode> 1.523 +bool 1.524 +js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript, 1.525 + HandleFunction fun, MutableHandleScript scriptp) 1.526 +{ 1.527 + /* NB: Keep this in sync with CloneScript. */ 1.528 + 1.529 + enum ScriptBits { 1.530 + NoScriptRval, 1.531 + SavedCallerFun, 1.532 + Strict, 1.533 + ContainsDynamicNameAccess, 1.534 + FunHasExtensibleScope, 1.535 + FunNeedsDeclEnvObject, 1.536 + FunHasAnyAliasedFormal, 1.537 + ArgumentsHasVarBinding, 1.538 + NeedsArgsObj, 1.539 + IsGeneratorExp, 1.540 + IsLegacyGenerator, 1.541 + IsStarGenerator, 1.542 + OwnSource, 1.543 + ExplicitUseStrict, 1.544 + SelfHosted, 1.545 + IsCompileAndGo, 1.546 + HasSingleton, 1.547 + TreatAsRunOnce, 1.548 + HasLazyScript 1.549 + }; 1.550 + 1.551 + uint32_t length, lineno, column, nslots, staticLevel; 1.552 + uint32_t natoms, nsrcnotes, i; 1.553 + uint32_t nconsts, nobjects, nregexps, ntrynotes, nblockscopes; 1.554 + uint32_t prologLength, version; 1.555 + uint32_t funLength = 0; 1.556 + uint32_t nTypeSets = 0; 1.557 + uint32_t scriptBits = 0; 1.558 + 1.559 + JSContext *cx = xdr->cx(); 1.560 + RootedScript script(cx); 1.561 + natoms = nsrcnotes = 0; 1.562 + nconsts = nobjects = nregexps = ntrynotes = nblockscopes = 0; 1.563 + 1.564 + /* XDR arguments and vars. */ 1.565 + uint16_t nargs = 0; 1.566 + uint16_t nblocklocals = 0; 1.567 + uint32_t nvars = 0; 1.568 + if (mode == XDR_ENCODE) { 1.569 + script = scriptp.get(); 1.570 + JS_ASSERT_IF(enclosingScript, enclosingScript->compartment() == script->compartment()); 1.571 + 1.572 + nargs = script->bindings.numArgs(); 1.573 + nblocklocals = script->bindings.numBlockScoped(); 1.574 + nvars = script->bindings.numVars(); 1.575 + } 1.576 + if (!xdr->codeUint16(&nargs)) 1.577 + return false; 1.578 + if (!xdr->codeUint16(&nblocklocals)) 1.579 + return false; 1.580 + if (!xdr->codeUint32(&nvars)) 1.581 + return false; 1.582 + 1.583 + if (mode == XDR_ENCODE) 1.584 + length = script->length(); 1.585 + if (!xdr->codeUint32(&length)) 1.586 + return false; 1.587 + 1.588 + if (mode == XDR_ENCODE) { 1.589 + prologLength = script->mainOffset(); 1.590 + JS_ASSERT(script->getVersion() != JSVERSION_UNKNOWN); 1.591 + version = script->getVersion(); 1.592 + lineno = script->lineno(); 1.593 + column = script->column(); 1.594 + nslots = script->nslots(); 1.595 + staticLevel = script->staticLevel(); 1.596 + natoms = script->natoms(); 1.597 + 1.598 + nsrcnotes = script->numNotes(); 1.599 + 1.600 + if (script->hasConsts()) 1.601 + nconsts = script->consts()->length; 1.602 + if (script->hasObjects()) 1.603 + nobjects = script->objects()->length; 1.604 + if (script->hasRegexps()) 1.605 + nregexps = script->regexps()->length; 1.606 + if (script->hasTrynotes()) 1.607 + ntrynotes = script->trynotes()->length; 1.608 + if (script->hasBlockScopes()) 1.609 + nblockscopes = script->blockScopes()->length; 1.610 + 1.611 + nTypeSets = script->nTypeSets(); 1.612 + funLength = script->funLength(); 1.613 + 1.614 + if (script->noScriptRval()) 1.615 + scriptBits |= (1 << NoScriptRval); 1.616 + if (script->savedCallerFun()) 1.617 + scriptBits |= (1 << SavedCallerFun); 1.618 + if (script->strict()) 1.619 + scriptBits |= (1 << Strict); 1.620 + if (script->explicitUseStrict()) 1.621 + scriptBits |= (1 << ExplicitUseStrict); 1.622 + if (script->selfHosted()) 1.623 + scriptBits |= (1 << SelfHosted); 1.624 + if (script->bindingsAccessedDynamically()) 1.625 + scriptBits |= (1 << ContainsDynamicNameAccess); 1.626 + if (script->funHasExtensibleScope()) 1.627 + scriptBits |= (1 << FunHasExtensibleScope); 1.628 + if (script->funNeedsDeclEnvObject()) 1.629 + scriptBits |= (1 << FunNeedsDeclEnvObject); 1.630 + if (script->funHasAnyAliasedFormal()) 1.631 + scriptBits |= (1 << FunHasAnyAliasedFormal); 1.632 + if (script->argumentsHasVarBinding()) 1.633 + scriptBits |= (1 << ArgumentsHasVarBinding); 1.634 + if (script->analyzedArgsUsage() && script->needsArgsObj()) 1.635 + scriptBits |= (1 << NeedsArgsObj); 1.636 + if (!enclosingScript || enclosingScript->scriptSource() != script->scriptSource()) 1.637 + scriptBits |= (1 << OwnSource); 1.638 + if (script->isGeneratorExp()) 1.639 + scriptBits |= (1 << IsGeneratorExp); 1.640 + if (script->isLegacyGenerator()) 1.641 + scriptBits |= (1 << IsLegacyGenerator); 1.642 + if (script->isStarGenerator()) 1.643 + scriptBits |= (1 << IsStarGenerator); 1.644 + if (script->compileAndGo()) 1.645 + scriptBits |= (1 << IsCompileAndGo); 1.646 + if (script->hasSingletons()) 1.647 + scriptBits |= (1 << HasSingleton); 1.648 + if (script->treatAsRunOnce()) 1.649 + scriptBits |= (1 << TreatAsRunOnce); 1.650 + if (script->isRelazifiable()) 1.651 + scriptBits |= (1 << HasLazyScript); 1.652 + } 1.653 + 1.654 + if (!xdr->codeUint32(&prologLength)) 1.655 + return false; 1.656 + if (!xdr->codeUint32(&version)) 1.657 + return false; 1.658 + 1.659 + // To fuse allocations, we need lengths of all embedded arrays early. 1.660 + if (!xdr->codeUint32(&natoms)) 1.661 + return false; 1.662 + if (!xdr->codeUint32(&nsrcnotes)) 1.663 + return false; 1.664 + if (!xdr->codeUint32(&nconsts)) 1.665 + return false; 1.666 + if (!xdr->codeUint32(&nobjects)) 1.667 + return false; 1.668 + if (!xdr->codeUint32(&nregexps)) 1.669 + return false; 1.670 + if (!xdr->codeUint32(&ntrynotes)) 1.671 + return false; 1.672 + if (!xdr->codeUint32(&nblockscopes)) 1.673 + return false; 1.674 + if (!xdr->codeUint32(&nTypeSets)) 1.675 + return false; 1.676 + if (!xdr->codeUint32(&funLength)) 1.677 + return false; 1.678 + if (!xdr->codeUint32(&scriptBits)) 1.679 + return false; 1.680 + 1.681 + if (mode == XDR_DECODE) { 1.682 + JSVersion version_ = JSVersion(version); 1.683 + JS_ASSERT((version_ & VersionFlags::MASK) == unsigned(version_)); 1.684 + 1.685 + // staticLevel is set below. 1.686 + CompileOptions options(cx); 1.687 + options.setVersion(version_) 1.688 + .setNoScriptRval(!!(scriptBits & (1 << NoScriptRval))) 1.689 + .setSelfHostingMode(!!(scriptBits & (1 << SelfHosted))); 1.690 + RootedScriptSource sourceObject(cx); 1.691 + if (scriptBits & (1 << OwnSource)) { 1.692 + ScriptSource *ss = cx->new_<ScriptSource>(); 1.693 + if (!ss) 1.694 + return false; 1.695 + ScriptSourceHolder ssHolder(ss); 1.696 + 1.697 + /* 1.698 + * We use this CompileOptions only to initialize the 1.699 + * ScriptSourceObject. Most CompileOptions fields aren't used by 1.700 + * ScriptSourceObject, and those that are (element; elementAttributeName) 1.701 + * aren't preserved by XDR. So this can be simple. 1.702 + */ 1.703 + CompileOptions options(cx); 1.704 + options.setOriginPrincipals(xdr->originPrincipals()); 1.705 + ss->initFromOptions(cx, options); 1.706 + sourceObject = ScriptSourceObject::create(cx, ss, options); 1.707 + if (!sourceObject) 1.708 + return false; 1.709 + } else { 1.710 + JS_ASSERT(enclosingScript); 1.711 + // When decoding, all the scripts and the script source object 1.712 + // are in the same compartment, so the script's source object 1.713 + // should never be a cross-compartment wrapper. 1.714 + JS_ASSERT(enclosingScript->sourceObject()->is<ScriptSourceObject>()); 1.715 + sourceObject = &enclosingScript->sourceObject()->as<ScriptSourceObject>(); 1.716 + } 1.717 + script = JSScript::Create(cx, enclosingScope, !!(scriptBits & (1 << SavedCallerFun)), 1.718 + options, /* staticLevel = */ 0, sourceObject, 0, 0); 1.719 + if (!script) 1.720 + return false; 1.721 + } 1.722 + 1.723 + /* JSScript::partiallyInit assumes script->bindings is fully initialized. */ 1.724 + LifoAllocScope las(&cx->tempLifoAlloc()); 1.725 + if (!XDRScriptBindings(xdr, las, nargs, nvars, script, nblocklocals)) 1.726 + return false; 1.727 + 1.728 + if (mode == XDR_DECODE) { 1.729 + if (!JSScript::partiallyInit(cx, script, nconsts, nobjects, nregexps, ntrynotes, 1.730 + nblockscopes, nTypeSets)) 1.731 + { 1.732 + return false; 1.733 + } 1.734 + 1.735 + JS_ASSERT(!script->mainOffset()); 1.736 + script->mainOffset_ = prologLength; 1.737 + script->setLength(length); 1.738 + script->funLength_ = funLength; 1.739 + 1.740 + scriptp.set(script); 1.741 + 1.742 + if (scriptBits & (1 << Strict)) 1.743 + script->strict_ = true; 1.744 + if (scriptBits & (1 << ExplicitUseStrict)) 1.745 + script->explicitUseStrict_ = true; 1.746 + if (scriptBits & (1 << ContainsDynamicNameAccess)) 1.747 + script->bindingsAccessedDynamically_ = true; 1.748 + if (scriptBits & (1 << FunHasExtensibleScope)) 1.749 + script->funHasExtensibleScope_ = true; 1.750 + if (scriptBits & (1 << FunNeedsDeclEnvObject)) 1.751 + script->funNeedsDeclEnvObject_ = true; 1.752 + if (scriptBits & (1 << FunHasAnyAliasedFormal)) 1.753 + script->funHasAnyAliasedFormal_ = true; 1.754 + if (scriptBits & (1 << ArgumentsHasVarBinding)) 1.755 + script->setArgumentsHasVarBinding(); 1.756 + if (scriptBits & (1 << NeedsArgsObj)) 1.757 + script->setNeedsArgsObj(true); 1.758 + if (scriptBits & (1 << IsGeneratorExp)) 1.759 + script->isGeneratorExp_ = true; 1.760 + if (scriptBits & (1 << IsCompileAndGo)) 1.761 + script->compileAndGo_ = true; 1.762 + if (scriptBits & (1 << HasSingleton)) 1.763 + script->hasSingletons_ = true; 1.764 + if (scriptBits & (1 << TreatAsRunOnce)) 1.765 + script->treatAsRunOnce_ = true; 1.766 + 1.767 + if (scriptBits & (1 << IsLegacyGenerator)) { 1.768 + JS_ASSERT(!(scriptBits & (1 << IsStarGenerator))); 1.769 + script->setGeneratorKind(LegacyGenerator); 1.770 + } else if (scriptBits & (1 << IsStarGenerator)) 1.771 + script->setGeneratorKind(StarGenerator); 1.772 + } 1.773 + 1.774 + JS_STATIC_ASSERT(sizeof(jsbytecode) == 1); 1.775 + JS_STATIC_ASSERT(sizeof(jssrcnote) == 1); 1.776 + 1.777 + if (scriptBits & (1 << OwnSource)) { 1.778 + if (!script->scriptSource()->performXDR<mode>(xdr)) 1.779 + return false; 1.780 + } 1.781 + if (!xdr->codeUint32(&script->sourceStart_)) 1.782 + return false; 1.783 + if (!xdr->codeUint32(&script->sourceEnd_)) 1.784 + return false; 1.785 + 1.786 + if (!xdr->codeUint32(&lineno) || 1.787 + !xdr->codeUint32(&column) || 1.788 + !xdr->codeUint32(&nslots) || 1.789 + !xdr->codeUint32(&staticLevel)) 1.790 + { 1.791 + return false; 1.792 + } 1.793 + 1.794 + if (mode == XDR_DECODE) { 1.795 + script->lineno_ = lineno; 1.796 + script->column_ = column; 1.797 + script->nslots_ = nslots; 1.798 + script->staticLevel_ = staticLevel; 1.799 + } 1.800 + 1.801 + jsbytecode *code = script->code(); 1.802 + SharedScriptData *ssd; 1.803 + if (mode == XDR_DECODE) { 1.804 + ssd = SharedScriptData::new_(cx, length, nsrcnotes, natoms); 1.805 + if (!ssd) 1.806 + return false; 1.807 + code = ssd->data; 1.808 + if (natoms != 0) { 1.809 + script->natoms_ = natoms; 1.810 + script->atoms = ssd->atoms(); 1.811 + } 1.812 + } 1.813 + 1.814 + if (!xdr->codeBytes(code, length) || !xdr->codeBytes(code + length, nsrcnotes)) { 1.815 + if (mode == XDR_DECODE) 1.816 + js_free(ssd); 1.817 + return false; 1.818 + } 1.819 + 1.820 + for (i = 0; i != natoms; ++i) { 1.821 + if (mode == XDR_DECODE) { 1.822 + RootedAtom tmp(cx); 1.823 + if (!XDRAtom(xdr, &tmp)) 1.824 + return false; 1.825 + script->atoms[i].init(tmp); 1.826 + } else { 1.827 + RootedAtom tmp(cx, script->atoms[i]); 1.828 + if (!XDRAtom(xdr, &tmp)) 1.829 + return false; 1.830 + } 1.831 + } 1.832 + 1.833 + if (mode == XDR_DECODE) { 1.834 + if (!SaveSharedScriptData(cx, script, ssd, nsrcnotes)) 1.835 + return false; 1.836 + } 1.837 + 1.838 + if (nconsts) { 1.839 + HeapValue *vector = script->consts()->vector; 1.840 + RootedValue val(cx); 1.841 + for (i = 0; i != nconsts; ++i) { 1.842 + if (mode == XDR_ENCODE) 1.843 + val = vector[i]; 1.844 + if (!XDRScriptConst(xdr, &val)) 1.845 + return false; 1.846 + if (mode == XDR_DECODE) 1.847 + vector[i].init(val); 1.848 + } 1.849 + } 1.850 + 1.851 + /* 1.852 + * Here looping from 0-to-length to xdr objects is essential to ensure that 1.853 + * all references to enclosing blocks (via FindScopeObjectIndex below) happen 1.854 + * after the enclosing block has been XDR'd. 1.855 + */ 1.856 + for (i = 0; i != nobjects; ++i) { 1.857 + HeapPtr<JSObject> *objp = &script->objects()->vector[i]; 1.858 + XDRClassKind classk; 1.859 + 1.860 + if (mode == XDR_ENCODE) { 1.861 + JSObject *obj = *objp; 1.862 + if (obj->is<BlockObject>()) 1.863 + classk = CK_BlockObject; 1.864 + else if (obj->is<StaticWithObject>()) 1.865 + classk = CK_WithObject; 1.866 + else if (obj->is<JSFunction>()) 1.867 + classk = CK_JSFunction; 1.868 + else if (obj->is<JSObject>() || obj->is<ArrayObject>()) 1.869 + classk = CK_JSObject; 1.870 + else 1.871 + MOZ_ASSUME_UNREACHABLE("Cannot encode this class of object."); 1.872 + } 1.873 + 1.874 + if (!xdr->codeEnum32(&classk)) 1.875 + return false; 1.876 + 1.877 + switch (classk) { 1.878 + case CK_BlockObject: 1.879 + case CK_WithObject: { 1.880 + /* Code the nested block's enclosing scope. */ 1.881 + uint32_t enclosingStaticScopeIndex = 0; 1.882 + if (mode == XDR_ENCODE) { 1.883 + NestedScopeObject &scope = (*objp)->as<NestedScopeObject>(); 1.884 + if (NestedScopeObject *enclosing = scope.enclosingNestedScope()) 1.885 + enclosingStaticScopeIndex = FindScopeObjectIndex(script, *enclosing); 1.886 + else 1.887 + enclosingStaticScopeIndex = UINT32_MAX; 1.888 + } 1.889 + if (!xdr->codeUint32(&enclosingStaticScopeIndex)) 1.890 + return false; 1.891 + Rooted<JSObject*> enclosingStaticScope(cx); 1.892 + if (mode == XDR_DECODE) { 1.893 + if (enclosingStaticScopeIndex != UINT32_MAX) { 1.894 + JS_ASSERT(enclosingStaticScopeIndex < i); 1.895 + enclosingStaticScope = script->objects()->vector[enclosingStaticScopeIndex]; 1.896 + } else { 1.897 + enclosingStaticScope = fun; 1.898 + } 1.899 + } 1.900 + 1.901 + if (classk == CK_BlockObject) { 1.902 + Rooted<StaticBlockObject*> tmp(cx, static_cast<StaticBlockObject *>(objp->get())); 1.903 + if (!XDRStaticBlockObject(xdr, enclosingStaticScope, tmp.address())) 1.904 + return false; 1.905 + *objp = tmp; 1.906 + } else { 1.907 + Rooted<StaticWithObject*> tmp(cx, static_cast<StaticWithObject *>(objp->get())); 1.908 + if (!XDRStaticWithObject(xdr, enclosingStaticScope, tmp.address())) 1.909 + return false; 1.910 + *objp = tmp; 1.911 + } 1.912 + break; 1.913 + } 1.914 + 1.915 + case CK_JSFunction: { 1.916 + /* Code the nested function's enclosing scope. */ 1.917 + uint32_t funEnclosingScopeIndex = 0; 1.918 + RootedObject funEnclosingScope(cx); 1.919 + if (mode == XDR_ENCODE) { 1.920 + RootedFunction function(cx, &(*objp)->as<JSFunction>()); 1.921 + 1.922 + if (function->isInterpretedLazy()) 1.923 + funEnclosingScope = function->lazyScript()->enclosingScope(); 1.924 + else 1.925 + funEnclosingScope = function->nonLazyScript()->enclosingStaticScope(); 1.926 + 1.927 + StaticScopeIter<NoGC> ssi(funEnclosingScope); 1.928 + if (ssi.done() || ssi.type() == StaticScopeIter<NoGC>::FUNCTION) { 1.929 + JS_ASSERT(ssi.done() == !fun); 1.930 + funEnclosingScopeIndex = UINT32_MAX; 1.931 + } else if (ssi.type() == StaticScopeIter<NoGC>::BLOCK) { 1.932 + funEnclosingScopeIndex = FindScopeObjectIndex(script, ssi.block()); 1.933 + JS_ASSERT(funEnclosingScopeIndex < i); 1.934 + } else { 1.935 + funEnclosingScopeIndex = FindScopeObjectIndex(script, ssi.staticWith()); 1.936 + JS_ASSERT(funEnclosingScopeIndex < i); 1.937 + } 1.938 + } 1.939 + 1.940 + if (!xdr->codeUint32(&funEnclosingScopeIndex)) 1.941 + return false; 1.942 + 1.943 + if (mode == XDR_DECODE) { 1.944 + if (funEnclosingScopeIndex == UINT32_MAX) { 1.945 + funEnclosingScope = fun; 1.946 + } else { 1.947 + JS_ASSERT(funEnclosingScopeIndex < i); 1.948 + funEnclosingScope = script->objects()->vector[funEnclosingScopeIndex]; 1.949 + } 1.950 + } 1.951 + 1.952 + // Code nested function and script. 1.953 + RootedObject tmp(cx, *objp); 1.954 + if (!XDRInterpretedFunction(xdr, funEnclosingScope, script, &tmp)) 1.955 + return false; 1.956 + *objp = tmp; 1.957 + break; 1.958 + } 1.959 + 1.960 + case CK_JSObject: { 1.961 + /* Code object literal. */ 1.962 + RootedObject tmp(cx, *objp); 1.963 + if (!XDRObjectLiteral(xdr, &tmp)) 1.964 + return false; 1.965 + *objp = tmp; 1.966 + break; 1.967 + } 1.968 + 1.969 + default: { 1.970 + MOZ_ASSERT(false, "Unknown class kind."); 1.971 + return false; 1.972 + } 1.973 + } 1.974 + } 1.975 + 1.976 + for (i = 0; i != nregexps; ++i) { 1.977 + if (!XDRScriptRegExpObject(xdr, &script->regexps()->vector[i])) 1.978 + return false; 1.979 + } 1.980 + 1.981 + if (ntrynotes != 0) { 1.982 + JSTryNote *tnfirst = script->trynotes()->vector; 1.983 + JS_ASSERT(script->trynotes()->length == ntrynotes); 1.984 + JSTryNote *tn = tnfirst + ntrynotes; 1.985 + do { 1.986 + --tn; 1.987 + if (!xdr->codeUint8(&tn->kind) || 1.988 + !xdr->codeUint32(&tn->stackDepth) || 1.989 + !xdr->codeUint32(&tn->start) || 1.990 + !xdr->codeUint32(&tn->length)) { 1.991 + return false; 1.992 + } 1.993 + } while (tn != tnfirst); 1.994 + } 1.995 + 1.996 + for (i = 0; i < nblockscopes; ++i) { 1.997 + BlockScopeNote *note = &script->blockScopes()->vector[i]; 1.998 + if (!xdr->codeUint32(¬e->index) || 1.999 + !xdr->codeUint32(¬e->start) || 1.1000 + !xdr->codeUint32(¬e->length) || 1.1001 + !xdr->codeUint32(¬e->parent)) 1.1002 + { 1.1003 + return false; 1.1004 + } 1.1005 + } 1.1006 + 1.1007 + if (scriptBits & (1 << HasLazyScript)) { 1.1008 + Rooted<LazyScript *> lazy(cx); 1.1009 + if (mode == XDR_ENCODE) 1.1010 + lazy = script->maybeLazyScript(); 1.1011 + 1.1012 + if (!XDRRelazificationInfo(xdr, fun, script, &lazy)) 1.1013 + return false; 1.1014 + 1.1015 + if (mode == XDR_DECODE) 1.1016 + script->setLazyScript(lazy); 1.1017 + } 1.1018 + 1.1019 + if (mode == XDR_DECODE) { 1.1020 + scriptp.set(script); 1.1021 + 1.1022 + /* see BytecodeEmitter::tellDebuggerAboutCompiledScript */ 1.1023 + CallNewScriptHook(cx, script, fun); 1.1024 + if (!fun) { 1.1025 + RootedGlobalObject global(cx, script->compileAndGo() ? &script->global() : nullptr); 1.1026 + Debugger::onNewScript(cx, script, global); 1.1027 + } 1.1028 + } 1.1029 + 1.1030 + return true; 1.1031 +} 1.1032 + 1.1033 +template bool 1.1034 +js::XDRScript(XDRState<XDR_ENCODE> *, HandleObject, HandleScript, HandleFunction, 1.1035 + MutableHandleScript); 1.1036 + 1.1037 +template bool 1.1038 +js::XDRScript(XDRState<XDR_DECODE> *, HandleObject, HandleScript, HandleFunction, 1.1039 + MutableHandleScript); 1.1040 + 1.1041 +template<XDRMode mode> 1.1042 +bool 1.1043 +js::XDRLazyScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript, 1.1044 + HandleFunction fun, MutableHandle<LazyScript *> lazy) 1.1045 +{ 1.1046 + JSContext *cx = xdr->cx(); 1.1047 + 1.1048 + { 1.1049 + uint32_t begin; 1.1050 + uint32_t end; 1.1051 + uint32_t lineno; 1.1052 + uint32_t column; 1.1053 + uint64_t packedFields; 1.1054 + 1.1055 + if (mode == XDR_ENCODE) { 1.1056 + MOZ_ASSERT(!lazy->maybeScript()); 1.1057 + MOZ_ASSERT(fun == lazy->functionNonDelazifying()); 1.1058 + 1.1059 + begin = lazy->begin(); 1.1060 + end = lazy->end(); 1.1061 + lineno = lazy->lineno(); 1.1062 + column = lazy->column(); 1.1063 + packedFields = lazy->packedFields(); 1.1064 + } 1.1065 + 1.1066 + if (!xdr->codeUint32(&begin) || !xdr->codeUint32(&end) || 1.1067 + !xdr->codeUint32(&lineno) || !xdr->codeUint32(&column) || 1.1068 + !xdr->codeUint64(&packedFields)) 1.1069 + { 1.1070 + return false; 1.1071 + } 1.1072 + 1.1073 + if (mode == XDR_DECODE) 1.1074 + lazy.set(LazyScript::Create(cx, fun, packedFields, begin, end, lineno, column)); 1.1075 + } 1.1076 + 1.1077 + // Code free variables. 1.1078 + if (!XDRLazyFreeVariables(xdr, lazy)) 1.1079 + return false; 1.1080 + 1.1081 + // Code inner functions. 1.1082 + { 1.1083 + RootedObject func(cx); 1.1084 + HeapPtrFunction *innerFunctions = lazy->innerFunctions(); 1.1085 + size_t numInnerFunctions = lazy->numInnerFunctions(); 1.1086 + for (size_t i = 0; i < numInnerFunctions; i++) { 1.1087 + if (mode == XDR_ENCODE) 1.1088 + func = innerFunctions[i]; 1.1089 + 1.1090 + if (!XDRInterpretedFunction(xdr, fun, enclosingScript, &func)) 1.1091 + return false; 1.1092 + 1.1093 + if (mode == XDR_DECODE) 1.1094 + innerFunctions[i] = &func->as<JSFunction>(); 1.1095 + } 1.1096 + } 1.1097 + 1.1098 + if (mode == XDR_DECODE) { 1.1099 + JS_ASSERT(!lazy->sourceObject()); 1.1100 + ScriptSourceObject *sourceObject = &enclosingScript->scriptSourceUnwrap(); 1.1101 + 1.1102 + // Set the enclosing scope of the lazy function, this would later be 1.1103 + // used to define the environment when the function would be used. 1.1104 + lazy->setParent(enclosingScope, sourceObject); 1.1105 + } 1.1106 + 1.1107 + return true; 1.1108 +} 1.1109 + 1.1110 +template bool 1.1111 +js::XDRLazyScript(XDRState<XDR_ENCODE> *, HandleObject, HandleScript, 1.1112 + HandleFunction, MutableHandle<LazyScript *>); 1.1113 + 1.1114 +template bool 1.1115 +js::XDRLazyScript(XDRState<XDR_DECODE> *, HandleObject, HandleScript, 1.1116 + HandleFunction, MutableHandle<LazyScript *>); 1.1117 + 1.1118 +void 1.1119 +JSScript::setSourceObject(JSObject *object) 1.1120 +{ 1.1121 + JS_ASSERT(compartment() == object->compartment()); 1.1122 + sourceObject_ = object; 1.1123 +} 1.1124 + 1.1125 +js::ScriptSourceObject & 1.1126 +JSScript::scriptSourceUnwrap() const { 1.1127 + return UncheckedUnwrap(sourceObject())->as<ScriptSourceObject>(); 1.1128 +} 1.1129 + 1.1130 +js::ScriptSource * 1.1131 +JSScript::scriptSource() const { 1.1132 + return scriptSourceUnwrap().source(); 1.1133 +} 1.1134 + 1.1135 +bool 1.1136 +JSScript::initScriptCounts(JSContext *cx) 1.1137 +{ 1.1138 + JS_ASSERT(!hasScriptCounts()); 1.1139 + 1.1140 + size_t n = 0; 1.1141 + 1.1142 + for (jsbytecode *pc = code(); pc < codeEnd(); pc += GetBytecodeLength(pc)) 1.1143 + n += PCCounts::numCounts(JSOp(*pc)); 1.1144 + 1.1145 + size_t bytes = (length() * sizeof(PCCounts)) + (n * sizeof(double)); 1.1146 + char *base = (char *) cx->calloc_(bytes); 1.1147 + if (!base) 1.1148 + return false; 1.1149 + 1.1150 + /* Create compartment's scriptCountsMap if necessary. */ 1.1151 + ScriptCountsMap *map = compartment()->scriptCountsMap; 1.1152 + if (!map) { 1.1153 + map = cx->new_<ScriptCountsMap>(); 1.1154 + if (!map || !map->init()) { 1.1155 + js_free(base); 1.1156 + js_delete(map); 1.1157 + return false; 1.1158 + } 1.1159 + compartment()->scriptCountsMap = map; 1.1160 + } 1.1161 + 1.1162 + char *cursor = base; 1.1163 + 1.1164 + ScriptCounts scriptCounts; 1.1165 + scriptCounts.pcCountsVector = (PCCounts *) cursor; 1.1166 + cursor += length() * sizeof(PCCounts); 1.1167 + 1.1168 + for (jsbytecode *pc = code(); pc < codeEnd(); pc += GetBytecodeLength(pc)) { 1.1169 + JS_ASSERT(uintptr_t(cursor) % sizeof(double) == 0); 1.1170 + scriptCounts.pcCountsVector[pcToOffset(pc)].counts = (double *) cursor; 1.1171 + size_t capacity = PCCounts::numCounts(JSOp(*pc)); 1.1172 +#ifdef DEBUG 1.1173 + scriptCounts.pcCountsVector[pcToOffset(pc)].capacity = capacity; 1.1174 +#endif 1.1175 + cursor += capacity * sizeof(double); 1.1176 + } 1.1177 + 1.1178 + if (!map->putNew(this, scriptCounts)) { 1.1179 + js_free(base); 1.1180 + return false; 1.1181 + } 1.1182 + hasScriptCounts_ = true; // safe to set this; we can't fail after this point 1.1183 + 1.1184 + JS_ASSERT(size_t(cursor - base) == bytes); 1.1185 + 1.1186 + /* Enable interrupts in any interpreter frames running on this script. */ 1.1187 + for (ActivationIterator iter(cx->runtime()); !iter.done(); ++iter) { 1.1188 + if (iter->isInterpreter()) 1.1189 + iter->asInterpreter()->enableInterruptsIfRunning(this); 1.1190 + } 1.1191 + 1.1192 + return true; 1.1193 +} 1.1194 + 1.1195 +static inline ScriptCountsMap::Ptr GetScriptCountsMapEntry(JSScript *script) 1.1196 +{ 1.1197 + JS_ASSERT(script->hasScriptCounts()); 1.1198 + ScriptCountsMap *map = script->compartment()->scriptCountsMap; 1.1199 + ScriptCountsMap::Ptr p = map->lookup(script); 1.1200 + JS_ASSERT(p); 1.1201 + return p; 1.1202 +} 1.1203 + 1.1204 +js::PCCounts 1.1205 +JSScript::getPCCounts(jsbytecode *pc) { 1.1206 + JS_ASSERT(containsPC(pc)); 1.1207 + ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this); 1.1208 + return p->value().pcCountsVector[pcToOffset(pc)]; 1.1209 +} 1.1210 + 1.1211 +void 1.1212 +JSScript::addIonCounts(jit::IonScriptCounts *ionCounts) 1.1213 +{ 1.1214 + ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this); 1.1215 + if (p->value().ionCounts) 1.1216 + ionCounts->setPrevious(p->value().ionCounts); 1.1217 + p->value().ionCounts = ionCounts; 1.1218 +} 1.1219 + 1.1220 +jit::IonScriptCounts * 1.1221 +JSScript::getIonCounts() 1.1222 +{ 1.1223 + ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this); 1.1224 + return p->value().ionCounts; 1.1225 +} 1.1226 + 1.1227 +ScriptCounts 1.1228 +JSScript::releaseScriptCounts() 1.1229 +{ 1.1230 + ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this); 1.1231 + ScriptCounts counts = p->value(); 1.1232 + compartment()->scriptCountsMap->remove(p); 1.1233 + hasScriptCounts_ = false; 1.1234 + return counts; 1.1235 +} 1.1236 + 1.1237 +void 1.1238 +JSScript::destroyScriptCounts(FreeOp *fop) 1.1239 +{ 1.1240 + if (hasScriptCounts()) { 1.1241 + ScriptCounts scriptCounts = releaseScriptCounts(); 1.1242 + scriptCounts.destroy(fop); 1.1243 + } 1.1244 +} 1.1245 + 1.1246 +void 1.1247 +ScriptSourceObject::setSource(ScriptSource *source) 1.1248 +{ 1.1249 + if (source) 1.1250 + source->incref(); 1.1251 + if (this->source()) 1.1252 + this->source()->decref(); 1.1253 + setReservedSlot(SOURCE_SLOT, PrivateValue(source)); 1.1254 +} 1.1255 + 1.1256 +JSObject * 1.1257 +ScriptSourceObject::element() const 1.1258 +{ 1.1259 + return getReservedSlot(ELEMENT_SLOT).toObjectOrNull(); 1.1260 +} 1.1261 + 1.1262 +void 1.1263 +ScriptSourceObject::initElement(HandleObject element) 1.1264 +{ 1.1265 + JS_ASSERT(getReservedSlot(ELEMENT_SLOT).isNull()); 1.1266 + setReservedSlot(ELEMENT_SLOT, ObjectOrNullValue(element)); 1.1267 +} 1.1268 + 1.1269 +const Value & 1.1270 +ScriptSourceObject::elementAttributeName() const 1.1271 +{ 1.1272 + const Value &prop = getReservedSlot(ELEMENT_PROPERTY_SLOT); 1.1273 + JS_ASSERT(prop.isUndefined() || prop.isString()); 1.1274 + return prop; 1.1275 +} 1.1276 + 1.1277 +void 1.1278 +ScriptSourceObject::initIntroductionScript(JSScript *script) 1.1279 +{ 1.1280 + JS_ASSERT(!getReservedSlot(INTRODUCTION_SCRIPT_SLOT).toPrivate()); 1.1281 + 1.1282 + // There is no equivalent of cross-compartment wrappers for scripts. If 1.1283 + // the introduction script would be in a different compartment from the 1.1284 + // compiled code, we would be creating a cross-compartment script 1.1285 + // reference, which would be bogus. In that case, just don't bother to 1.1286 + // retain the introduction script. 1.1287 + if (script && script->compartment() == compartment()) 1.1288 + setReservedSlot(INTRODUCTION_SCRIPT_SLOT, PrivateValue(script)); 1.1289 +} 1.1290 + 1.1291 +void 1.1292 +ScriptSourceObject::trace(JSTracer *trc, JSObject *obj) 1.1293 +{ 1.1294 + ScriptSourceObject *sso = static_cast<ScriptSourceObject *>(obj); 1.1295 + 1.1296 + if (JSScript *script = sso->introductionScript()) { 1.1297 + MarkScriptUnbarriered(trc, &script, "ScriptSourceObject introductionScript"); 1.1298 + sso->setReservedSlot(INTRODUCTION_SCRIPT_SLOT, PrivateValue(script)); 1.1299 + } 1.1300 +} 1.1301 + 1.1302 +void 1.1303 +ScriptSourceObject::finalize(FreeOp *fop, JSObject *obj) 1.1304 +{ 1.1305 + // ScriptSource::setSource automatically takes care of the refcount 1.1306 + obj->as<ScriptSourceObject>().setSource(nullptr); 1.1307 +} 1.1308 + 1.1309 +const Class ScriptSourceObject::class_ = { 1.1310 + "ScriptSource", 1.1311 + JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | 1.1312 + JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_IS_ANONYMOUS, 1.1313 + JS_PropertyStub, /* addProperty */ 1.1314 + JS_DeletePropertyStub, /* delProperty */ 1.1315 + JS_PropertyStub, /* getProperty */ 1.1316 + JS_StrictPropertyStub, /* setProperty */ 1.1317 + JS_EnumerateStub, 1.1318 + JS_ResolveStub, 1.1319 + JS_ConvertStub, 1.1320 + finalize, 1.1321 + nullptr, /* call */ 1.1322 + nullptr, /* hasInstance */ 1.1323 + nullptr, /* construct */ 1.1324 + trace 1.1325 +}; 1.1326 + 1.1327 +ScriptSourceObject * 1.1328 +ScriptSourceObject::create(ExclusiveContext *cx, ScriptSource *source, 1.1329 + const ReadOnlyCompileOptions &options) 1.1330 +{ 1.1331 + RootedObject object(cx, NewObjectWithGivenProto(cx, &class_, nullptr, cx->global())); 1.1332 + if (!object) 1.1333 + return nullptr; 1.1334 + RootedScriptSource sourceObject(cx, &object->as<ScriptSourceObject>()); 1.1335 + 1.1336 + source->incref(); 1.1337 + sourceObject->initSlot(SOURCE_SLOT, PrivateValue(source)); 1.1338 + sourceObject->initSlot(ELEMENT_SLOT, ObjectOrNullValue(options.element())); 1.1339 + if (options.elementAttributeName()) 1.1340 + sourceObject->initSlot(ELEMENT_PROPERTY_SLOT, StringValue(options.elementAttributeName())); 1.1341 + else 1.1342 + sourceObject->initSlot(ELEMENT_PROPERTY_SLOT, UndefinedValue()); 1.1343 + 1.1344 + sourceObject->initSlot(INTRODUCTION_SCRIPT_SLOT, PrivateValue(nullptr)); 1.1345 + sourceObject->initIntroductionScript(options.introductionScript()); 1.1346 + 1.1347 + return sourceObject; 1.1348 +} 1.1349 + 1.1350 +static const unsigned char emptySource[] = ""; 1.1351 + 1.1352 +/* Adjust the amount of memory this script source uses for source data, 1.1353 + reallocating if needed. */ 1.1354 +bool 1.1355 +ScriptSource::adjustDataSize(size_t nbytes) 1.1356 +{ 1.1357 + // Allocating 0 bytes has undefined behavior, so special-case it. 1.1358 + if (nbytes == 0) { 1.1359 + if (data.compressed != emptySource) 1.1360 + js_free(data.compressed); 1.1361 + data.compressed = const_cast<unsigned char *>(emptySource); 1.1362 + return true; 1.1363 + } 1.1364 + 1.1365 + // |data.compressed| can be nullptr. 1.1366 + void *buf = js_realloc(data.compressed, nbytes); 1.1367 + if (!buf && data.compressed != emptySource) 1.1368 + js_free(data.compressed); 1.1369 + data.compressed = static_cast<unsigned char *>(buf); 1.1370 + return !!data.compressed; 1.1371 +} 1.1372 + 1.1373 +/* static */ bool 1.1374 +JSScript::loadSource(JSContext *cx, ScriptSource *ss, bool *worked) 1.1375 +{ 1.1376 + JS_ASSERT(!ss->hasSourceData()); 1.1377 + *worked = false; 1.1378 + if (!cx->runtime()->sourceHook || !ss->sourceRetrievable()) 1.1379 + return true; 1.1380 + jschar *src = nullptr; 1.1381 + size_t length; 1.1382 + if (!cx->runtime()->sourceHook->load(cx, ss->filename(), &src, &length)) 1.1383 + return false; 1.1384 + if (!src) 1.1385 + return true; 1.1386 + ss->setSource(src, length); 1.1387 + *worked = true; 1.1388 + return true; 1.1389 +} 1.1390 + 1.1391 +JSFlatString * 1.1392 +JSScript::sourceData(JSContext *cx) 1.1393 +{ 1.1394 + JS_ASSERT(scriptSource()->hasSourceData()); 1.1395 + return scriptSource()->substring(cx, sourceStart(), sourceEnd()); 1.1396 +} 1.1397 + 1.1398 +SourceDataCache::AutoHoldEntry::AutoHoldEntry() 1.1399 + : cache_(nullptr), source_(nullptr), charsToFree_(nullptr) 1.1400 +{ 1.1401 +} 1.1402 + 1.1403 +void 1.1404 +SourceDataCache::AutoHoldEntry::holdEntry(SourceDataCache *cache, ScriptSource *source) 1.1405 +{ 1.1406 + // Initialise the holder for a specific cache and script source. This will 1.1407 + // hold on to the cached source chars in the event that the cache is purged. 1.1408 + JS_ASSERT(!cache_ && !source_ && !charsToFree_); 1.1409 + cache_ = cache; 1.1410 + source_ = source; 1.1411 +} 1.1412 + 1.1413 +void 1.1414 +SourceDataCache::AutoHoldEntry::deferDelete(const jschar *chars) 1.1415 +{ 1.1416 + // Take ownership of source chars now the cache is being purged. Remove our 1.1417 + // reference to the ScriptSource which might soon be destroyed. 1.1418 + JS_ASSERT(cache_ && source_ && !charsToFree_); 1.1419 + cache_ = nullptr; 1.1420 + source_ = nullptr; 1.1421 + charsToFree_ = chars; 1.1422 +} 1.1423 + 1.1424 +SourceDataCache::AutoHoldEntry::~AutoHoldEntry() 1.1425 +{ 1.1426 + // The holder is going out of scope. If it has taken ownership of cached 1.1427 + // chars then delete them, otherwise unregister ourself with the cache. 1.1428 + if (charsToFree_) { 1.1429 + JS_ASSERT(!cache_ && !source_); 1.1430 + js_free(const_cast<jschar *>(charsToFree_)); 1.1431 + } else if (cache_) { 1.1432 + JS_ASSERT(source_); 1.1433 + cache_->releaseEntry(*this); 1.1434 + } 1.1435 +} 1.1436 + 1.1437 +void 1.1438 +SourceDataCache::holdEntry(AutoHoldEntry &holder, ScriptSource *ss) 1.1439 +{ 1.1440 + JS_ASSERT(!holder_); 1.1441 + holder.holdEntry(this, ss); 1.1442 + holder_ = &holder; 1.1443 +} 1.1444 + 1.1445 +void 1.1446 +SourceDataCache::releaseEntry(AutoHoldEntry &holder) 1.1447 +{ 1.1448 + JS_ASSERT(holder_ == &holder); 1.1449 + holder_ = nullptr; 1.1450 +} 1.1451 + 1.1452 +const jschar * 1.1453 +SourceDataCache::lookup(ScriptSource *ss, AutoHoldEntry &holder) 1.1454 +{ 1.1455 + JS_ASSERT(!holder_); 1.1456 + if (!map_) 1.1457 + return nullptr; 1.1458 + if (Map::Ptr p = map_->lookup(ss)) { 1.1459 + holdEntry(holder, ss); 1.1460 + return p->value(); 1.1461 + } 1.1462 + return nullptr; 1.1463 +} 1.1464 + 1.1465 +bool 1.1466 +SourceDataCache::put(ScriptSource *ss, const jschar *str, AutoHoldEntry &holder) 1.1467 +{ 1.1468 + JS_ASSERT(!holder_); 1.1469 + 1.1470 + if (!map_) { 1.1471 + map_ = js_new<Map>(); 1.1472 + if (!map_) 1.1473 + return false; 1.1474 + 1.1475 + if (!map_->init()) { 1.1476 + js_delete(map_); 1.1477 + map_ = nullptr; 1.1478 + return false; 1.1479 + } 1.1480 + } 1.1481 + 1.1482 + if (!map_->put(ss, str)) 1.1483 + return false; 1.1484 + 1.1485 + holdEntry(holder, ss); 1.1486 + return true; 1.1487 +} 1.1488 + 1.1489 +void 1.1490 +SourceDataCache::purge() 1.1491 +{ 1.1492 + if (!map_) 1.1493 + return; 1.1494 + 1.1495 + for (Map::Range r = map_->all(); !r.empty(); r.popFront()) { 1.1496 + const jschar *chars = r.front().value(); 1.1497 + if (holder_ && r.front().key() == holder_->source()) { 1.1498 + holder_->deferDelete(chars); 1.1499 + holder_ = nullptr; 1.1500 + } else { 1.1501 + js_free(const_cast<jschar*>(chars)); 1.1502 + } 1.1503 + } 1.1504 + 1.1505 + js_delete(map_); 1.1506 + map_ = nullptr; 1.1507 +} 1.1508 + 1.1509 +size_t 1.1510 +SourceDataCache::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) 1.1511 +{ 1.1512 + size_t n = 0; 1.1513 + if (map_ && !map_->empty()) { 1.1514 + n += map_->sizeOfIncludingThis(mallocSizeOf); 1.1515 + for (Map::Range r = map_->all(); !r.empty(); r.popFront()) { 1.1516 + const jschar *v = r.front().value(); 1.1517 + n += mallocSizeOf(v); 1.1518 + } 1.1519 + } 1.1520 + return n; 1.1521 +} 1.1522 + 1.1523 +const jschar * 1.1524 +ScriptSource::chars(JSContext *cx, SourceDataCache::AutoHoldEntry &holder) 1.1525 +{ 1.1526 + if (const jschar *chars = getOffThreadCompressionChars(cx)) 1.1527 + return chars; 1.1528 + JS_ASSERT(ready()); 1.1529 + 1.1530 +#ifdef USE_ZLIB 1.1531 + if (compressed()) { 1.1532 + if (const jschar *decompressed = cx->runtime()->sourceDataCache.lookup(this, holder)) 1.1533 + return decompressed; 1.1534 + 1.1535 + const size_t nbytes = sizeof(jschar) * (length_ + 1); 1.1536 + jschar *decompressed = static_cast<jschar *>(js_malloc(nbytes)); 1.1537 + if (!decompressed) 1.1538 + return nullptr; 1.1539 + 1.1540 + if (!DecompressString(data.compressed, compressedLength_, 1.1541 + reinterpret_cast<unsigned char *>(decompressed), nbytes)) { 1.1542 + JS_ReportOutOfMemory(cx); 1.1543 + js_free(decompressed); 1.1544 + return nullptr; 1.1545 + } 1.1546 + 1.1547 + decompressed[length_] = 0; 1.1548 + 1.1549 + if (!cx->runtime()->sourceDataCache.put(this, decompressed, holder)) { 1.1550 + JS_ReportOutOfMemory(cx); 1.1551 + js_free(decompressed); 1.1552 + return nullptr; 1.1553 + } 1.1554 + 1.1555 + return decompressed; 1.1556 + } 1.1557 +#endif 1.1558 + return data.source; 1.1559 +} 1.1560 + 1.1561 +JSFlatString * 1.1562 +ScriptSource::substring(JSContext *cx, uint32_t start, uint32_t stop) 1.1563 +{ 1.1564 + JS_ASSERT(start <= stop); 1.1565 + SourceDataCache::AutoHoldEntry holder; 1.1566 + const jschar *chars = this->chars(cx, holder); 1.1567 + if (!chars) 1.1568 + return nullptr; 1.1569 + return js_NewStringCopyN<CanGC>(cx, chars + start, stop - start); 1.1570 +} 1.1571 + 1.1572 +bool 1.1573 +ScriptSource::setSourceCopy(ExclusiveContext *cx, SourceBufferHolder &srcBuf, 1.1574 + bool argumentsNotIncluded, SourceCompressionTask *task) 1.1575 +{ 1.1576 + JS_ASSERT(!hasSourceData()); 1.1577 + length_ = srcBuf.length(); 1.1578 + argumentsNotIncluded_ = argumentsNotIncluded; 1.1579 + 1.1580 + // There are several cases where source compression is not a good idea: 1.1581 + // - If the script is tiny, then compression will save little or no space. 1.1582 + // - If the script is enormous, then decompression can take seconds. With 1.1583 + // lazy parsing, decompression is not uncommon, so this can significantly 1.1584 + // increase latency. 1.1585 + // - If there is only one core, then compression will contend with JS 1.1586 + // execution (which hurts benchmarketing). 1.1587 + // - If the source contains a giant string, then parsing will finish much 1.1588 + // faster than compression which increases latency (this case is handled 1.1589 + // in Parser::stringLiteral). 1.1590 + // 1.1591 + // Lastly, since the parsing thread will eventually perform a blocking wait 1.1592 + // on the compresion task's worker thread, require that there are at least 2 1.1593 + // worker threads: 1.1594 + // - If we are on a worker thread, there must be another worker thread to 1.1595 + // execute our compression task. 1.1596 + // - If we are on the main thread, there must be at least two worker 1.1597 + // threads since at most one worker thread can be blocking on the main 1.1598 + // thread (see WorkerThreadState::canStartParseTask) which would cause a 1.1599 + // deadlock if there wasn't a second worker thread that could make 1.1600 + // progress on our compression task. 1.1601 +#ifdef JS_THREADSAFE 1.1602 + bool canCompressOffThread = 1.1603 + WorkerThreadState().cpuCount > 1 && 1.1604 + WorkerThreadState().threadCount >= 2; 1.1605 +#else 1.1606 + bool canCompressOffThread = false; 1.1607 +#endif 1.1608 + const size_t TINY_SCRIPT = 256; 1.1609 + const size_t HUGE_SCRIPT = 5 * 1024 * 1024; 1.1610 + if (TINY_SCRIPT <= srcBuf.length() && srcBuf.length() < HUGE_SCRIPT && canCompressOffThread) { 1.1611 + task->ss = this; 1.1612 + task->chars = srcBuf.get(); 1.1613 + ready_ = false; 1.1614 + if (!StartOffThreadCompression(cx, task)) 1.1615 + return false; 1.1616 + } else if (srcBuf.ownsChars()) { 1.1617 + data.source = srcBuf.take(); 1.1618 + } else { 1.1619 + if (!adjustDataSize(sizeof(jschar) * srcBuf.length())) 1.1620 + return false; 1.1621 + PodCopy(data.source, srcBuf.get(), length_); 1.1622 + } 1.1623 + 1.1624 + return true; 1.1625 +} 1.1626 + 1.1627 +void 1.1628 +ScriptSource::setSource(const jschar *src, size_t length) 1.1629 +{ 1.1630 + JS_ASSERT(!hasSourceData()); 1.1631 + length_ = length; 1.1632 + JS_ASSERT(!argumentsNotIncluded_); 1.1633 + data.source = const_cast<jschar *>(src); 1.1634 +} 1.1635 + 1.1636 +bool 1.1637 +SourceCompressionTask::work() 1.1638 +{ 1.1639 + // A given compression token can be compressed on any thread, and the ss 1.1640 + // not being ready indicates to other threads that its fields might change 1.1641 + // with no lock held. 1.1642 + JS_ASSERT(!ss->ready()); 1.1643 + 1.1644 + size_t compressedLength = 0; 1.1645 + size_t nbytes = sizeof(jschar) * ss->length_; 1.1646 + 1.1647 + // Memory allocation functions on JSRuntime and JSContext are not 1.1648 + // threadsafe. We have to use the js_* variants. 1.1649 + 1.1650 +#ifdef USE_ZLIB 1.1651 + // Try to keep the maximum memory usage down by only allocating half the 1.1652 + // size of the string, first. 1.1653 + size_t firstSize = nbytes / 2; 1.1654 + if (!ss->adjustDataSize(firstSize)) 1.1655 + return false; 1.1656 + Compressor comp(reinterpret_cast<const unsigned char *>(chars), nbytes); 1.1657 + if (!comp.init()) 1.1658 + return false; 1.1659 + comp.setOutput(ss->data.compressed, firstSize); 1.1660 + bool cont = !abort_; 1.1661 + while (cont) { 1.1662 + switch (comp.compressMore()) { 1.1663 + case Compressor::CONTINUE: 1.1664 + break; 1.1665 + case Compressor::MOREOUTPUT: { 1.1666 + if (comp.outWritten() == nbytes) { 1.1667 + cont = false; 1.1668 + break; 1.1669 + } 1.1670 + 1.1671 + // The compressed output is greater than half the size of the 1.1672 + // original string. Reallocate to the full size. 1.1673 + if (!ss->adjustDataSize(nbytes)) 1.1674 + return false; 1.1675 + comp.setOutput(ss->data.compressed, nbytes); 1.1676 + break; 1.1677 + } 1.1678 + case Compressor::DONE: 1.1679 + cont = false; 1.1680 + break; 1.1681 + case Compressor::OOM: 1.1682 + return false; 1.1683 + } 1.1684 + cont = cont && !abort_; 1.1685 + } 1.1686 + compressedLength = comp.outWritten(); 1.1687 + if (abort_ || compressedLength == nbytes) 1.1688 + compressedLength = 0; 1.1689 +#endif 1.1690 + 1.1691 + if (compressedLength == 0) { 1.1692 + if (!ss->adjustDataSize(nbytes)) 1.1693 + return false; 1.1694 + PodCopy(ss->data.source, chars, ss->length()); 1.1695 + } else { 1.1696 + // Shrink the buffer to the size of the compressed data. Shouldn't fail. 1.1697 + JS_ALWAYS_TRUE(ss->adjustDataSize(compressedLength)); 1.1698 + } 1.1699 + ss->compressedLength_ = compressedLength; 1.1700 + return true; 1.1701 +} 1.1702 + 1.1703 +void 1.1704 +ScriptSource::destroy() 1.1705 +{ 1.1706 + JS_ASSERT(ready()); 1.1707 + adjustDataSize(0); 1.1708 + if (introducerFilename_ != filename_) 1.1709 + js_free(introducerFilename_); 1.1710 + js_free(filename_); 1.1711 + js_free(displayURL_); 1.1712 + js_free(sourceMapURL_); 1.1713 + if (originPrincipals_) 1.1714 + JS_DropPrincipals(TlsPerThreadData.get()->runtimeFromMainThread(), originPrincipals_); 1.1715 + ready_ = false; 1.1716 + js_free(this); 1.1717 +} 1.1718 + 1.1719 +void 1.1720 +ScriptSource::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, 1.1721 + JS::ScriptSourceInfo *info) const 1.1722 +{ 1.1723 + if (ready() && data.compressed != emptySource) { 1.1724 + if (compressed()) 1.1725 + info->compressed += mallocSizeOf(data.compressed); 1.1726 + else 1.1727 + info->uncompressed += mallocSizeOf(data.source); 1.1728 + } 1.1729 + info->misc += mallocSizeOf(this) + mallocSizeOf(filename_); 1.1730 + info->numScripts++; 1.1731 +} 1.1732 + 1.1733 +template<XDRMode mode> 1.1734 +bool 1.1735 +ScriptSource::performXDR(XDRState<mode> *xdr) 1.1736 +{ 1.1737 + uint8_t hasSource = hasSourceData(); 1.1738 + if (!xdr->codeUint8(&hasSource)) 1.1739 + return false; 1.1740 + 1.1741 + uint8_t retrievable = sourceRetrievable_; 1.1742 + if (!xdr->codeUint8(&retrievable)) 1.1743 + return false; 1.1744 + sourceRetrievable_ = retrievable; 1.1745 + 1.1746 + if (hasSource && !sourceRetrievable_) { 1.1747 + // Only set members when we know decoding cannot fail. This prevents the 1.1748 + // script source from being partially initialized. 1.1749 + uint32_t length = length_; 1.1750 + if (!xdr->codeUint32(&length)) 1.1751 + return false; 1.1752 + 1.1753 + uint32_t compressedLength = compressedLength_; 1.1754 + if (!xdr->codeUint32(&compressedLength)) 1.1755 + return false; 1.1756 + 1.1757 + uint8_t argumentsNotIncluded = argumentsNotIncluded_; 1.1758 + if (!xdr->codeUint8(&argumentsNotIncluded)) 1.1759 + return false; 1.1760 + 1.1761 + size_t byteLen = compressedLength ? compressedLength : (length * sizeof(jschar)); 1.1762 + if (mode == XDR_DECODE) { 1.1763 + if (!adjustDataSize(byteLen)) 1.1764 + return false; 1.1765 + } 1.1766 + if (!xdr->codeBytes(data.compressed, byteLen)) { 1.1767 + if (mode == XDR_DECODE) { 1.1768 + js_free(data.compressed); 1.1769 + data.compressed = nullptr; 1.1770 + } 1.1771 + return false; 1.1772 + } 1.1773 + length_ = length; 1.1774 + compressedLength_ = compressedLength; 1.1775 + argumentsNotIncluded_ = argumentsNotIncluded; 1.1776 + } 1.1777 + 1.1778 + uint8_t haveSourceMap = hasSourceMapURL(); 1.1779 + if (!xdr->codeUint8(&haveSourceMap)) 1.1780 + return false; 1.1781 + 1.1782 + if (haveSourceMap) { 1.1783 + uint32_t sourceMapURLLen = (mode == XDR_DECODE) ? 0 : js_strlen(sourceMapURL_); 1.1784 + if (!xdr->codeUint32(&sourceMapURLLen)) 1.1785 + return false; 1.1786 + 1.1787 + if (mode == XDR_DECODE) { 1.1788 + size_t byteLen = (sourceMapURLLen + 1) * sizeof(jschar); 1.1789 + sourceMapURL_ = static_cast<jschar *>(xdr->cx()->malloc_(byteLen)); 1.1790 + if (!sourceMapURL_) 1.1791 + return false; 1.1792 + } 1.1793 + if (!xdr->codeChars(sourceMapURL_, sourceMapURLLen)) { 1.1794 + if (mode == XDR_DECODE) { 1.1795 + js_free(sourceMapURL_); 1.1796 + sourceMapURL_ = nullptr; 1.1797 + } 1.1798 + return false; 1.1799 + } 1.1800 + sourceMapURL_[sourceMapURLLen] = '\0'; 1.1801 + } 1.1802 + 1.1803 + uint8_t haveDisplayURL = hasDisplayURL(); 1.1804 + if (!xdr->codeUint8(&haveDisplayURL)) 1.1805 + return false; 1.1806 + 1.1807 + if (haveDisplayURL) { 1.1808 + uint32_t displayURLLen = (mode == XDR_DECODE) ? 0 : js_strlen(displayURL_); 1.1809 + if (!xdr->codeUint32(&displayURLLen)) 1.1810 + return false; 1.1811 + 1.1812 + if (mode == XDR_DECODE) { 1.1813 + size_t byteLen = (displayURLLen + 1) * sizeof(jschar); 1.1814 + displayURL_ = static_cast<jschar *>(xdr->cx()->malloc_(byteLen)); 1.1815 + if (!displayURL_) 1.1816 + return false; 1.1817 + } 1.1818 + if (!xdr->codeChars(displayURL_, displayURLLen)) { 1.1819 + if (mode == XDR_DECODE) { 1.1820 + js_free(displayURL_); 1.1821 + displayURL_ = nullptr; 1.1822 + } 1.1823 + return false; 1.1824 + } 1.1825 + displayURL_[displayURLLen] = '\0'; 1.1826 + } 1.1827 + 1.1828 + uint8_t haveFilename = !!filename_; 1.1829 + if (!xdr->codeUint8(&haveFilename)) 1.1830 + return false; 1.1831 + 1.1832 + if (haveFilename) { 1.1833 + const char *fn = filename(); 1.1834 + if (!xdr->codeCString(&fn)) 1.1835 + return false; 1.1836 + if (mode == XDR_DECODE && !setFilename(xdr->cx(), fn)) 1.1837 + return false; 1.1838 + } 1.1839 + 1.1840 + if (mode == XDR_DECODE) 1.1841 + ready_ = true; 1.1842 + 1.1843 + return true; 1.1844 +} 1.1845 + 1.1846 +// Format and return a cx->malloc_'ed URL for a generated script like: 1.1847 +// {filename} line {lineno} > {introducer} 1.1848 +// For example: 1.1849 +// foo.js line 7 > eval 1.1850 +// indicating code compiled by the call to 'eval' on line 7 of foo.js. 1.1851 +static char * 1.1852 +FormatIntroducedFilename(ExclusiveContext *cx, const char *filename, unsigned lineno, 1.1853 + const char *introducer) 1.1854 +{ 1.1855 + // Compute the length of the string in advance, so we can allocate a 1.1856 + // buffer of the right size on the first shot. 1.1857 + // 1.1858 + // (JS_smprintf would be perfect, as that allocates the result 1.1859 + // dynamically as it formats the string, but it won't allocate from cx, 1.1860 + // and wants us to use a special free function.) 1.1861 + char linenoBuf[15]; 1.1862 + size_t filenameLen = strlen(filename); 1.1863 + size_t linenoLen = JS_snprintf(linenoBuf, 15, "%u", lineno); 1.1864 + size_t introducerLen = strlen(introducer); 1.1865 + size_t len = filenameLen + 1.1866 + 6 /* == strlen(" line ") */ + 1.1867 + linenoLen + 1.1868 + 3 /* == strlen(" > ") */ + 1.1869 + introducerLen + 1.1870 + 1 /* \0 */; 1.1871 + char *formatted = cx->pod_malloc<char>(len); 1.1872 + if (!formatted) 1.1873 + return nullptr; 1.1874 + mozilla::DebugOnly<size_t> checkLen = JS_snprintf(formatted, len, "%s line %s > %s", 1.1875 + filename, linenoBuf, introducer); 1.1876 + JS_ASSERT(checkLen == len - 1); 1.1877 + 1.1878 + return formatted; 1.1879 +} 1.1880 + 1.1881 +bool 1.1882 +ScriptSource::initFromOptions(ExclusiveContext *cx, const ReadOnlyCompileOptions &options) 1.1883 +{ 1.1884 + JS_ASSERT(!filename_); 1.1885 + JS_ASSERT(!introducerFilename_); 1.1886 + 1.1887 + originPrincipals_ = options.originPrincipals(cx); 1.1888 + if (originPrincipals_) 1.1889 + JS_HoldPrincipals(originPrincipals_); 1.1890 + 1.1891 + introductionType_ = options.introductionType; 1.1892 + setIntroductionOffset(options.introductionOffset); 1.1893 + 1.1894 + if (options.hasIntroductionInfo) { 1.1895 + JS_ASSERT(options.introductionType != nullptr); 1.1896 + const char *filename = options.filename() ? options.filename() : "<unknown>"; 1.1897 + char *formatted = FormatIntroducedFilename(cx, filename, options.introductionLineno, 1.1898 + options.introductionType); 1.1899 + if (!formatted) 1.1900 + return false; 1.1901 + filename_ = formatted; 1.1902 + } else if (options.filename()) { 1.1903 + if (!setFilename(cx, options.filename())) 1.1904 + return false; 1.1905 + } 1.1906 + 1.1907 + if (options.introducerFilename()) { 1.1908 + introducerFilename_ = js_strdup(cx, options.introducerFilename()); 1.1909 + if (!introducerFilename_) 1.1910 + return false; 1.1911 + } else { 1.1912 + introducerFilename_ = filename_; 1.1913 + } 1.1914 + 1.1915 + return true; 1.1916 +} 1.1917 + 1.1918 +bool 1.1919 +ScriptSource::setFilename(ExclusiveContext *cx, const char *filename) 1.1920 +{ 1.1921 + JS_ASSERT(!filename_); 1.1922 + filename_ = js_strdup(cx, filename); 1.1923 + if (!filename_) 1.1924 + return false; 1.1925 + return true; 1.1926 +} 1.1927 + 1.1928 +bool 1.1929 +ScriptSource::setDisplayURL(ExclusiveContext *cx, const jschar *displayURL) 1.1930 +{ 1.1931 + JS_ASSERT(displayURL); 1.1932 + if (hasDisplayURL()) { 1.1933 + if (cx->isJSContext() && 1.1934 + !JS_ReportErrorFlagsAndNumber(cx->asJSContext(), JSREPORT_WARNING, 1.1935 + js_GetErrorMessage, nullptr, 1.1936 + JSMSG_ALREADY_HAS_PRAGMA, filename_, 1.1937 + "//# sourceURL")) 1.1938 + { 1.1939 + return false; 1.1940 + } 1.1941 + } 1.1942 + size_t len = js_strlen(displayURL) + 1; 1.1943 + if (len == 1) 1.1944 + return true; 1.1945 + displayURL_ = js_strdup(cx, displayURL); 1.1946 + if (!displayURL_) 1.1947 + return false; 1.1948 + return true; 1.1949 +} 1.1950 + 1.1951 +const jschar * 1.1952 +ScriptSource::displayURL() 1.1953 +{ 1.1954 + JS_ASSERT(hasDisplayURL()); 1.1955 + return displayURL_; 1.1956 +} 1.1957 + 1.1958 +bool 1.1959 +ScriptSource::setSourceMapURL(ExclusiveContext *cx, const jschar *sourceMapURL) 1.1960 +{ 1.1961 + JS_ASSERT(sourceMapURL); 1.1962 + if (hasSourceMapURL()) { 1.1963 + if (cx->isJSContext() && 1.1964 + !JS_ReportErrorFlagsAndNumber(cx->asJSContext(), JSREPORT_WARNING, 1.1965 + js_GetErrorMessage, nullptr, 1.1966 + JSMSG_ALREADY_HAS_PRAGMA, filename_, 1.1967 + "//# sourceMappingURL")) 1.1968 + { 1.1969 + return false; 1.1970 + } 1.1971 + } 1.1972 + 1.1973 + size_t len = js_strlen(sourceMapURL) + 1; 1.1974 + if (len == 1) 1.1975 + return true; 1.1976 + sourceMapURL_ = js_strdup(cx, sourceMapURL); 1.1977 + if (!sourceMapURL_) 1.1978 + return false; 1.1979 + return true; 1.1980 +} 1.1981 + 1.1982 +const jschar * 1.1983 +ScriptSource::sourceMapURL() 1.1984 +{ 1.1985 + JS_ASSERT(hasSourceMapURL()); 1.1986 + return sourceMapURL_; 1.1987 +} 1.1988 + 1.1989 +/* 1.1990 + * Shared script data management. 1.1991 + */ 1.1992 + 1.1993 +SharedScriptData * 1.1994 +js::SharedScriptData::new_(ExclusiveContext *cx, uint32_t codeLength, 1.1995 + uint32_t srcnotesLength, uint32_t natoms) 1.1996 +{ 1.1997 + /* 1.1998 + * Ensure the atoms are aligned, as some architectures don't allow unaligned 1.1999 + * access. 1.2000 + */ 1.2001 + const uint32_t pointerSize = sizeof(JSAtom *); 1.2002 + const uint32_t pointerMask = pointerSize - 1; 1.2003 + const uint32_t dataOffset = offsetof(SharedScriptData, data); 1.2004 + uint32_t baseLength = codeLength + srcnotesLength; 1.2005 + uint32_t padding = (pointerSize - ((baseLength + dataOffset) & pointerMask)) & pointerMask; 1.2006 + uint32_t length = baseLength + padding + pointerSize * natoms; 1.2007 + 1.2008 + SharedScriptData *entry = (SharedScriptData *)cx->malloc_(length + dataOffset); 1.2009 + if (!entry) 1.2010 + return nullptr; 1.2011 + 1.2012 + entry->length = length; 1.2013 + entry->natoms = natoms; 1.2014 + entry->marked = false; 1.2015 + memset(entry->data + baseLength, 0, padding); 1.2016 + 1.2017 + /* 1.2018 + * Call constructors to initialize the storage that will be accessed as a 1.2019 + * HeapPtrAtom array via atoms(). 1.2020 + */ 1.2021 + HeapPtrAtom *atoms = entry->atoms(); 1.2022 + JS_ASSERT(reinterpret_cast<uintptr_t>(atoms) % sizeof(JSAtom *) == 0); 1.2023 + for (unsigned i = 0; i < natoms; ++i) 1.2024 + new (&atoms[i]) HeapPtrAtom(); 1.2025 + 1.2026 + return entry; 1.2027 +} 1.2028 + 1.2029 +/* 1.2030 + * Takes ownership of its *ssd parameter and either adds it into the runtime's 1.2031 + * ScriptDataTable or frees it if a matching entry already exists. 1.2032 + * 1.2033 + * Sets the |code| and |atoms| fields on the given JSScript. 1.2034 + */ 1.2035 +static bool 1.2036 +SaveSharedScriptData(ExclusiveContext *cx, Handle<JSScript *> script, SharedScriptData *ssd, 1.2037 + uint32_t nsrcnotes) 1.2038 +{ 1.2039 + ASSERT(script != nullptr); 1.2040 + ASSERT(ssd != nullptr); 1.2041 + 1.2042 + AutoLockForExclusiveAccess lock(cx); 1.2043 + 1.2044 + ScriptBytecodeHasher::Lookup l(ssd); 1.2045 + 1.2046 + ScriptDataTable::AddPtr p = cx->scriptDataTable().lookupForAdd(l); 1.2047 + if (p) { 1.2048 + js_free(ssd); 1.2049 + ssd = *p; 1.2050 + } else { 1.2051 + if (!cx->scriptDataTable().add(p, ssd)) { 1.2052 + script->setCode(nullptr); 1.2053 + script->atoms = nullptr; 1.2054 + js_free(ssd); 1.2055 + js_ReportOutOfMemory(cx); 1.2056 + return false; 1.2057 + } 1.2058 + } 1.2059 + 1.2060 +#ifdef JSGC_INCREMENTAL 1.2061 + /* 1.2062 + * During the IGC we need to ensure that bytecode is marked whenever it is 1.2063 + * accessed even if the bytecode was already in the table: at this point 1.2064 + * old scripts or exceptions pointing to the bytecode may no longer be 1.2065 + * reachable. This is effectively a read barrier. 1.2066 + */ 1.2067 + if (cx->isJSContext()) { 1.2068 + JSRuntime *rt = cx->asJSContext()->runtime(); 1.2069 + if (JS::IsIncrementalGCInProgress(rt) && rt->gcIsFull) 1.2070 + ssd->marked = true; 1.2071 + } 1.2072 +#endif 1.2073 + 1.2074 + script->setCode(ssd->data); 1.2075 + script->atoms = ssd->atoms(); 1.2076 + return true; 1.2077 +} 1.2078 + 1.2079 +static inline void 1.2080 +MarkScriptData(JSRuntime *rt, const jsbytecode *bytecode) 1.2081 +{ 1.2082 + /* 1.2083 + * As an invariant, a ScriptBytecodeEntry should not be 'marked' outside of 1.2084 + * a GC. Since SweepScriptBytecodes is only called during a full gc, 1.2085 + * to preserve this invariant, only mark during a full gc. 1.2086 + */ 1.2087 + if (rt->gcIsFull) 1.2088 + SharedScriptData::fromBytecode(bytecode)->marked = true; 1.2089 +} 1.2090 + 1.2091 +void 1.2092 +js::UnmarkScriptData(JSRuntime *rt) 1.2093 +{ 1.2094 + JS_ASSERT(rt->gcIsFull); 1.2095 + ScriptDataTable &table = rt->scriptDataTable(); 1.2096 + for (ScriptDataTable::Enum e(table); !e.empty(); e.popFront()) { 1.2097 + SharedScriptData *entry = e.front(); 1.2098 + entry->marked = false; 1.2099 + } 1.2100 +} 1.2101 + 1.2102 +void 1.2103 +js::SweepScriptData(JSRuntime *rt) 1.2104 +{ 1.2105 + JS_ASSERT(rt->gcIsFull); 1.2106 + ScriptDataTable &table = rt->scriptDataTable(); 1.2107 + 1.2108 + if (rt->keepAtoms()) 1.2109 + return; 1.2110 + 1.2111 + for (ScriptDataTable::Enum e(table); !e.empty(); e.popFront()) { 1.2112 + SharedScriptData *entry = e.front(); 1.2113 + if (!entry->marked) { 1.2114 + js_free(entry); 1.2115 + e.removeFront(); 1.2116 + } 1.2117 + } 1.2118 +} 1.2119 + 1.2120 +void 1.2121 +js::FreeScriptData(JSRuntime *rt) 1.2122 +{ 1.2123 + ScriptDataTable &table = rt->scriptDataTable(); 1.2124 + if (!table.initialized()) 1.2125 + return; 1.2126 + 1.2127 + for (ScriptDataTable::Enum e(table); !e.empty(); e.popFront()) 1.2128 + js_free(e.front()); 1.2129 + 1.2130 + table.clear(); 1.2131 +} 1.2132 + 1.2133 +/* 1.2134 + * JSScript::data and SharedScriptData::data have complex, 1.2135 + * manually-controlled, memory layouts. 1.2136 + * 1.2137 + * JSScript::data begins with some optional array headers. They are optional 1.2138 + * because they often aren't needed, i.e. the corresponding arrays often have 1.2139 + * zero elements. Each header has a bit in JSScript::hasArrayBits that 1.2140 + * indicates if it's present within |data|; from this the offset of each 1.2141 + * present array header can be computed. Each header has an accessor function 1.2142 + * in JSScript that encapsulates this offset computation. 1.2143 + * 1.2144 + * Array type Array elements Accessor 1.2145 + * ---------- -------------- -------- 1.2146 + * ConstArray Consts consts() 1.2147 + * ObjectArray Objects objects() 1.2148 + * ObjectArray Regexps regexps() 1.2149 + * TryNoteArray Try notes trynotes() 1.2150 + * BlockScopeArray Scope notes blockScopes() 1.2151 + * 1.2152 + * Then are the elements of several arrays. 1.2153 + * - Most of these arrays have headers listed above (if present). For each of 1.2154 + * these, the array pointer and the array length is stored in the header. 1.2155 + * - The remaining arrays have pointers and lengths that are stored directly in 1.2156 + * JSScript. This is because, unlike the others, they are nearly always 1.2157 + * non-zero length and so the optional-header space optimization isn't 1.2158 + * worthwhile. 1.2159 + * 1.2160 + * Array elements Pointed to by Length 1.2161 + * -------------- ------------- ------ 1.2162 + * Consts consts()->vector consts()->length 1.2163 + * Objects objects()->vector objects()->length 1.2164 + * Regexps regexps()->vector regexps()->length 1.2165 + * Try notes trynotes()->vector trynotes()->length 1.2166 + * Scope notes blockScopes()->vector blockScopes()->length 1.2167 + * 1.2168 + * IMPORTANT: This layout has two key properties. 1.2169 + * - It ensures that everything has sufficient alignment; in particular, the 1.2170 + * consts() elements need jsval alignment. 1.2171 + * - It ensures there are no gaps between elements, which saves space and makes 1.2172 + * manual layout easy. In particular, in the second part, arrays with larger 1.2173 + * elements precede arrays with smaller elements. 1.2174 + * 1.2175 + * SharedScriptData::data contains data that can be shared within a 1.2176 + * runtime. These items' layout is manually controlled to make it easier to 1.2177 + * manage both during (temporary) allocation and during matching against 1.2178 + * existing entries in the runtime. As the jsbytecode has to come first to 1.2179 + * enable lookup by bytecode identity, SharedScriptData::data, the atoms part 1.2180 + * has to manually be aligned sufficiently by adding padding after the notes 1.2181 + * part. 1.2182 + * 1.2183 + * Array elements Pointed to by Length 1.2184 + * -------------- ------------- ------ 1.2185 + * jsbytecode code length 1.2186 + * jsscrnote notes() numNotes() 1.2187 + * Atoms atoms natoms 1.2188 + * 1.2189 + * The following static assertions check JSScript::data's alignment properties. 1.2190 + */ 1.2191 + 1.2192 +#define KEEPS_JSVAL_ALIGNMENT(T) \ 1.2193 + (JS_ALIGNMENT_OF(jsval) % JS_ALIGNMENT_OF(T) == 0 && \ 1.2194 + sizeof(T) % sizeof(jsval) == 0) 1.2195 + 1.2196 +#define HAS_JSVAL_ALIGNMENT(T) \ 1.2197 + (JS_ALIGNMENT_OF(jsval) == JS_ALIGNMENT_OF(T) && \ 1.2198 + sizeof(T) == sizeof(jsval)) 1.2199 + 1.2200 +#define NO_PADDING_BETWEEN_ENTRIES(T1, T2) \ 1.2201 + (JS_ALIGNMENT_OF(T1) % JS_ALIGNMENT_OF(T2) == 0) 1.2202 + 1.2203 +/* 1.2204 + * These assertions ensure that there is no padding between the array headers, 1.2205 + * and also that the consts() elements (which follow immediately afterward) are 1.2206 + * jsval-aligned. (There is an assumption that |data| itself is jsval-aligned; 1.2207 + * we check this below). 1.2208 + */ 1.2209 +JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(ConstArray)); 1.2210 +JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(ObjectArray)); /* there are two of these */ 1.2211 +JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(TryNoteArray)); 1.2212 +JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(BlockScopeArray)); 1.2213 + 1.2214 +/* These assertions ensure there is no padding required between array elements. */ 1.2215 +JS_STATIC_ASSERT(HAS_JSVAL_ALIGNMENT(HeapValue)); 1.2216 +JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapValue, HeapPtrObject)); 1.2217 +JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapPtrObject, HeapPtrObject)); 1.2218 +JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapPtrObject, JSTryNote)); 1.2219 +JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(JSTryNote, uint32_t)); 1.2220 +JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(uint32_t, uint32_t)); 1.2221 + 1.2222 +JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapValue, BlockScopeNote)); 1.2223 +JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(BlockScopeNote, BlockScopeNote)); 1.2224 +JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(JSTryNote, BlockScopeNote)); 1.2225 +JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapPtrObject, BlockScopeNote)); 1.2226 +JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(BlockScopeNote, uint32_t)); 1.2227 + 1.2228 +static inline size_t 1.2229 +ScriptDataSize(uint32_t nbindings, uint32_t nconsts, uint32_t nobjects, uint32_t nregexps, 1.2230 + uint32_t ntrynotes, uint32_t nblockscopes) 1.2231 +{ 1.2232 + size_t size = 0; 1.2233 + 1.2234 + if (nconsts != 0) 1.2235 + size += sizeof(ConstArray) + nconsts * sizeof(Value); 1.2236 + if (nobjects != 0) 1.2237 + size += sizeof(ObjectArray) + nobjects * sizeof(JSObject *); 1.2238 + if (nregexps != 0) 1.2239 + size += sizeof(ObjectArray) + nregexps * sizeof(JSObject *); 1.2240 + if (ntrynotes != 0) 1.2241 + size += sizeof(TryNoteArray) + ntrynotes * sizeof(JSTryNote); 1.2242 + if (nblockscopes != 0) 1.2243 + size += sizeof(BlockScopeArray) + nblockscopes * sizeof(BlockScopeNote); 1.2244 + 1.2245 + if (nbindings != 0) { 1.2246 + // Make sure bindings are sufficiently aligned. 1.2247 + size = JS_ROUNDUP(size, JS_ALIGNMENT_OF(Binding)) + nbindings * sizeof(Binding); 1.2248 + } 1.2249 + 1.2250 + return size; 1.2251 +} 1.2252 + 1.2253 +void 1.2254 +JSScript::initCompartment(ExclusiveContext *cx) 1.2255 +{ 1.2256 + compartment_ = cx->compartment_; 1.2257 +} 1.2258 + 1.2259 +JSScript * 1.2260 +JSScript::Create(ExclusiveContext *cx, HandleObject enclosingScope, bool savedCallerFun, 1.2261 + const ReadOnlyCompileOptions &options, unsigned staticLevel, 1.2262 + HandleObject sourceObject, uint32_t bufStart, uint32_t bufEnd) 1.2263 +{ 1.2264 + JS_ASSERT(bufStart <= bufEnd); 1.2265 + 1.2266 + RootedScript script(cx, js_NewGCScript(cx)); 1.2267 + if (!script) 1.2268 + return nullptr; 1.2269 + 1.2270 + PodZero(script.get()); 1.2271 + new (&script->bindings) Bindings; 1.2272 + 1.2273 + script->enclosingScopeOrOriginalFunction_ = enclosingScope; 1.2274 + script->savedCallerFun_ = savedCallerFun; 1.2275 + script->initCompartment(cx); 1.2276 + 1.2277 + script->compileAndGo_ = options.compileAndGo; 1.2278 + script->selfHosted_ = options.selfHostingMode; 1.2279 + script->noScriptRval_ = options.noScriptRval; 1.2280 + 1.2281 + script->version = options.version; 1.2282 + JS_ASSERT(script->getVersion() == options.version); // assert that no overflow occurred 1.2283 + 1.2284 + // This is an unsigned-to-uint16_t conversion, test for too-high values. 1.2285 + // In practice, recursion in Parser and/or BytecodeEmitter will blow the 1.2286 + // stack if we nest functions more than a few hundred deep, so this will 1.2287 + // never trigger. Oh well. 1.2288 + if (staticLevel > UINT16_MAX) { 1.2289 + if (cx->isJSContext()) { 1.2290 + JS_ReportErrorNumber(cx->asJSContext(), 1.2291 + js_GetErrorMessage, nullptr, JSMSG_TOO_DEEP, js_function_str); 1.2292 + } 1.2293 + return nullptr; 1.2294 + } 1.2295 + script->staticLevel_ = uint16_t(staticLevel); 1.2296 + 1.2297 + script->setSourceObject(sourceObject); 1.2298 + script->sourceStart_ = bufStart; 1.2299 + script->sourceEnd_ = bufEnd; 1.2300 + 1.2301 + return script; 1.2302 +} 1.2303 + 1.2304 +static inline uint8_t * 1.2305 +AllocScriptData(ExclusiveContext *cx, size_t size) 1.2306 +{ 1.2307 + uint8_t *data = static_cast<uint8_t *>(cx->calloc_(JS_ROUNDUP(size, sizeof(Value)))); 1.2308 + if (!data) 1.2309 + return nullptr; 1.2310 + 1.2311 + // All script data is optional, so size might be 0. In that case, we don't care about alignment. 1.2312 + JS_ASSERT(size == 0 || size_t(data) % sizeof(Value) == 0); 1.2313 + return data; 1.2314 +} 1.2315 + 1.2316 +/* static */ bool 1.2317 +JSScript::partiallyInit(ExclusiveContext *cx, HandleScript script, uint32_t nconsts, 1.2318 + uint32_t nobjects, uint32_t nregexps, uint32_t ntrynotes, 1.2319 + uint32_t nblockscopes, uint32_t nTypeSets) 1.2320 +{ 1.2321 + size_t size = ScriptDataSize(script->bindings.count(), nconsts, nobjects, nregexps, ntrynotes, 1.2322 + nblockscopes); 1.2323 + if (size > 0) { 1.2324 + script->data = AllocScriptData(cx, size); 1.2325 + if (!script->data) 1.2326 + return false; 1.2327 + } else { 1.2328 + script->data = nullptr; 1.2329 + } 1.2330 + script->dataSize_ = size; 1.2331 + 1.2332 + JS_ASSERT(nTypeSets <= UINT16_MAX); 1.2333 + script->nTypeSets_ = uint16_t(nTypeSets); 1.2334 + 1.2335 + uint8_t *cursor = script->data; 1.2336 + if (nconsts != 0) { 1.2337 + script->setHasArray(CONSTS); 1.2338 + cursor += sizeof(ConstArray); 1.2339 + } 1.2340 + if (nobjects != 0) { 1.2341 + script->setHasArray(OBJECTS); 1.2342 + cursor += sizeof(ObjectArray); 1.2343 + } 1.2344 + if (nregexps != 0) { 1.2345 + script->setHasArray(REGEXPS); 1.2346 + cursor += sizeof(ObjectArray); 1.2347 + } 1.2348 + if (ntrynotes != 0) { 1.2349 + script->setHasArray(TRYNOTES); 1.2350 + cursor += sizeof(TryNoteArray); 1.2351 + } 1.2352 + if (nblockscopes != 0) { 1.2353 + script->setHasArray(BLOCK_SCOPES); 1.2354 + cursor += sizeof(BlockScopeArray); 1.2355 + } 1.2356 + 1.2357 + if (nconsts != 0) { 1.2358 + JS_ASSERT(reinterpret_cast<uintptr_t>(cursor) % sizeof(jsval) == 0); 1.2359 + script->consts()->length = nconsts; 1.2360 + script->consts()->vector = (HeapValue *)cursor; 1.2361 + cursor += nconsts * sizeof(script->consts()->vector[0]); 1.2362 + } 1.2363 + 1.2364 + if (nobjects != 0) { 1.2365 + script->objects()->length = nobjects; 1.2366 + script->objects()->vector = (HeapPtr<JSObject> *)cursor; 1.2367 + cursor += nobjects * sizeof(script->objects()->vector[0]); 1.2368 + } 1.2369 + 1.2370 + if (nregexps != 0) { 1.2371 + script->regexps()->length = nregexps; 1.2372 + script->regexps()->vector = (HeapPtr<JSObject> *)cursor; 1.2373 + cursor += nregexps * sizeof(script->regexps()->vector[0]); 1.2374 + } 1.2375 + 1.2376 + if (ntrynotes != 0) { 1.2377 + script->trynotes()->length = ntrynotes; 1.2378 + script->trynotes()->vector = reinterpret_cast<JSTryNote *>(cursor); 1.2379 + size_t vectorSize = ntrynotes * sizeof(script->trynotes()->vector[0]); 1.2380 +#ifdef DEBUG 1.2381 + memset(cursor, 0, vectorSize); 1.2382 +#endif 1.2383 + cursor += vectorSize; 1.2384 + } 1.2385 + 1.2386 + if (nblockscopes != 0) { 1.2387 + script->blockScopes()->length = nblockscopes; 1.2388 + script->blockScopes()->vector = reinterpret_cast<BlockScopeNote *>(cursor); 1.2389 + size_t vectorSize = nblockscopes * sizeof(script->blockScopes()->vector[0]); 1.2390 +#ifdef DEBUG 1.2391 + memset(cursor, 0, vectorSize); 1.2392 +#endif 1.2393 + cursor += vectorSize; 1.2394 + } 1.2395 + 1.2396 + if (script->bindings.count() != 0) { 1.2397 + // Make sure bindings are sufficiently aligned. 1.2398 + cursor = reinterpret_cast<uint8_t*> 1.2399 + (JS_ROUNDUP(reinterpret_cast<uintptr_t>(cursor), JS_ALIGNMENT_OF(Binding))); 1.2400 + } 1.2401 + cursor = script->bindings.switchToScriptStorage(reinterpret_cast<Binding *>(cursor)); 1.2402 + 1.2403 + JS_ASSERT(cursor == script->data + size); 1.2404 + return true; 1.2405 +} 1.2406 + 1.2407 +/* static */ bool 1.2408 +JSScript::fullyInitTrivial(ExclusiveContext *cx, Handle<JSScript*> script) 1.2409 +{ 1.2410 + if (!partiallyInit(cx, script, 0, 0, 0, 0, 0, 0)) 1.2411 + return false; 1.2412 + 1.2413 + SharedScriptData *ssd = SharedScriptData::new_(cx, 1, 1, 0); 1.2414 + if (!ssd) 1.2415 + return false; 1.2416 + 1.2417 + ssd->data[0] = JSOP_RETRVAL; 1.2418 + ssd->data[1] = SRC_NULL; 1.2419 + script->setLength(1); 1.2420 + return SaveSharedScriptData(cx, script, ssd, 1); 1.2421 +} 1.2422 + 1.2423 +/* static */ bool 1.2424 +JSScript::fullyInitFromEmitter(ExclusiveContext *cx, HandleScript script, BytecodeEmitter *bce) 1.2425 +{ 1.2426 + /* The counts of indexed things must be checked during code generation. */ 1.2427 + JS_ASSERT(bce->atomIndices->count() <= INDEX_LIMIT); 1.2428 + JS_ASSERT(bce->objectList.length <= INDEX_LIMIT); 1.2429 + JS_ASSERT(bce->regexpList.length <= INDEX_LIMIT); 1.2430 + 1.2431 + uint32_t mainLength = bce->offset(); 1.2432 + uint32_t prologLength = bce->prologOffset(); 1.2433 + uint32_t nsrcnotes; 1.2434 + if (!FinishTakingSrcNotes(cx, bce, &nsrcnotes)) 1.2435 + return false; 1.2436 + uint32_t natoms = bce->atomIndices->count(); 1.2437 + if (!partiallyInit(cx, script, 1.2438 + bce->constList.length(), bce->objectList.length, bce->regexpList.length, 1.2439 + bce->tryNoteList.length(), bce->blockScopeList.length(), bce->typesetCount)) 1.2440 + { 1.2441 + return false; 1.2442 + } 1.2443 + 1.2444 + JS_ASSERT(script->mainOffset() == 0); 1.2445 + script->mainOffset_ = prologLength; 1.2446 + 1.2447 + script->lineno_ = bce->firstLine; 1.2448 + 1.2449 + script->setLength(prologLength + mainLength); 1.2450 + script->natoms_ = natoms; 1.2451 + SharedScriptData *ssd = SharedScriptData::new_(cx, script->length(), nsrcnotes, natoms); 1.2452 + if (!ssd) 1.2453 + return false; 1.2454 + 1.2455 + jsbytecode *code = ssd->data; 1.2456 + PodCopy<jsbytecode>(code, bce->prolog.code.begin(), prologLength); 1.2457 + PodCopy<jsbytecode>(code + prologLength, bce->code().begin(), mainLength); 1.2458 + CopySrcNotes(bce, (jssrcnote *)(code + script->length()), nsrcnotes); 1.2459 + InitAtomMap(bce->atomIndices.getMap(), ssd->atoms()); 1.2460 + 1.2461 + if (!SaveSharedScriptData(cx, script, ssd, nsrcnotes)) 1.2462 + return false; 1.2463 + 1.2464 + FunctionBox *funbox = bce->sc->isFunctionBox() ? bce->sc->asFunctionBox() : nullptr; 1.2465 + 1.2466 + if (bce->constList.length() != 0) 1.2467 + bce->constList.finish(script->consts()); 1.2468 + if (bce->objectList.length != 0) 1.2469 + bce->objectList.finish(script->objects()); 1.2470 + if (bce->regexpList.length != 0) 1.2471 + bce->regexpList.finish(script->regexps()); 1.2472 + if (bce->tryNoteList.length() != 0) 1.2473 + bce->tryNoteList.finish(script->trynotes()); 1.2474 + if (bce->blockScopeList.length() != 0) 1.2475 + bce->blockScopeList.finish(script->blockScopes()); 1.2476 + script->strict_ = bce->sc->strict; 1.2477 + script->explicitUseStrict_ = bce->sc->hasExplicitUseStrict(); 1.2478 + script->bindingsAccessedDynamically_ = bce->sc->bindingsAccessedDynamically(); 1.2479 + script->funHasExtensibleScope_ = funbox ? funbox->hasExtensibleScope() : false; 1.2480 + script->funNeedsDeclEnvObject_ = funbox ? funbox->needsDeclEnvObject() : false; 1.2481 + script->hasSingletons_ = bce->hasSingletons; 1.2482 + 1.2483 + if (funbox) { 1.2484 + if (funbox->argumentsHasLocalBinding()) { 1.2485 + // This must precede the script->bindings.transfer() call below 1.2486 + script->setArgumentsHasVarBinding(); 1.2487 + if (funbox->definitelyNeedsArgsObj()) 1.2488 + script->setNeedsArgsObj(true); 1.2489 + } else { 1.2490 + JS_ASSERT(!funbox->definitelyNeedsArgsObj()); 1.2491 + } 1.2492 + 1.2493 + script->funLength_ = funbox->length; 1.2494 + } 1.2495 + 1.2496 + RootedFunction fun(cx, nullptr); 1.2497 + if (funbox) { 1.2498 + JS_ASSERT(!bce->script->noScriptRval()); 1.2499 + script->isGeneratorExp_ = funbox->inGenexpLambda; 1.2500 + script->setGeneratorKind(funbox->generatorKind()); 1.2501 + script->setFunction(funbox->function()); 1.2502 + } 1.2503 + 1.2504 + // The call to nfixed() depends on the above setFunction() call. 1.2505 + if (UINT32_MAX - script->nfixed() < bce->maxStackDepth) { 1.2506 + bce->reportError(nullptr, JSMSG_NEED_DIET, "script"); 1.2507 + return false; 1.2508 + } 1.2509 + script->nslots_ = script->nfixed() + bce->maxStackDepth; 1.2510 + 1.2511 + for (unsigned i = 0, n = script->bindings.numArgs(); i < n; ++i) { 1.2512 + if (script->formalIsAliased(i)) { 1.2513 + script->funHasAnyAliasedFormal_ = true; 1.2514 + break; 1.2515 + } 1.2516 + } 1.2517 + 1.2518 + return true; 1.2519 +} 1.2520 + 1.2521 +size_t 1.2522 +JSScript::computedSizeOfData() const 1.2523 +{ 1.2524 + return dataSize(); 1.2525 +} 1.2526 + 1.2527 +size_t 1.2528 +JSScript::sizeOfData(mozilla::MallocSizeOf mallocSizeOf) const 1.2529 +{ 1.2530 + return mallocSizeOf(data); 1.2531 +} 1.2532 + 1.2533 +size_t 1.2534 +JSScript::sizeOfTypeScript(mozilla::MallocSizeOf mallocSizeOf) const 1.2535 +{ 1.2536 + return types->sizeOfIncludingThis(mallocSizeOf); 1.2537 +} 1.2538 + 1.2539 +/* 1.2540 + * Nb: srcnotes are variable-length. This function computes the number of 1.2541 + * srcnote *slots*, which may be greater than the number of srcnotes. 1.2542 + */ 1.2543 +uint32_t 1.2544 +JSScript::numNotes() 1.2545 +{ 1.2546 + jssrcnote *sn; 1.2547 + jssrcnote *notes_ = notes(); 1.2548 + for (sn = notes_; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) 1.2549 + continue; 1.2550 + return sn - notes_ + 1; /* +1 for the terminator */ 1.2551 +} 1.2552 + 1.2553 +js::GlobalObject& 1.2554 +JSScript::uninlinedGlobal() const 1.2555 +{ 1.2556 + return global(); 1.2557 +} 1.2558 + 1.2559 +void 1.2560 +js::CallNewScriptHook(JSContext *cx, HandleScript script, HandleFunction fun) 1.2561 +{ 1.2562 + if (script->selfHosted()) 1.2563 + return; 1.2564 + 1.2565 + JS_ASSERT(!script->isActiveEval()); 1.2566 + if (JSNewScriptHook hook = cx->runtime()->debugHooks.newScriptHook) { 1.2567 + AutoKeepAtoms keepAtoms(cx->perThreadData); 1.2568 + hook(cx, script->filename(), script->lineno(), script, fun, 1.2569 + cx->runtime()->debugHooks.newScriptHookData); 1.2570 + } 1.2571 +} 1.2572 + 1.2573 +void 1.2574 +js::CallDestroyScriptHook(FreeOp *fop, JSScript *script) 1.2575 +{ 1.2576 + if (script->selfHosted()) 1.2577 + return; 1.2578 + 1.2579 + // The hook will only call into JS if a GC is not running. 1.2580 + if (JSDestroyScriptHook hook = fop->runtime()->debugHooks.destroyScriptHook) 1.2581 + hook(fop, script, fop->runtime()->debugHooks.destroyScriptHookData); 1.2582 + script->clearTraps(fop); 1.2583 +} 1.2584 + 1.2585 +void 1.2586 +JSScript::finalize(FreeOp *fop) 1.2587 +{ 1.2588 + // NOTE: this JSScript may be partially initialized at this point. E.g. we 1.2589 + // may have created it and partially initialized it with 1.2590 + // JSScript::Create(), but not yet finished initializing it with 1.2591 + // fullyInitFromEmitter() or fullyInitTrivial(). 1.2592 + 1.2593 + CallDestroyScriptHook(fop, this); 1.2594 + fop->runtime()->spsProfiler.onScriptFinalized(this); 1.2595 + 1.2596 + if (types) 1.2597 + types->destroy(); 1.2598 + 1.2599 +#ifdef JS_ION 1.2600 + jit::DestroyIonScripts(fop, this); 1.2601 +#endif 1.2602 + 1.2603 + destroyScriptCounts(fop); 1.2604 + destroyDebugScript(fop); 1.2605 + 1.2606 + if (data) { 1.2607 + JS_POISON(data, 0xdb, computedSizeOfData()); 1.2608 + fop->free_(data); 1.2609 + } 1.2610 + 1.2611 + fop->runtime()->lazyScriptCache.remove(this); 1.2612 +} 1.2613 + 1.2614 +static const uint32_t GSN_CACHE_THRESHOLD = 100; 1.2615 + 1.2616 +void 1.2617 +GSNCache::purge() 1.2618 +{ 1.2619 + code = nullptr; 1.2620 + if (map.initialized()) 1.2621 + map.finish(); 1.2622 +} 1.2623 + 1.2624 +jssrcnote * 1.2625 +js::GetSrcNote(GSNCache &cache, JSScript *script, jsbytecode *pc) 1.2626 +{ 1.2627 + size_t target = pc - script->code(); 1.2628 + if (target >= script->length()) 1.2629 + return nullptr; 1.2630 + 1.2631 + if (cache.code == script->code()) { 1.2632 + JS_ASSERT(cache.map.initialized()); 1.2633 + GSNCache::Map::Ptr p = cache.map.lookup(pc); 1.2634 + return p ? p->value() : nullptr; 1.2635 + } 1.2636 + 1.2637 + size_t offset = 0; 1.2638 + jssrcnote *result; 1.2639 + for (jssrcnote *sn = script->notes(); ; sn = SN_NEXT(sn)) { 1.2640 + if (SN_IS_TERMINATOR(sn)) { 1.2641 + result = nullptr; 1.2642 + break; 1.2643 + } 1.2644 + offset += SN_DELTA(sn); 1.2645 + if (offset == target && SN_IS_GETTABLE(sn)) { 1.2646 + result = sn; 1.2647 + break; 1.2648 + } 1.2649 + } 1.2650 + 1.2651 + if (cache.code != script->code() && script->length() >= GSN_CACHE_THRESHOLD) { 1.2652 + unsigned nsrcnotes = 0; 1.2653 + for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); 1.2654 + sn = SN_NEXT(sn)) { 1.2655 + if (SN_IS_GETTABLE(sn)) 1.2656 + ++nsrcnotes; 1.2657 + } 1.2658 + if (cache.code) { 1.2659 + JS_ASSERT(cache.map.initialized()); 1.2660 + cache.map.finish(); 1.2661 + cache.code = nullptr; 1.2662 + } 1.2663 + if (cache.map.init(nsrcnotes)) { 1.2664 + pc = script->code(); 1.2665 + for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); 1.2666 + sn = SN_NEXT(sn)) { 1.2667 + pc += SN_DELTA(sn); 1.2668 + if (SN_IS_GETTABLE(sn)) 1.2669 + JS_ALWAYS_TRUE(cache.map.put(pc, sn)); 1.2670 + } 1.2671 + cache.code = script->code(); 1.2672 + } 1.2673 + } 1.2674 + 1.2675 + return result; 1.2676 +} 1.2677 + 1.2678 +jssrcnote * 1.2679 +js_GetSrcNote(JSContext *cx, JSScript *script, jsbytecode *pc) 1.2680 +{ 1.2681 + return GetSrcNote(cx->runtime()->gsnCache, script, pc); 1.2682 +} 1.2683 + 1.2684 +unsigned 1.2685 +js::PCToLineNumber(unsigned startLine, jssrcnote *notes, jsbytecode *code, jsbytecode *pc, 1.2686 + unsigned *columnp) 1.2687 +{ 1.2688 + unsigned lineno = startLine; 1.2689 + unsigned column = 0; 1.2690 + 1.2691 + /* 1.2692 + * Walk through source notes accumulating their deltas, keeping track of 1.2693 + * line-number notes, until we pass the note for pc's offset within 1.2694 + * script->code. 1.2695 + */ 1.2696 + ptrdiff_t offset = 0; 1.2697 + ptrdiff_t target = pc - code; 1.2698 + for (jssrcnote *sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { 1.2699 + offset += SN_DELTA(sn); 1.2700 + SrcNoteType type = (SrcNoteType) SN_TYPE(sn); 1.2701 + if (type == SRC_SETLINE) { 1.2702 + if (offset <= target) 1.2703 + lineno = (unsigned) js_GetSrcNoteOffset(sn, 0); 1.2704 + column = 0; 1.2705 + } else if (type == SRC_NEWLINE) { 1.2706 + if (offset <= target) 1.2707 + lineno++; 1.2708 + column = 0; 1.2709 + } 1.2710 + 1.2711 + if (offset > target) 1.2712 + break; 1.2713 + 1.2714 + if (type == SRC_COLSPAN) { 1.2715 + ptrdiff_t colspan = js_GetSrcNoteOffset(sn, 0); 1.2716 + 1.2717 + if (colspan >= SN_COLSPAN_DOMAIN / 2) 1.2718 + colspan -= SN_COLSPAN_DOMAIN; 1.2719 + JS_ASSERT(ptrdiff_t(column) + colspan >= 0); 1.2720 + column += colspan; 1.2721 + } 1.2722 + } 1.2723 + 1.2724 + if (columnp) 1.2725 + *columnp = column; 1.2726 + 1.2727 + return lineno; 1.2728 +} 1.2729 + 1.2730 +unsigned 1.2731 +js::PCToLineNumber(JSScript *script, jsbytecode *pc, unsigned *columnp) 1.2732 +{ 1.2733 + /* Cope with InterpreterFrame.pc value prior to entering Interpret. */ 1.2734 + if (!pc) 1.2735 + return 0; 1.2736 + 1.2737 + return PCToLineNumber(script->lineno(), script->notes(), script->code(), pc, columnp); 1.2738 +} 1.2739 + 1.2740 +jsbytecode * 1.2741 +js_LineNumberToPC(JSScript *script, unsigned target) 1.2742 +{ 1.2743 + ptrdiff_t offset = 0; 1.2744 + ptrdiff_t best = -1; 1.2745 + unsigned lineno = script->lineno(); 1.2746 + unsigned bestdiff = SN_MAX_OFFSET; 1.2747 + for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { 1.2748 + /* 1.2749 + * Exact-match only if offset is not in the prolog; otherwise use 1.2750 + * nearest greater-or-equal line number match. 1.2751 + */ 1.2752 + if (lineno == target && offset >= ptrdiff_t(script->mainOffset())) 1.2753 + goto out; 1.2754 + if (lineno >= target) { 1.2755 + unsigned diff = lineno - target; 1.2756 + if (diff < bestdiff) { 1.2757 + bestdiff = diff; 1.2758 + best = offset; 1.2759 + } 1.2760 + } 1.2761 + offset += SN_DELTA(sn); 1.2762 + SrcNoteType type = (SrcNoteType) SN_TYPE(sn); 1.2763 + if (type == SRC_SETLINE) { 1.2764 + lineno = (unsigned) js_GetSrcNoteOffset(sn, 0); 1.2765 + } else if (type == SRC_NEWLINE) { 1.2766 + lineno++; 1.2767 + } 1.2768 + } 1.2769 + if (best >= 0) 1.2770 + offset = best; 1.2771 +out: 1.2772 + return script->offsetToPC(offset); 1.2773 +} 1.2774 + 1.2775 +JS_FRIEND_API(unsigned) 1.2776 +js_GetScriptLineExtent(JSScript *script) 1.2777 +{ 1.2778 + unsigned lineno = script->lineno(); 1.2779 + unsigned maxLineNo = lineno; 1.2780 + for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { 1.2781 + SrcNoteType type = (SrcNoteType) SN_TYPE(sn); 1.2782 + if (type == SRC_SETLINE) 1.2783 + lineno = (unsigned) js_GetSrcNoteOffset(sn, 0); 1.2784 + else if (type == SRC_NEWLINE) 1.2785 + lineno++; 1.2786 + 1.2787 + if (maxLineNo < lineno) 1.2788 + maxLineNo = lineno; 1.2789 + } 1.2790 + 1.2791 + return 1 + maxLineNo - script->lineno(); 1.2792 +} 1.2793 + 1.2794 +void 1.2795 +js::DescribeScriptedCallerForCompilation(JSContext *cx, MutableHandleScript maybeScript, 1.2796 + const char **file, unsigned *linenop, 1.2797 + uint32_t *pcOffset, JSPrincipals **origin, 1.2798 + LineOption opt) 1.2799 +{ 1.2800 + if (opt == CALLED_FROM_JSOP_EVAL) { 1.2801 + jsbytecode *pc = nullptr; 1.2802 + maybeScript.set(cx->currentScript(&pc)); 1.2803 + JS_ASSERT(JSOp(*pc) == JSOP_EVAL || JSOp(*pc) == JSOP_SPREADEVAL); 1.2804 + JS_ASSERT(*(pc + (JSOp(*pc) == JSOP_EVAL ? JSOP_EVAL_LENGTH 1.2805 + : JSOP_SPREADEVAL_LENGTH)) == JSOP_LINENO); 1.2806 + *file = maybeScript->filename(); 1.2807 + *linenop = GET_UINT16(pc + (JSOp(*pc) == JSOP_EVAL ? JSOP_EVAL_LENGTH 1.2808 + : JSOP_SPREADEVAL_LENGTH)); 1.2809 + *pcOffset = pc - maybeScript->code(); 1.2810 + *origin = maybeScript->originPrincipals(); 1.2811 + return; 1.2812 + } 1.2813 + 1.2814 + NonBuiltinFrameIter iter(cx); 1.2815 + 1.2816 + if (iter.done()) { 1.2817 + maybeScript.set(nullptr); 1.2818 + *file = nullptr; 1.2819 + *linenop = 0; 1.2820 + *pcOffset = 0; 1.2821 + *origin = cx->compartment()->principals; 1.2822 + return; 1.2823 + } 1.2824 + 1.2825 + *file = iter.scriptFilename(); 1.2826 + *linenop = iter.computeLine(); 1.2827 + *origin = iter.originPrincipals(); 1.2828 + 1.2829 + // These values are only used for introducer fields which are debugging 1.2830 + // information and can be safely left null for asm.js frames. 1.2831 + if (iter.hasScript()) { 1.2832 + maybeScript.set(iter.script()); 1.2833 + *pcOffset = iter.pc() - maybeScript->code(); 1.2834 + } else { 1.2835 + maybeScript.set(nullptr); 1.2836 + *pcOffset = 0; 1.2837 + } 1.2838 +} 1.2839 + 1.2840 +template <class T> 1.2841 +static inline T * 1.2842 +Rebase(JSScript *dst, JSScript *src, T *srcp) 1.2843 +{ 1.2844 + size_t off = reinterpret_cast<uint8_t *>(srcp) - src->data; 1.2845 + return reinterpret_cast<T *>(dst->data + off); 1.2846 +} 1.2847 + 1.2848 +JSScript * 1.2849 +js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, HandleScript src, 1.2850 + NewObjectKind newKind /* = GenericObject */) 1.2851 +{ 1.2852 + /* NB: Keep this in sync with XDRScript. */ 1.2853 + 1.2854 + /* Some embeddings are not careful to use ExposeObjectToActiveJS as needed. */ 1.2855 + JS_ASSERT(!src->sourceObject()->isMarked(gc::GRAY)); 1.2856 + 1.2857 + uint32_t nconsts = src->hasConsts() ? src->consts()->length : 0; 1.2858 + uint32_t nobjects = src->hasObjects() ? src->objects()->length : 0; 1.2859 + uint32_t nregexps = src->hasRegexps() ? src->regexps()->length : 0; 1.2860 + uint32_t ntrynotes = src->hasTrynotes() ? src->trynotes()->length : 0; 1.2861 + uint32_t nblockscopes = src->hasBlockScopes() ? src->blockScopes()->length : 0; 1.2862 + 1.2863 + /* Script data */ 1.2864 + 1.2865 + size_t size = src->dataSize(); 1.2866 + uint8_t *data = AllocScriptData(cx, size); 1.2867 + if (!data) 1.2868 + return nullptr; 1.2869 + 1.2870 + /* Bindings */ 1.2871 + 1.2872 + Rooted<Bindings> bindings(cx); 1.2873 + InternalHandle<Bindings*> bindingsHandle = 1.2874 + InternalHandle<Bindings*>::fromMarkedLocation(bindings.address()); 1.2875 + if (!Bindings::clone(cx, bindingsHandle, data, src)) 1.2876 + return nullptr; 1.2877 + 1.2878 + /* Objects */ 1.2879 + 1.2880 + AutoObjectVector objects(cx); 1.2881 + if (nobjects != 0) { 1.2882 + HeapPtrObject *vector = src->objects()->vector; 1.2883 + for (unsigned i = 0; i < nobjects; i++) { 1.2884 + RootedObject obj(cx, vector[i]); 1.2885 + RootedObject clone(cx); 1.2886 + if (obj->is<NestedScopeObject>()) { 1.2887 + Rooted<NestedScopeObject*> innerBlock(cx, &obj->as<NestedScopeObject>()); 1.2888 + 1.2889 + RootedObject enclosingScope(cx); 1.2890 + if (NestedScopeObject *enclosingBlock = innerBlock->enclosingNestedScope()) 1.2891 + enclosingScope = objects[FindScopeObjectIndex(src, *enclosingBlock)]; 1.2892 + else 1.2893 + enclosingScope = fun; 1.2894 + 1.2895 + clone = CloneNestedScopeObject(cx, enclosingScope, innerBlock); 1.2896 + } else if (obj->is<JSFunction>()) { 1.2897 + RootedFunction innerFun(cx, &obj->as<JSFunction>()); 1.2898 + if (innerFun->isNative()) { 1.2899 + assertSameCompartment(cx, innerFun); 1.2900 + clone = innerFun; 1.2901 + } else { 1.2902 + if (innerFun->isInterpretedLazy()) { 1.2903 + AutoCompartment ac(cx, innerFun); 1.2904 + if (!innerFun->getOrCreateScript(cx)) 1.2905 + return nullptr; 1.2906 + } 1.2907 + RootedObject staticScope(cx, innerFun->nonLazyScript()->enclosingStaticScope()); 1.2908 + StaticScopeIter<CanGC> ssi(cx, staticScope); 1.2909 + RootedObject enclosingScope(cx); 1.2910 + if (ssi.done() || ssi.type() == StaticScopeIter<CanGC>::FUNCTION) 1.2911 + enclosingScope = fun; 1.2912 + else if (ssi.type() == StaticScopeIter<CanGC>::BLOCK) 1.2913 + enclosingScope = objects[FindScopeObjectIndex(src, ssi.block())]; 1.2914 + else 1.2915 + enclosingScope = objects[FindScopeObjectIndex(src, ssi.staticWith())]; 1.2916 + 1.2917 + clone = CloneFunctionAndScript(cx, enclosingScope, innerFun); 1.2918 + } 1.2919 + } else { 1.2920 + /* 1.2921 + * Clone object literals emitted for the JSOP_NEWOBJECT opcode. We only emit that 1.2922 + * instead of the less-optimized JSOP_NEWINIT for self-hosted code or code compiled 1.2923 + * with JSOPTION_COMPILE_N_GO set. As we don't clone the latter type of code, this 1.2924 + * case should only ever be hit when cloning objects from self-hosted code. 1.2925 + */ 1.2926 + clone = CloneObjectLiteral(cx, cx->global(), obj); 1.2927 + } 1.2928 + if (!clone || !objects.append(clone)) 1.2929 + return nullptr; 1.2930 + } 1.2931 + } 1.2932 + 1.2933 + /* RegExps */ 1.2934 + 1.2935 + AutoObjectVector regexps(cx); 1.2936 + for (unsigned i = 0; i < nregexps; i++) { 1.2937 + HeapPtrObject *vector = src->regexps()->vector; 1.2938 + for (unsigned i = 0; i < nregexps; i++) { 1.2939 + JSObject *clone = CloneScriptRegExpObject(cx, vector[i]->as<RegExpObject>()); 1.2940 + if (!clone || !regexps.append(clone)) 1.2941 + return nullptr; 1.2942 + } 1.2943 + } 1.2944 + 1.2945 + /* 1.2946 + * Wrap the script source object as needed. Self-hosted scripts may be 1.2947 + * in another runtime, so lazily create a new script source object to 1.2948 + * use for them. 1.2949 + */ 1.2950 + RootedObject sourceObject(cx); 1.2951 + if (cx->runtime()->isSelfHostingCompartment(src->compartment())) { 1.2952 + if (!cx->compartment()->selfHostingScriptSource) { 1.2953 + CompileOptions options(cx); 1.2954 + FillSelfHostingCompileOptions(options); 1.2955 + 1.2956 + ScriptSourceObject *obj = frontend::CreateScriptSourceObject(cx, options); 1.2957 + if (!obj) 1.2958 + return nullptr; 1.2959 + cx->compartment()->selfHostingScriptSource = obj; 1.2960 + } 1.2961 + sourceObject = cx->compartment()->selfHostingScriptSource; 1.2962 + } else { 1.2963 + sourceObject = src->sourceObject(); 1.2964 + if (!cx->compartment()->wrap(cx, &sourceObject)) 1.2965 + return nullptr; 1.2966 + } 1.2967 + 1.2968 + /* Now that all fallible allocation is complete, create the GC thing. */ 1.2969 + 1.2970 + CompileOptions options(cx); 1.2971 + options.setOriginPrincipals(src->originPrincipals()) 1.2972 + .setCompileAndGo(src->compileAndGo()) 1.2973 + .setSelfHostingMode(src->selfHosted()) 1.2974 + .setNoScriptRval(src->noScriptRval()) 1.2975 + .setVersion(src->getVersion()); 1.2976 + 1.2977 + RootedScript dst(cx, JSScript::Create(cx, enclosingScope, src->savedCallerFun(), 1.2978 + options, src->staticLevel(), 1.2979 + sourceObject, src->sourceStart(), src->sourceEnd())); 1.2980 + if (!dst) { 1.2981 + js_free(data); 1.2982 + return nullptr; 1.2983 + } 1.2984 + 1.2985 + dst->bindings = bindings; 1.2986 + 1.2987 + /* This assignment must occur before all the Rebase calls. */ 1.2988 + dst->data = data; 1.2989 + dst->dataSize_ = size; 1.2990 + memcpy(data, src->data, size); 1.2991 + 1.2992 + /* Script filenames, bytecodes and atoms are runtime-wide. */ 1.2993 + dst->setCode(src->code()); 1.2994 + dst->atoms = src->atoms; 1.2995 + 1.2996 + dst->setLength(src->length()); 1.2997 + dst->lineno_ = src->lineno(); 1.2998 + dst->mainOffset_ = src->mainOffset(); 1.2999 + dst->natoms_ = src->natoms(); 1.3000 + dst->funLength_ = src->funLength(); 1.3001 + dst->nTypeSets_ = src->nTypeSets(); 1.3002 + dst->nslots_ = src->nslots(); 1.3003 + if (src->argumentsHasVarBinding()) { 1.3004 + dst->setArgumentsHasVarBinding(); 1.3005 + if (src->analyzedArgsUsage()) 1.3006 + dst->setNeedsArgsObj(src->needsArgsObj()); 1.3007 + } 1.3008 + dst->cloneHasArray(src); 1.3009 + dst->strict_ = src->strict(); 1.3010 + dst->explicitUseStrict_ = src->explicitUseStrict(); 1.3011 + dst->bindingsAccessedDynamically_ = src->bindingsAccessedDynamically(); 1.3012 + dst->funHasExtensibleScope_ = src->funHasExtensibleScope(); 1.3013 + dst->funNeedsDeclEnvObject_ = src->funNeedsDeclEnvObject(); 1.3014 + dst->funHasAnyAliasedFormal_ = src->funHasAnyAliasedFormal(); 1.3015 + dst->hasSingletons_ = src->hasSingletons(); 1.3016 + dst->treatAsRunOnce_ = src->treatAsRunOnce(); 1.3017 + dst->isGeneratorExp_ = src->isGeneratorExp(); 1.3018 + dst->setGeneratorKind(src->generatorKind()); 1.3019 + 1.3020 + /* Copy over hints. */ 1.3021 + dst->shouldInline_ = src->shouldInline(); 1.3022 + dst->shouldCloneAtCallsite_ = src->shouldCloneAtCallsite(); 1.3023 + dst->isCallsiteClone_ = src->isCallsiteClone(); 1.3024 + 1.3025 + if (nconsts != 0) { 1.3026 + HeapValue *vector = Rebase<HeapValue>(dst, src, src->consts()->vector); 1.3027 + dst->consts()->vector = vector; 1.3028 + for (unsigned i = 0; i < nconsts; ++i) 1.3029 + JS_ASSERT_IF(vector[i].isMarkable(), vector[i].toString()->isAtom()); 1.3030 + } 1.3031 + if (nobjects != 0) { 1.3032 + HeapPtrObject *vector = Rebase<HeapPtr<JSObject> >(dst, src, src->objects()->vector); 1.3033 + dst->objects()->vector = vector; 1.3034 + for (unsigned i = 0; i < nobjects; ++i) 1.3035 + vector[i].init(objects[i]); 1.3036 + } 1.3037 + if (nregexps != 0) { 1.3038 + HeapPtrObject *vector = Rebase<HeapPtr<JSObject> >(dst, src, src->regexps()->vector); 1.3039 + dst->regexps()->vector = vector; 1.3040 + for (unsigned i = 0; i < nregexps; ++i) 1.3041 + vector[i].init(regexps[i]); 1.3042 + } 1.3043 + if (ntrynotes != 0) 1.3044 + dst->trynotes()->vector = Rebase<JSTryNote>(dst, src, src->trynotes()->vector); 1.3045 + if (nblockscopes != 0) 1.3046 + dst->blockScopes()->vector = Rebase<BlockScopeNote>(dst, src, src->blockScopes()->vector); 1.3047 + 1.3048 + return dst; 1.3049 +} 1.3050 + 1.3051 +bool 1.3052 +js::CloneFunctionScript(JSContext *cx, HandleFunction original, HandleFunction clone, 1.3053 + NewObjectKind newKind /* = GenericObject */) 1.3054 +{ 1.3055 + JS_ASSERT(clone->isInterpreted()); 1.3056 + 1.3057 + RootedScript script(cx, clone->nonLazyScript()); 1.3058 + JS_ASSERT(script); 1.3059 + JS_ASSERT(script->compartment() == original->compartment()); 1.3060 + JS_ASSERT_IF(script->compartment() != cx->compartment(), 1.3061 + !script->enclosingStaticScope()); 1.3062 + 1.3063 + RootedObject scope(cx, script->enclosingStaticScope()); 1.3064 + 1.3065 + clone->mutableScript().init(nullptr); 1.3066 + 1.3067 + JSScript *cscript = CloneScript(cx, scope, clone, script, newKind); 1.3068 + if (!cscript) 1.3069 + return false; 1.3070 + 1.3071 + clone->setScript(cscript); 1.3072 + cscript->setFunction(clone); 1.3073 + 1.3074 + script = clone->nonLazyScript(); 1.3075 + CallNewScriptHook(cx, script, clone); 1.3076 + RootedGlobalObject global(cx, script->compileAndGo() ? &script->global() : nullptr); 1.3077 + Debugger::onNewScript(cx, script, global); 1.3078 + 1.3079 + return true; 1.3080 +} 1.3081 + 1.3082 +DebugScript * 1.3083 +JSScript::debugScript() 1.3084 +{ 1.3085 + JS_ASSERT(hasDebugScript_); 1.3086 + DebugScriptMap *map = compartment()->debugScriptMap; 1.3087 + JS_ASSERT(map); 1.3088 + DebugScriptMap::Ptr p = map->lookup(this); 1.3089 + JS_ASSERT(p); 1.3090 + return p->value(); 1.3091 +} 1.3092 + 1.3093 +DebugScript * 1.3094 +JSScript::releaseDebugScript() 1.3095 +{ 1.3096 + JS_ASSERT(hasDebugScript_); 1.3097 + DebugScriptMap *map = compartment()->debugScriptMap; 1.3098 + JS_ASSERT(map); 1.3099 + DebugScriptMap::Ptr p = map->lookup(this); 1.3100 + JS_ASSERT(p); 1.3101 + DebugScript *debug = p->value(); 1.3102 + map->remove(p); 1.3103 + hasDebugScript_ = false; 1.3104 + return debug; 1.3105 +} 1.3106 + 1.3107 +void 1.3108 +JSScript::destroyDebugScript(FreeOp *fop) 1.3109 +{ 1.3110 + if (hasDebugScript_) { 1.3111 + for (jsbytecode *pc = code(); pc < codeEnd(); pc++) { 1.3112 + if (BreakpointSite *site = getBreakpointSite(pc)) { 1.3113 + /* Breakpoints are swept before finalization. */ 1.3114 + JS_ASSERT(site->firstBreakpoint() == nullptr); 1.3115 + site->clearTrap(fop, nullptr, nullptr); 1.3116 + JS_ASSERT(getBreakpointSite(pc) == nullptr); 1.3117 + } 1.3118 + } 1.3119 + fop->free_(releaseDebugScript()); 1.3120 + } 1.3121 +} 1.3122 + 1.3123 +bool 1.3124 +JSScript::ensureHasDebugScript(JSContext *cx) 1.3125 +{ 1.3126 + if (hasDebugScript_) 1.3127 + return true; 1.3128 + 1.3129 + size_t nbytes = offsetof(DebugScript, breakpoints) + length() * sizeof(BreakpointSite*); 1.3130 + DebugScript *debug = (DebugScript *) cx->calloc_(nbytes); 1.3131 + if (!debug) 1.3132 + return false; 1.3133 + 1.3134 + /* Create compartment's debugScriptMap if necessary. */ 1.3135 + DebugScriptMap *map = compartment()->debugScriptMap; 1.3136 + if (!map) { 1.3137 + map = cx->new_<DebugScriptMap>(); 1.3138 + if (!map || !map->init()) { 1.3139 + js_free(debug); 1.3140 + js_delete(map); 1.3141 + return false; 1.3142 + } 1.3143 + compartment()->debugScriptMap = map; 1.3144 + } 1.3145 + 1.3146 + if (!map->putNew(this, debug)) { 1.3147 + js_free(debug); 1.3148 + return false; 1.3149 + } 1.3150 + hasDebugScript_ = true; // safe to set this; we can't fail after this point 1.3151 + 1.3152 + /* 1.3153 + * Ensure that any Interpret() instances running on this script have 1.3154 + * interrupts enabled. The interrupts must stay enabled until the 1.3155 + * debug state is destroyed. 1.3156 + */ 1.3157 + for (ActivationIterator iter(cx->runtime()); !iter.done(); ++iter) { 1.3158 + if (iter->isInterpreter()) 1.3159 + iter->asInterpreter()->enableInterruptsIfRunning(this); 1.3160 + } 1.3161 + 1.3162 + return true; 1.3163 +} 1.3164 + 1.3165 +void 1.3166 +JSScript::setNewStepMode(FreeOp *fop, uint32_t newValue) 1.3167 +{ 1.3168 + DebugScript *debug = debugScript(); 1.3169 + uint32_t prior = debug->stepMode; 1.3170 + debug->stepMode = newValue; 1.3171 + 1.3172 + if (!prior != !newValue) { 1.3173 +#ifdef JS_ION 1.3174 + if (hasBaselineScript()) 1.3175 + baseline->toggleDebugTraps(this, nullptr); 1.3176 +#endif 1.3177 + 1.3178 + if (!stepModeEnabled() && !debug->numSites) 1.3179 + fop->free_(releaseDebugScript()); 1.3180 + } 1.3181 +} 1.3182 + 1.3183 +bool 1.3184 +JSScript::setStepModeFlag(JSContext *cx, bool step) 1.3185 +{ 1.3186 + if (!ensureHasDebugScript(cx)) 1.3187 + return false; 1.3188 + 1.3189 + setNewStepMode(cx->runtime()->defaultFreeOp(), 1.3190 + (debugScript()->stepMode & stepCountMask) | 1.3191 + (step ? stepFlagMask : 0)); 1.3192 + return true; 1.3193 +} 1.3194 + 1.3195 +bool 1.3196 +JSScript::incrementStepModeCount(JSContext *cx) 1.3197 +{ 1.3198 + assertSameCompartment(cx, this); 1.3199 + MOZ_ASSERT(cx->compartment()->debugMode()); 1.3200 + 1.3201 + if (!ensureHasDebugScript(cx)) 1.3202 + return false; 1.3203 + 1.3204 + DebugScript *debug = debugScript(); 1.3205 + uint32_t count = debug->stepMode & stepCountMask; 1.3206 + MOZ_ASSERT(((count + 1) & stepCountMask) == count + 1); 1.3207 + 1.3208 + setNewStepMode(cx->runtime()->defaultFreeOp(), 1.3209 + (debug->stepMode & stepFlagMask) | 1.3210 + ((count + 1) & stepCountMask)); 1.3211 + return true; 1.3212 +} 1.3213 + 1.3214 +void 1.3215 +JSScript::decrementStepModeCount(FreeOp *fop) 1.3216 +{ 1.3217 + DebugScript *debug = debugScript(); 1.3218 + uint32_t count = debug->stepMode & stepCountMask; 1.3219 + 1.3220 + setNewStepMode(fop, 1.3221 + (debug->stepMode & stepFlagMask) | 1.3222 + ((count - 1) & stepCountMask)); 1.3223 +} 1.3224 + 1.3225 +BreakpointSite * 1.3226 +JSScript::getOrCreateBreakpointSite(JSContext *cx, jsbytecode *pc) 1.3227 +{ 1.3228 + if (!ensureHasDebugScript(cx)) 1.3229 + return nullptr; 1.3230 + 1.3231 + DebugScript *debug = debugScript(); 1.3232 + BreakpointSite *&site = debug->breakpoints[pcToOffset(pc)]; 1.3233 + 1.3234 + if (!site) { 1.3235 + site = cx->runtime()->new_<BreakpointSite>(this, pc); 1.3236 + if (!site) { 1.3237 + js_ReportOutOfMemory(cx); 1.3238 + return nullptr; 1.3239 + } 1.3240 + debug->numSites++; 1.3241 + } 1.3242 + 1.3243 + return site; 1.3244 +} 1.3245 + 1.3246 +void 1.3247 +JSScript::destroyBreakpointSite(FreeOp *fop, jsbytecode *pc) 1.3248 +{ 1.3249 + DebugScript *debug = debugScript(); 1.3250 + BreakpointSite *&site = debug->breakpoints[pcToOffset(pc)]; 1.3251 + JS_ASSERT(site); 1.3252 + 1.3253 + fop->delete_(site); 1.3254 + site = nullptr; 1.3255 + 1.3256 + if (--debug->numSites == 0 && !stepModeEnabled()) 1.3257 + fop->free_(releaseDebugScript()); 1.3258 +} 1.3259 + 1.3260 +void 1.3261 +JSScript::clearBreakpointsIn(FreeOp *fop, js::Debugger *dbg, JSObject *handler) 1.3262 +{ 1.3263 + if (!hasAnyBreakpointsOrStepMode()) 1.3264 + return; 1.3265 + 1.3266 + for (jsbytecode *pc = code(); pc < codeEnd(); pc++) { 1.3267 + BreakpointSite *site = getBreakpointSite(pc); 1.3268 + if (site) { 1.3269 + Breakpoint *nextbp; 1.3270 + for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) { 1.3271 + nextbp = bp->nextInSite(); 1.3272 + if ((!dbg || bp->debugger == dbg) && (!handler || bp->getHandler() == handler)) 1.3273 + bp->destroy(fop); 1.3274 + } 1.3275 + } 1.3276 + } 1.3277 +} 1.3278 + 1.3279 +bool 1.3280 +JSScript::hasBreakpointsAt(jsbytecode *pc) 1.3281 +{ 1.3282 + BreakpointSite *site = getBreakpointSite(pc); 1.3283 + if (!site) 1.3284 + return false; 1.3285 + 1.3286 + return site->enabledCount > 0 || site->trapHandler; 1.3287 +} 1.3288 + 1.3289 +void 1.3290 +JSScript::clearTraps(FreeOp *fop) 1.3291 +{ 1.3292 + if (!hasAnyBreakpointsOrStepMode()) 1.3293 + return; 1.3294 + 1.3295 + for (jsbytecode *pc = code(); pc < codeEnd(); pc++) { 1.3296 + BreakpointSite *site = getBreakpointSite(pc); 1.3297 + if (site) 1.3298 + site->clearTrap(fop); 1.3299 + } 1.3300 +} 1.3301 + 1.3302 +void 1.3303 +JSScript::markChildren(JSTracer *trc) 1.3304 +{ 1.3305 + // NOTE: this JSScript may be partially initialized at this point. E.g. we 1.3306 + // may have created it and partially initialized it with 1.3307 + // JSScript::Create(), but not yet finished initializing it with 1.3308 + // fullyInitFromEmitter() or fullyInitTrivial(). 1.3309 + 1.3310 + JS_ASSERT_IF(trc->runtime()->gcStrictCompartmentChecking, zone()->isCollecting()); 1.3311 + 1.3312 + for (uint32_t i = 0; i < natoms(); ++i) { 1.3313 + if (atoms[i]) 1.3314 + MarkString(trc, &atoms[i], "atom"); 1.3315 + } 1.3316 + 1.3317 + if (hasObjects()) { 1.3318 + ObjectArray *objarray = objects(); 1.3319 + MarkObjectRange(trc, objarray->length, objarray->vector, "objects"); 1.3320 + } 1.3321 + 1.3322 + if (hasRegexps()) { 1.3323 + ObjectArray *objarray = regexps(); 1.3324 + MarkObjectRange(trc, objarray->length, objarray->vector, "objects"); 1.3325 + } 1.3326 + 1.3327 + if (hasConsts()) { 1.3328 + ConstArray *constarray = consts(); 1.3329 + MarkValueRange(trc, constarray->length, constarray->vector, "consts"); 1.3330 + } 1.3331 + 1.3332 + if (sourceObject()) { 1.3333 + JS_ASSERT(sourceObject()->compartment() == compartment()); 1.3334 + MarkObject(trc, &sourceObject_, "sourceObject"); 1.3335 + } 1.3336 + 1.3337 + if (functionNonDelazifying()) 1.3338 + MarkObject(trc, &function_, "function"); 1.3339 + 1.3340 + if (enclosingScopeOrOriginalFunction_) 1.3341 + MarkObject(trc, &enclosingScopeOrOriginalFunction_, "enclosing"); 1.3342 + 1.3343 + if (maybeLazyScript()) 1.3344 + MarkLazyScriptUnbarriered(trc, &lazyScript, "lazyScript"); 1.3345 + 1.3346 + if (IS_GC_MARKING_TRACER(trc)) { 1.3347 + compartment()->mark(); 1.3348 + 1.3349 + if (code()) 1.3350 + MarkScriptData(trc->runtime(), code()); 1.3351 + } 1.3352 + 1.3353 + bindings.trace(trc); 1.3354 + 1.3355 + if (hasAnyBreakpointsOrStepMode()) { 1.3356 + for (unsigned i = 0; i < length(); i++) { 1.3357 + BreakpointSite *site = debugScript()->breakpoints[i]; 1.3358 + if (site && site->trapHandler) 1.3359 + MarkValue(trc, &site->trapClosure, "trap closure"); 1.3360 + } 1.3361 + } 1.3362 + 1.3363 +#ifdef JS_ION 1.3364 + jit::TraceIonScripts(trc, this); 1.3365 +#endif 1.3366 +} 1.3367 + 1.3368 +void 1.3369 +LazyScript::markChildren(JSTracer *trc) 1.3370 +{ 1.3371 + if (function_) 1.3372 + MarkObject(trc, &function_, "function"); 1.3373 + 1.3374 + if (sourceObject_) 1.3375 + MarkObject(trc, &sourceObject_, "sourceObject"); 1.3376 + 1.3377 + if (enclosingScope_) 1.3378 + MarkObject(trc, &enclosingScope_, "enclosingScope"); 1.3379 + 1.3380 + if (script_) 1.3381 + MarkScript(trc, &script_, "realScript"); 1.3382 + 1.3383 + HeapPtrAtom *freeVariables = this->freeVariables(); 1.3384 + for (size_t i = 0; i < numFreeVariables(); i++) 1.3385 + MarkString(trc, &freeVariables[i], "lazyScriptFreeVariable"); 1.3386 + 1.3387 + HeapPtrFunction *innerFunctions = this->innerFunctions(); 1.3388 + for (size_t i = 0; i < numInnerFunctions(); i++) 1.3389 + MarkObject(trc, &innerFunctions[i], "lazyScriptInnerFunction"); 1.3390 +} 1.3391 + 1.3392 +void 1.3393 +LazyScript::finalize(FreeOp *fop) 1.3394 +{ 1.3395 + if (table_) 1.3396 + fop->free_(table_); 1.3397 +} 1.3398 + 1.3399 +NestedScopeObject * 1.3400 +JSScript::getStaticScope(jsbytecode *pc) 1.3401 +{ 1.3402 + JS_ASSERT(containsPC(pc)); 1.3403 + 1.3404 + if (!hasBlockScopes()) 1.3405 + return nullptr; 1.3406 + 1.3407 + ptrdiff_t offset = pc - main(); 1.3408 + 1.3409 + if (offset < 0) 1.3410 + return nullptr; 1.3411 + 1.3412 + BlockScopeArray *scopes = blockScopes(); 1.3413 + NestedScopeObject *blockChain = nullptr; 1.3414 + 1.3415 + // Find the innermost block chain using a binary search. 1.3416 + size_t bottom = 0; 1.3417 + size_t top = scopes->length; 1.3418 + 1.3419 + while (bottom < top) { 1.3420 + size_t mid = bottom + (top - bottom) / 2; 1.3421 + const BlockScopeNote *note = &scopes->vector[mid]; 1.3422 + if (note->start <= offset) { 1.3423 + // Block scopes are ordered in the list by their starting offset, and since 1.3424 + // blocks form a tree ones earlier in the list may cover the pc even if 1.3425 + // later blocks end before the pc. This only happens when the earlier block 1.3426 + // is a parent of the later block, so we need to check parents of |mid| in 1.3427 + // the searched range for coverage. 1.3428 + size_t check = mid; 1.3429 + while (check >= bottom) { 1.3430 + const BlockScopeNote *checkNote = &scopes->vector[check]; 1.3431 + JS_ASSERT(checkNote->start <= offset); 1.3432 + if (offset < checkNote->start + checkNote->length) { 1.3433 + // We found a matching block chain but there may be inner ones 1.3434 + // at a higher block chain index than mid. Continue the binary search. 1.3435 + if (checkNote->index == BlockScopeNote::NoBlockScopeIndex) 1.3436 + blockChain = nullptr; 1.3437 + else 1.3438 + blockChain = &getObject(checkNote->index)->as<NestedScopeObject>(); 1.3439 + break; 1.3440 + } 1.3441 + if (checkNote->parent == UINT32_MAX) 1.3442 + break; 1.3443 + check = checkNote->parent; 1.3444 + } 1.3445 + bottom = mid + 1; 1.3446 + } else { 1.3447 + top = mid; 1.3448 + } 1.3449 + } 1.3450 + 1.3451 + return blockChain; 1.3452 +} 1.3453 + 1.3454 +void 1.3455 +JSScript::setArgumentsHasVarBinding() 1.3456 +{ 1.3457 + argsHasVarBinding_ = true; 1.3458 +#ifdef JS_ION 1.3459 + needsArgsAnalysis_ = true; 1.3460 +#else 1.3461 + // The arguments analysis is performed by IonBuilder. 1.3462 + needsArgsObj_ = true; 1.3463 +#endif 1.3464 +} 1.3465 + 1.3466 +void 1.3467 +JSScript::setNeedsArgsObj(bool needsArgsObj) 1.3468 +{ 1.3469 + JS_ASSERT_IF(needsArgsObj, argumentsHasVarBinding()); 1.3470 + needsArgsAnalysis_ = false; 1.3471 + needsArgsObj_ = needsArgsObj; 1.3472 +} 1.3473 + 1.3474 +void 1.3475 +js::SetFrameArgumentsObject(JSContext *cx, AbstractFramePtr frame, 1.3476 + HandleScript script, JSObject *argsobj) 1.3477 +{ 1.3478 + /* 1.3479 + * Replace any optimized arguments in the frame with an explicit arguments 1.3480 + * object. Note that 'arguments' may have already been overwritten. 1.3481 + */ 1.3482 + 1.3483 + InternalBindingsHandle bindings(script, &script->bindings); 1.3484 + const uint32_t var = Bindings::argumentsVarIndex(cx, bindings); 1.3485 + 1.3486 + if (script->varIsAliased(var)) { 1.3487 + /* 1.3488 + * Scan the script to find the slot in the call object that 'arguments' 1.3489 + * is assigned to. 1.3490 + */ 1.3491 + jsbytecode *pc = script->code(); 1.3492 + while (*pc != JSOP_ARGUMENTS) 1.3493 + pc += GetBytecodeLength(pc); 1.3494 + pc += JSOP_ARGUMENTS_LENGTH; 1.3495 + JS_ASSERT(*pc == JSOP_SETALIASEDVAR); 1.3496 + 1.3497 + // Note that here and below, it is insufficient to only check for 1.3498 + // JS_OPTIMIZED_ARGUMENTS, as Ion could have optimized out the 1.3499 + // arguments slot. 1.3500 + if (IsOptimizedPlaceholderMagicValue(frame.callObj().as<ScopeObject>().aliasedVar(pc))) 1.3501 + frame.callObj().as<ScopeObject>().setAliasedVar(cx, pc, cx->names().arguments, ObjectValue(*argsobj)); 1.3502 + } else { 1.3503 + if (IsOptimizedPlaceholderMagicValue(frame.unaliasedLocal(var))) 1.3504 + frame.unaliasedLocal(var) = ObjectValue(*argsobj); 1.3505 + } 1.3506 +} 1.3507 + 1.3508 +/* static */ bool 1.3509 +JSScript::argumentsOptimizationFailed(JSContext *cx, HandleScript script) 1.3510 +{ 1.3511 + JS_ASSERT(script->functionNonDelazifying()); 1.3512 + JS_ASSERT(script->analyzedArgsUsage()); 1.3513 + JS_ASSERT(script->argumentsHasVarBinding()); 1.3514 + 1.3515 + /* 1.3516 + * It is possible that the arguments optimization has already failed, 1.3517 + * everything has been fixed up, but there was an outstanding magic value 1.3518 + * on the stack that has just now flowed into an apply. In this case, there 1.3519 + * is nothing to do; GuardFunApplySpeculation will patch in the real 1.3520 + * argsobj. 1.3521 + */ 1.3522 + if (script->needsArgsObj()) 1.3523 + return true; 1.3524 + 1.3525 + JS_ASSERT(!script->isGenerator()); 1.3526 + 1.3527 + script->needsArgsObj_ = true; 1.3528 + 1.3529 +#ifdef JS_ION 1.3530 + /* 1.3531 + * Since we can't invalidate baseline scripts, set a flag that's checked from 1.3532 + * JIT code to indicate the arguments optimization failed and JSOP_ARGUMENTS 1.3533 + * should create an arguments object next time. 1.3534 + */ 1.3535 + if (script->hasBaselineScript()) 1.3536 + script->baselineScript()->setNeedsArgsObj(); 1.3537 +#endif 1.3538 + 1.3539 + /* 1.3540 + * By design, the arguments optimization is only made when there are no 1.3541 + * outstanding cases of MagicValue(JS_OPTIMIZED_ARGUMENTS) at any points 1.3542 + * where the optimization could fail, other than an active invocation of 1.3543 + * 'f.apply(x, arguments)'. Thus, there are no outstanding values of 1.3544 + * MagicValue(JS_OPTIMIZED_ARGUMENTS) on the stack. However, there are 1.3545 + * three things that need fixup: 1.3546 + * - there may be any number of activations of this script that don't have 1.3547 + * an argsObj that now need one. 1.3548 + * - jit code compiled (and possible active on the stack) with the static 1.3549 + * assumption of !script->needsArgsObj(); 1.3550 + * - type inference data for the script assuming script->needsArgsObj 1.3551 + */ 1.3552 + for (AllFramesIter i(cx); !i.done(); ++i) { 1.3553 + /* 1.3554 + * We cannot reliably create an arguments object for Ion activations of 1.3555 + * this script. To maintain the invariant that "script->needsArgsObj 1.3556 + * implies fp->hasArgsObj", the Ion bail mechanism will create an 1.3557 + * arguments object right after restoring the BaselineFrame and before 1.3558 + * entering Baseline code (in jit::FinishBailoutToBaseline). 1.3559 + */ 1.3560 + if (i.isIon()) 1.3561 + continue; 1.3562 + AbstractFramePtr frame = i.abstractFramePtr(); 1.3563 + if (frame.isFunctionFrame() && frame.script() == script) { 1.3564 + ArgumentsObject *argsobj = ArgumentsObject::createExpected(cx, frame); 1.3565 + if (!argsobj) { 1.3566 + /* 1.3567 + * We can't leave stack frames with script->needsArgsObj but no 1.3568 + * arguments object. It is, however, safe to leave frames with 1.3569 + * an arguments object but !script->needsArgsObj. 1.3570 + */ 1.3571 + script->needsArgsObj_ = false; 1.3572 + return false; 1.3573 + } 1.3574 + 1.3575 + SetFrameArgumentsObject(cx, frame, script, argsobj); 1.3576 + } 1.3577 + } 1.3578 + 1.3579 + return true; 1.3580 +} 1.3581 + 1.3582 +bool 1.3583 +JSScript::varIsAliased(uint32_t varSlot) 1.3584 +{ 1.3585 + return bindings.bindingIsAliased(bindings.numArgs() + varSlot); 1.3586 +} 1.3587 + 1.3588 +bool 1.3589 +JSScript::formalIsAliased(unsigned argSlot) 1.3590 +{ 1.3591 + return bindings.bindingIsAliased(argSlot); 1.3592 +} 1.3593 + 1.3594 +bool 1.3595 +JSScript::formalLivesInArgumentsObject(unsigned argSlot) 1.3596 +{ 1.3597 + return argsObjAliasesFormals() && !formalIsAliased(argSlot); 1.3598 +} 1.3599 + 1.3600 +LazyScript::LazyScript(JSFunction *fun, void *table, uint64_t packedFields, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column) 1.3601 + : script_(nullptr), 1.3602 + function_(fun), 1.3603 + enclosingScope_(nullptr), 1.3604 + sourceObject_(nullptr), 1.3605 + table_(table), 1.3606 + packedFields_(packedFields), 1.3607 + begin_(begin), 1.3608 + end_(end), 1.3609 + lineno_(lineno), 1.3610 + column_(column) 1.3611 +{ 1.3612 + JS_ASSERT(begin <= end); 1.3613 +} 1.3614 + 1.3615 +void 1.3616 +LazyScript::initScript(JSScript *script) 1.3617 +{ 1.3618 + JS_ASSERT(script && !script_); 1.3619 + script_ = script; 1.3620 +} 1.3621 + 1.3622 +void 1.3623 +LazyScript::resetScript() 1.3624 +{ 1.3625 + JS_ASSERT(script_); 1.3626 + script_ = nullptr; 1.3627 +} 1.3628 + 1.3629 +void 1.3630 +LazyScript::setParent(JSObject *enclosingScope, ScriptSourceObject *sourceObject) 1.3631 +{ 1.3632 + JS_ASSERT(!sourceObject_ && !enclosingScope_); 1.3633 + JS_ASSERT_IF(enclosingScope, function_->compartment() == enclosingScope->compartment()); 1.3634 + JS_ASSERT(function_->compartment() == sourceObject->compartment()); 1.3635 + 1.3636 + enclosingScope_ = enclosingScope; 1.3637 + sourceObject_ = sourceObject; 1.3638 +} 1.3639 + 1.3640 +ScriptSourceObject * 1.3641 +LazyScript::sourceObject() const 1.3642 +{ 1.3643 + return sourceObject_ ? &sourceObject_->as<ScriptSourceObject>() : nullptr; 1.3644 +} 1.3645 + 1.3646 +/* static */ LazyScript * 1.3647 +LazyScript::CreateRaw(ExclusiveContext *cx, HandleFunction fun, 1.3648 + uint64_t packedFields, uint32_t begin, uint32_t end, 1.3649 + uint32_t lineno, uint32_t column) 1.3650 +{ 1.3651 + union { 1.3652 + PackedView p; 1.3653 + uint64_t packed; 1.3654 + }; 1.3655 + 1.3656 + packed = packedFields; 1.3657 + 1.3658 + // Reset runtime flags to obtain a fresh LazyScript. 1.3659 + p.hasBeenCloned = false; 1.3660 + p.treatAsRunOnce = false; 1.3661 + 1.3662 + size_t bytes = (p.numFreeVariables * sizeof(HeapPtrAtom)) 1.3663 + + (p.numInnerFunctions * sizeof(HeapPtrFunction)); 1.3664 + 1.3665 + ScopedJSFreePtr<void> table(bytes ? cx->malloc_(bytes) : nullptr); 1.3666 + if (bytes && !table) 1.3667 + return nullptr; 1.3668 + 1.3669 + LazyScript *res = js_NewGCLazyScript(cx); 1.3670 + if (!res) 1.3671 + return nullptr; 1.3672 + 1.3673 + cx->compartment()->scheduleDelazificationForDebugMode(); 1.3674 + 1.3675 + return new (res) LazyScript(fun, table.forget(), packed, begin, end, lineno, column); 1.3676 +} 1.3677 + 1.3678 +/* static */ LazyScript * 1.3679 +LazyScript::CreateRaw(ExclusiveContext *cx, HandleFunction fun, 1.3680 + uint32_t numFreeVariables, uint32_t numInnerFunctions, JSVersion version, 1.3681 + uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column) 1.3682 +{ 1.3683 + union { 1.3684 + PackedView p; 1.3685 + uint64_t packedFields; 1.3686 + }; 1.3687 + 1.3688 + p.version = version; 1.3689 + p.numFreeVariables = numFreeVariables; 1.3690 + p.numInnerFunctions = numInnerFunctions; 1.3691 + p.generatorKindBits = GeneratorKindAsBits(NotGenerator); 1.3692 + p.strict = false; 1.3693 + p.bindingsAccessedDynamically = false; 1.3694 + p.hasDebuggerStatement = false; 1.3695 + p.directlyInsideEval = false; 1.3696 + p.usesArgumentsAndApply = false; 1.3697 + 1.3698 + LazyScript *res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column); 1.3699 + JS_ASSERT_IF(res, res->version() == version); 1.3700 + return res; 1.3701 +} 1.3702 + 1.3703 +/* static */ LazyScript * 1.3704 +LazyScript::Create(ExclusiveContext *cx, HandleFunction fun, 1.3705 + uint64_t packedFields, uint32_t begin, uint32_t end, 1.3706 + uint32_t lineno, uint32_t column) 1.3707 +{ 1.3708 + // Dummy atom which is not a valid property name. 1.3709 + RootedAtom dummyAtom(cx, cx->names().comma); 1.3710 + 1.3711 + // Dummy function which is not a valid function as this is the one which is 1.3712 + // holding this lazy script. 1.3713 + HandleFunction dummyFun = fun; 1.3714 + 1.3715 + LazyScript *res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column); 1.3716 + if (!res) 1.3717 + return nullptr; 1.3718 + 1.3719 + // Fill with dummies, to be GC-safe after the initialization of the free 1.3720 + // variables and inner functions. 1.3721 + size_t i, num; 1.3722 + HeapPtrAtom *variables = res->freeVariables(); 1.3723 + for (i = 0, num = res->numFreeVariables(); i < num; i++) 1.3724 + variables[i].init(dummyAtom); 1.3725 + 1.3726 + HeapPtrFunction *functions = res->innerFunctions(); 1.3727 + for (i = 0, num = res->numInnerFunctions(); i < num; i++) 1.3728 + functions[i].init(dummyFun); 1.3729 + 1.3730 + return res; 1.3731 +} 1.3732 + 1.3733 +void 1.3734 +LazyScript::initRuntimeFields(uint64_t packedFields) 1.3735 +{ 1.3736 + union { 1.3737 + PackedView p; 1.3738 + uint64_t packed; 1.3739 + }; 1.3740 + 1.3741 + packed = packedFields; 1.3742 + p_.hasBeenCloned = p.hasBeenCloned; 1.3743 + p_.treatAsRunOnce = p.treatAsRunOnce; 1.3744 +} 1.3745 + 1.3746 +bool 1.3747 +LazyScript::hasUncompiledEnclosingScript() const 1.3748 +{ 1.3749 + // It can happen that we created lazy scripts while compiling an enclosing 1.3750 + // script, but we errored out while compiling that script. When we iterate 1.3751 + // over lazy script in a compartment, we might see lazy scripts that never 1.3752 + // escaped to script and should be ignored. 1.3753 + // 1.3754 + // If the enclosing scope is a function with a null script or has a script 1.3755 + // without code, it was not successfully compiled. 1.3756 + 1.3757 + if (!enclosingScope() || !enclosingScope()->is<JSFunction>()) 1.3758 + return false; 1.3759 + 1.3760 + JSFunction &fun = enclosingScope()->as<JSFunction>(); 1.3761 + return fun.isInterpreted() && (!fun.mutableScript() || !fun.nonLazyScript()->code()); 1.3762 +} 1.3763 + 1.3764 +uint32_t 1.3765 +LazyScript::staticLevel(JSContext *cx) const 1.3766 +{ 1.3767 + for (StaticScopeIter<NoGC> ssi(enclosingScope()); !ssi.done(); ssi++) { 1.3768 + if (ssi.type() == StaticScopeIter<NoGC>::FUNCTION) 1.3769 + return ssi.funScript()->staticLevel() + 1; 1.3770 + } 1.3771 + return 1; 1.3772 +} 1.3773 + 1.3774 +void 1.3775 +JSScript::updateBaselineOrIonRaw() 1.3776 +{ 1.3777 +#ifdef JS_ION 1.3778 + if (hasIonScript()) { 1.3779 + baselineOrIonRaw = ion->method()->raw(); 1.3780 + baselineOrIonSkipArgCheck = ion->method()->raw() + ion->getSkipArgCheckEntryOffset(); 1.3781 + } else if (hasBaselineScript()) { 1.3782 + baselineOrIonRaw = baseline->method()->raw(); 1.3783 + baselineOrIonSkipArgCheck = baseline->method()->raw(); 1.3784 + } else { 1.3785 + baselineOrIonRaw = nullptr; 1.3786 + baselineOrIonSkipArgCheck = nullptr; 1.3787 + } 1.3788 +#endif 1.3789 +} 1.3790 + 1.3791 +bool 1.3792 +JSScript::hasLoops() 1.3793 +{ 1.3794 + if (!hasTrynotes()) 1.3795 + return false; 1.3796 + JSTryNote *tn = trynotes()->vector; 1.3797 + JSTryNote *tnlimit = tn + trynotes()->length; 1.3798 + for (; tn < tnlimit; tn++) { 1.3799 + if (tn->kind == JSTRY_ITER || tn->kind == JSTRY_LOOP) 1.3800 + return true; 1.3801 + } 1.3802 + return false; 1.3803 +} 1.3804 + 1.3805 +static inline void 1.3806 +LazyScriptHash(uint32_t lineno, uint32_t column, uint32_t begin, uint32_t end, 1.3807 + HashNumber hashes[3]) 1.3808 +{ 1.3809 + HashNumber hash = lineno; 1.3810 + hash = RotateLeft(hash, 4) ^ column; 1.3811 + hash = RotateLeft(hash, 4) ^ begin; 1.3812 + hash = RotateLeft(hash, 4) ^ end; 1.3813 + 1.3814 + hashes[0] = hash; 1.3815 + hashes[1] = RotateLeft(hashes[0], 4) ^ begin; 1.3816 + hashes[2] = RotateLeft(hashes[1], 4) ^ end; 1.3817 +} 1.3818 + 1.3819 +void 1.3820 +LazyScriptHashPolicy::hash(const Lookup &lookup, HashNumber hashes[3]) 1.3821 +{ 1.3822 + LazyScript *lazy = lookup.lazy; 1.3823 + LazyScriptHash(lazy->lineno(), lazy->column(), lazy->begin(), lazy->end(), hashes); 1.3824 +} 1.3825 + 1.3826 +void 1.3827 +LazyScriptHashPolicy::hash(JSScript *script, HashNumber hashes[3]) 1.3828 +{ 1.3829 + LazyScriptHash(script->lineno(), script->column(), script->sourceStart(), script->sourceEnd(), hashes); 1.3830 +} 1.3831 + 1.3832 +bool 1.3833 +LazyScriptHashPolicy::match(JSScript *script, const Lookup &lookup) 1.3834 +{ 1.3835 + JSContext *cx = lookup.cx; 1.3836 + LazyScript *lazy = lookup.lazy; 1.3837 + 1.3838 + // To be a match, the script and lazy script need to have the same line 1.3839 + // and column and to be at the same position within their respective 1.3840 + // source blobs, and to have the same source contents and version. 1.3841 + // 1.3842 + // While the surrounding code in the source may differ, this is 1.3843 + // sufficient to ensure that compiling the lazy script will yield an 1.3844 + // identical result to compiling the original script. 1.3845 + // 1.3846 + // Note that the filenames and origin principals of the lazy script and 1.3847 + // original script can differ. If there is a match, these will be fixed 1.3848 + // up in the resulting clone by the caller. 1.3849 + 1.3850 + if (script->lineno() != lazy->lineno() || 1.3851 + script->column() != lazy->column() || 1.3852 + script->getVersion() != lazy->version() || 1.3853 + script->sourceStart() != lazy->begin() || 1.3854 + script->sourceEnd() != lazy->end()) 1.3855 + { 1.3856 + return false; 1.3857 + } 1.3858 + 1.3859 + SourceDataCache::AutoHoldEntry holder; 1.3860 + 1.3861 + const jschar *scriptChars = script->scriptSource()->chars(cx, holder); 1.3862 + if (!scriptChars) 1.3863 + return false; 1.3864 + 1.3865 + const jschar *lazyChars = lazy->source()->chars(cx, holder); 1.3866 + if (!lazyChars) 1.3867 + return false; 1.3868 + 1.3869 + size_t begin = script->sourceStart(); 1.3870 + size_t length = script->sourceEnd() - begin; 1.3871 + return !memcmp(scriptChars + begin, lazyChars + begin, length); 1.3872 +}