js/src/jsscript.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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(&note->index) ||
michael@0 996 !xdr->codeUint32(&note->start) ||
michael@0 997 !xdr->codeUint32(&note->length) ||
michael@0 998 !xdr->codeUint32(&note->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 }

mercurial