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