js/src/jsinferinlines.h

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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 /* Inline members for javascript type inference. */
michael@0 8
michael@0 9 #ifndef jsinferinlines_h
michael@0 10 #define jsinferinlines_h
michael@0 11
michael@0 12 #include "jsinfer.h"
michael@0 13
michael@0 14 #include "mozilla/PodOperations.h"
michael@0 15
michael@0 16 #include "jsanalyze.h"
michael@0 17
michael@0 18 #include "vm/ArrayObject.h"
michael@0 19 #include "vm/BooleanObject.h"
michael@0 20 #include "vm/NumberObject.h"
michael@0 21 #include "vm/SharedArrayObject.h"
michael@0 22 #include "vm/StringObject.h"
michael@0 23 #include "vm/TypedArrayObject.h"
michael@0 24
michael@0 25 #include "jscntxtinlines.h"
michael@0 26
michael@0 27 #include "jit/ExecutionMode-inl.h"
michael@0 28
michael@0 29 namespace js {
michael@0 30 namespace types {
michael@0 31
michael@0 32 /////////////////////////////////////////////////////////////////////
michael@0 33 // CompilerOutput & RecompileInfo
michael@0 34 /////////////////////////////////////////////////////////////////////
michael@0 35
michael@0 36 inline jit::IonScript *
michael@0 37 CompilerOutput::ion() const
michael@0 38 {
michael@0 39 #ifdef JS_ION
michael@0 40 // Note: If type constraints are generated before compilation has finished
michael@0 41 // (i.e. after IonBuilder but before CodeGenerator::link) then a valid
michael@0 42 // CompilerOutput may not yet have an associated IonScript.
michael@0 43 JS_ASSERT(isValid());
michael@0 44 jit::IonScript *ion = jit::GetIonScript(script(), mode());
michael@0 45 JS_ASSERT(ion != ION_COMPILING_SCRIPT);
michael@0 46 return ion;
michael@0 47 #endif
michael@0 48 MOZ_ASSUME_UNREACHABLE("Invalid kind of CompilerOutput");
michael@0 49 }
michael@0 50
michael@0 51 inline CompilerOutput*
michael@0 52 RecompileInfo::compilerOutput(TypeZone &types) const
michael@0 53 {
michael@0 54 if (!types.compilerOutputs || outputIndex >= types.compilerOutputs->length())
michael@0 55 return nullptr;
michael@0 56 return &(*types.compilerOutputs)[outputIndex];
michael@0 57 }
michael@0 58
michael@0 59 inline CompilerOutput*
michael@0 60 RecompileInfo::compilerOutput(JSContext *cx) const
michael@0 61 {
michael@0 62 return compilerOutput(cx->zone()->types);
michael@0 63 }
michael@0 64
michael@0 65 inline bool
michael@0 66 RecompileInfo::shouldSweep(TypeZone &types)
michael@0 67 {
michael@0 68 CompilerOutput *output = compilerOutput(types);
michael@0 69 if (!output || !output->isValid())
michael@0 70 return true;
michael@0 71
michael@0 72 // Update this info for the output's new index in the zone's compiler outputs.
michael@0 73 outputIndex = output->sweepIndex();
michael@0 74 return false;
michael@0 75 }
michael@0 76
michael@0 77 /////////////////////////////////////////////////////////////////////
michael@0 78 // Types
michael@0 79 /////////////////////////////////////////////////////////////////////
michael@0 80
michael@0 81 /* static */ inline Type
michael@0 82 Type::ObjectType(JSObject *obj)
michael@0 83 {
michael@0 84 if (obj->hasSingletonType())
michael@0 85 return Type(uintptr_t(obj) | 1);
michael@0 86 return Type(uintptr_t(obj->type()));
michael@0 87 }
michael@0 88
michael@0 89 /* static */ inline Type
michael@0 90 Type::ObjectType(TypeObject *obj)
michael@0 91 {
michael@0 92 if (obj->singleton())
michael@0 93 return Type(uintptr_t(obj->singleton()) | 1);
michael@0 94 return Type(uintptr_t(obj));
michael@0 95 }
michael@0 96
michael@0 97 /* static */ inline Type
michael@0 98 Type::ObjectType(TypeObjectKey *obj)
michael@0 99 {
michael@0 100 return Type(uintptr_t(obj));
michael@0 101 }
michael@0 102
michael@0 103 inline Type
michael@0 104 GetValueType(const Value &val)
michael@0 105 {
michael@0 106 if (val.isDouble())
michael@0 107 return Type::DoubleType();
michael@0 108 if (val.isObject())
michael@0 109 return Type::ObjectType(&val.toObject());
michael@0 110 return Type::PrimitiveType(val.extractNonDoubleType());
michael@0 111 }
michael@0 112
michael@0 113 inline Type
michael@0 114 GetMaybeOptimizedOutValueType(const Value &val)
michael@0 115 {
michael@0 116 if (val.isMagic() && val.whyMagic() == JS_OPTIMIZED_OUT)
michael@0 117 return Type::UnknownType();
michael@0 118 return GetValueType(val);
michael@0 119 }
michael@0 120
michael@0 121 inline TypeFlags
michael@0 122 PrimitiveTypeFlag(JSValueType type)
michael@0 123 {
michael@0 124 switch (type) {
michael@0 125 case JSVAL_TYPE_UNDEFINED:
michael@0 126 return TYPE_FLAG_UNDEFINED;
michael@0 127 case JSVAL_TYPE_NULL:
michael@0 128 return TYPE_FLAG_NULL;
michael@0 129 case JSVAL_TYPE_BOOLEAN:
michael@0 130 return TYPE_FLAG_BOOLEAN;
michael@0 131 case JSVAL_TYPE_INT32:
michael@0 132 return TYPE_FLAG_INT32;
michael@0 133 case JSVAL_TYPE_DOUBLE:
michael@0 134 return TYPE_FLAG_DOUBLE;
michael@0 135 case JSVAL_TYPE_STRING:
michael@0 136 return TYPE_FLAG_STRING;
michael@0 137 case JSVAL_TYPE_MAGIC:
michael@0 138 return TYPE_FLAG_LAZYARGS;
michael@0 139 default:
michael@0 140 MOZ_ASSUME_UNREACHABLE("Bad type");
michael@0 141 }
michael@0 142 }
michael@0 143
michael@0 144 inline JSValueType
michael@0 145 TypeFlagPrimitive(TypeFlags flags)
michael@0 146 {
michael@0 147 switch (flags) {
michael@0 148 case TYPE_FLAG_UNDEFINED:
michael@0 149 return JSVAL_TYPE_UNDEFINED;
michael@0 150 case TYPE_FLAG_NULL:
michael@0 151 return JSVAL_TYPE_NULL;
michael@0 152 case TYPE_FLAG_BOOLEAN:
michael@0 153 return JSVAL_TYPE_BOOLEAN;
michael@0 154 case TYPE_FLAG_INT32:
michael@0 155 return JSVAL_TYPE_INT32;
michael@0 156 case TYPE_FLAG_DOUBLE:
michael@0 157 return JSVAL_TYPE_DOUBLE;
michael@0 158 case TYPE_FLAG_STRING:
michael@0 159 return JSVAL_TYPE_STRING;
michael@0 160 case TYPE_FLAG_LAZYARGS:
michael@0 161 return JSVAL_TYPE_MAGIC;
michael@0 162 default:
michael@0 163 MOZ_ASSUME_UNREACHABLE("Bad type");
michael@0 164 }
michael@0 165 }
michael@0 166
michael@0 167 /*
michael@0 168 * Get the canonical representation of an id to use when doing inference. This
michael@0 169 * maintains the constraint that if two different jsids map to the same property
michael@0 170 * in JS (e.g. 3 and "3"), they have the same type representation.
michael@0 171 */
michael@0 172 inline jsid
michael@0 173 IdToTypeId(jsid id)
michael@0 174 {
michael@0 175 JS_ASSERT(!JSID_IS_EMPTY(id));
michael@0 176
michael@0 177 /*
michael@0 178 * All integers must map to the aggregate property for index types, including
michael@0 179 * negative integers.
michael@0 180 */
michael@0 181 if (JSID_IS_INT(id))
michael@0 182 return JSID_VOID;
michael@0 183
michael@0 184 /*
michael@0 185 * Check for numeric strings, as in js_StringIsIndex, but allow negative
michael@0 186 * and overflowing integers.
michael@0 187 */
michael@0 188 if (JSID_IS_STRING(id)) {
michael@0 189 JSAtom *atom = JSID_TO_ATOM(id);
michael@0 190 JS::TwoByteChars cp = atom->range();
michael@0 191 if (cp.length() > 0 && (JS7_ISDEC(cp[0]) || cp[0] == '-')) {
michael@0 192 for (size_t i = 1; i < cp.length(); ++i) {
michael@0 193 if (!JS7_ISDEC(cp[i]))
michael@0 194 return id;
michael@0 195 }
michael@0 196 return JSID_VOID;
michael@0 197 }
michael@0 198 return id;
michael@0 199 }
michael@0 200
michael@0 201 return JSID_VOID;
michael@0 202 }
michael@0 203
michael@0 204 const char * TypeIdStringImpl(jsid id);
michael@0 205
michael@0 206 /* Convert an id for printing during debug. */
michael@0 207 static inline const char *
michael@0 208 TypeIdString(jsid id)
michael@0 209 {
michael@0 210 #ifdef DEBUG
michael@0 211 return TypeIdStringImpl(id);
michael@0 212 #else
michael@0 213 return "(missing)";
michael@0 214 #endif
michael@0 215 }
michael@0 216
michael@0 217 /*
michael@0 218 * Structure for type inference entry point functions. All functions which can
michael@0 219 * change type information must use this, and functions which depend on
michael@0 220 * intermediate types (i.e. JITs) can use this to ensure that intermediate
michael@0 221 * information is not collected and does not change.
michael@0 222 *
michael@0 223 * Pins inference results so that intermediate type information, TypeObjects
michael@0 224 * and JSScripts won't be collected during GC. Does additional sanity checking
michael@0 225 * that inference is not reentrant and that recompilations occur properly.
michael@0 226 */
michael@0 227 struct AutoEnterAnalysis
michael@0 228 {
michael@0 229 /* Prevent GC activity in the middle of analysis. */
michael@0 230 gc::AutoSuppressGC suppressGC;
michael@0 231
michael@0 232 FreeOp *freeOp;
michael@0 233 JSCompartment *compartment;
michael@0 234 bool oldActiveAnalysis;
michael@0 235
michael@0 236 AutoEnterAnalysis(ExclusiveContext *cx)
michael@0 237 : suppressGC(cx)
michael@0 238 {
michael@0 239 init(cx->defaultFreeOp(), cx->compartment());
michael@0 240 }
michael@0 241
michael@0 242 AutoEnterAnalysis(FreeOp *fop, JSCompartment *comp)
michael@0 243 : suppressGC(comp)
michael@0 244 {
michael@0 245 init(fop, comp);
michael@0 246 }
michael@0 247
michael@0 248 ~AutoEnterAnalysis()
michael@0 249 {
michael@0 250 compartment->activeAnalysis = oldActiveAnalysis;
michael@0 251
michael@0 252 /*
michael@0 253 * If there are no more type inference activations on the stack,
michael@0 254 * process any triggered recompilations. Note that we should not be
michael@0 255 * invoking any scripted code while type inference is running.
michael@0 256 */
michael@0 257 if (!compartment->activeAnalysis) {
michael@0 258 TypeZone &types = compartment->zone()->types;
michael@0 259 if (types.pendingRecompiles)
michael@0 260 types.processPendingRecompiles(freeOp);
michael@0 261 }
michael@0 262 }
michael@0 263
michael@0 264 private:
michael@0 265 void init(FreeOp *fop, JSCompartment *comp) {
michael@0 266 freeOp = fop;
michael@0 267 compartment = comp;
michael@0 268 oldActiveAnalysis = compartment->activeAnalysis;
michael@0 269 compartment->activeAnalysis = true;
michael@0 270 }
michael@0 271 };
michael@0 272
michael@0 273 /////////////////////////////////////////////////////////////////////
michael@0 274 // Interface functions
michael@0 275 /////////////////////////////////////////////////////////////////////
michael@0 276
michael@0 277 inline const Class *
michael@0 278 GetClassForProtoKey(JSProtoKey key)
michael@0 279 {
michael@0 280 switch (key) {
michael@0 281 case JSProto_Object:
michael@0 282 return &JSObject::class_;
michael@0 283 case JSProto_Array:
michael@0 284 return &ArrayObject::class_;
michael@0 285
michael@0 286 case JSProto_Number:
michael@0 287 return &NumberObject::class_;
michael@0 288 case JSProto_Boolean:
michael@0 289 return &BooleanObject::class_;
michael@0 290 case JSProto_String:
michael@0 291 return &StringObject::class_;
michael@0 292 case JSProto_RegExp:
michael@0 293 return &RegExpObject::class_;
michael@0 294
michael@0 295 case JSProto_Int8Array:
michael@0 296 case JSProto_Uint8Array:
michael@0 297 case JSProto_Int16Array:
michael@0 298 case JSProto_Uint16Array:
michael@0 299 case JSProto_Int32Array:
michael@0 300 case JSProto_Uint32Array:
michael@0 301 case JSProto_Float32Array:
michael@0 302 case JSProto_Float64Array:
michael@0 303 case JSProto_Uint8ClampedArray:
michael@0 304 return &TypedArrayObject::classes[key - JSProto_Int8Array];
michael@0 305
michael@0 306 case JSProto_ArrayBuffer:
michael@0 307 return &ArrayBufferObject::class_;
michael@0 308
michael@0 309 case JSProto_SharedArrayBuffer:
michael@0 310 return &SharedArrayBufferObject::class_;
michael@0 311
michael@0 312 case JSProto_DataView:
michael@0 313 return &DataViewObject::class_;
michael@0 314
michael@0 315 default:
michael@0 316 MOZ_ASSUME_UNREACHABLE("Bad proto key");
michael@0 317 }
michael@0 318 }
michael@0 319
michael@0 320 /*
michael@0 321 * Get the default 'new' object for a given standard class, per the currently
michael@0 322 * active global.
michael@0 323 */
michael@0 324 inline TypeObject *
michael@0 325 GetTypeNewObject(JSContext *cx, JSProtoKey key)
michael@0 326 {
michael@0 327 RootedObject proto(cx);
michael@0 328 if (!GetBuiltinPrototype(cx, key, &proto))
michael@0 329 return nullptr;
michael@0 330 return cx->getNewType(GetClassForProtoKey(key), proto.get());
michael@0 331 }
michael@0 332
michael@0 333 /* Get a type object for the immediate allocation site within a native. */
michael@0 334 inline TypeObject *
michael@0 335 GetTypeCallerInitObject(JSContext *cx, JSProtoKey key)
michael@0 336 {
michael@0 337 jsbytecode *pc;
michael@0 338 RootedScript script(cx, cx->currentScript(&pc));
michael@0 339 if (script)
michael@0 340 return TypeScript::InitObject(cx, script, pc, key);
michael@0 341 return GetTypeNewObject(cx, key);
michael@0 342 }
michael@0 343
michael@0 344 void MarkIteratorUnknownSlow(JSContext *cx);
michael@0 345
michael@0 346 void TypeMonitorCallSlow(JSContext *cx, JSObject *callee, const CallArgs &args,
michael@0 347 bool constructing);
michael@0 348
michael@0 349 /*
michael@0 350 * Monitor a javascript call, either on entry to the interpreter or made
michael@0 351 * from within the interpreter.
michael@0 352 */
michael@0 353 inline void
michael@0 354 TypeMonitorCall(JSContext *cx, const js::CallArgs &args, bool constructing)
michael@0 355 {
michael@0 356 if (args.callee().is<JSFunction>()) {
michael@0 357 JSFunction *fun = &args.callee().as<JSFunction>();
michael@0 358 if (fun->isInterpreted() && fun->nonLazyScript()->types)
michael@0 359 TypeMonitorCallSlow(cx, &args.callee(), args, constructing);
michael@0 360 }
michael@0 361 }
michael@0 362
michael@0 363 inline bool
michael@0 364 TrackPropertyTypes(ExclusiveContext *cx, JSObject *obj, jsid id)
michael@0 365 {
michael@0 366 if (obj->hasLazyType() || obj->type()->unknownProperties())
michael@0 367 return false;
michael@0 368
michael@0 369 if (obj->hasSingletonType() && !obj->type()->maybeGetProperty(id))
michael@0 370 return false;
michael@0 371
michael@0 372 return true;
michael@0 373 }
michael@0 374
michael@0 375 inline void
michael@0 376 EnsureTrackPropertyTypes(JSContext *cx, JSObject *obj, jsid id)
michael@0 377 {
michael@0 378 id = IdToTypeId(id);
michael@0 379
michael@0 380 if (obj->hasSingletonType()) {
michael@0 381 AutoEnterAnalysis enter(cx);
michael@0 382 if (obj->hasLazyType() && !obj->getType(cx)) {
michael@0 383 CrashAtUnhandlableOOM("Could not allocate TypeObject in EnsureTrackPropertyTypes");
michael@0 384 return;
michael@0 385 }
michael@0 386 if (!obj->type()->unknownProperties() && !obj->type()->getProperty(cx, id)) {
michael@0 387 MOZ_ASSERT(obj->type()->unknownProperties());
michael@0 388 return;
michael@0 389 }
michael@0 390 }
michael@0 391
michael@0 392 JS_ASSERT(obj->type()->unknownProperties() || TrackPropertyTypes(cx, obj, id));
michael@0 393 }
michael@0 394
michael@0 395 inline bool
michael@0 396 CanHaveEmptyPropertyTypesForOwnProperty(JSObject *obj)
michael@0 397 {
michael@0 398 // Per the comment on TypeSet::propertySet, property type sets for global
michael@0 399 // objects may be empty for 'own' properties if the global property still
michael@0 400 // has its initial undefined value.
michael@0 401 return obj->is<GlobalObject>();
michael@0 402 }
michael@0 403
michael@0 404 inline bool
michael@0 405 HasTypePropertyId(JSObject *obj, jsid id, Type type)
michael@0 406 {
michael@0 407 if (obj->hasLazyType())
michael@0 408 return true;
michael@0 409
michael@0 410 if (obj->type()->unknownProperties())
michael@0 411 return true;
michael@0 412
michael@0 413 if (HeapTypeSet *types = obj->type()->maybeGetProperty(IdToTypeId(id)))
michael@0 414 return types->hasType(type);
michael@0 415
michael@0 416 return false;
michael@0 417 }
michael@0 418
michael@0 419 inline bool
michael@0 420 HasTypePropertyId(JSObject *obj, jsid id, const Value &value)
michael@0 421 {
michael@0 422 return HasTypePropertyId(obj, id, GetValueType(value));
michael@0 423 }
michael@0 424
michael@0 425 /* Add a possible type for a property of obj. */
michael@0 426 inline void
michael@0 427 AddTypePropertyId(ExclusiveContext *cx, JSObject *obj, jsid id, Type type)
michael@0 428 {
michael@0 429 id = IdToTypeId(id);
michael@0 430 if (TrackPropertyTypes(cx, obj, id))
michael@0 431 obj->type()->addPropertyType(cx, id, type);
michael@0 432 }
michael@0 433
michael@0 434 inline void
michael@0 435 AddTypePropertyId(ExclusiveContext *cx, JSObject *obj, jsid id, const Value &value)
michael@0 436 {
michael@0 437 id = IdToTypeId(id);
michael@0 438 if (TrackPropertyTypes(cx, obj, id))
michael@0 439 obj->type()->addPropertyType(cx, id, value);
michael@0 440 }
michael@0 441
michael@0 442 inline void
michael@0 443 AddTypePropertyId(ExclusiveContext *cx, TypeObject *obj, jsid id, Type type)
michael@0 444 {
michael@0 445 if (!obj->unknownProperties())
michael@0 446 obj->addPropertyType(cx, id, type);
michael@0 447 }
michael@0 448
michael@0 449 inline void
michael@0 450 AddTypePropertyId(ExclusiveContext *cx, TypeObject *obj, jsid id, const Value &value)
michael@0 451 {
michael@0 452 if (!obj->unknownProperties())
michael@0 453 obj->addPropertyType(cx, id, value);
michael@0 454 }
michael@0 455
michael@0 456 /* Set one or more dynamic flags on a type object. */
michael@0 457 inline void
michael@0 458 MarkTypeObjectFlags(ExclusiveContext *cx, JSObject *obj, TypeObjectFlags flags)
michael@0 459 {
michael@0 460 if (!obj->hasLazyType() && !obj->type()->hasAllFlags(flags))
michael@0 461 obj->type()->setFlags(cx, flags);
michael@0 462 }
michael@0 463
michael@0 464 /*
michael@0 465 * Mark all properties of a type object as unknown. If markSetsUnknown is set,
michael@0 466 * scan the entire compartment and mark all type sets containing it as having
michael@0 467 * an unknown object. This is needed for correctness in dealing with mutable
michael@0 468 * __proto__, which can change the type of an object dynamically.
michael@0 469 */
michael@0 470 inline void
michael@0 471 MarkTypeObjectUnknownProperties(JSContext *cx, TypeObject *obj,
michael@0 472 bool markSetsUnknown = false)
michael@0 473 {
michael@0 474 if (!obj->unknownProperties())
michael@0 475 obj->markUnknown(cx);
michael@0 476 if (markSetsUnknown && !(obj->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN))
michael@0 477 cx->compartment()->types.markSetsUnknown(cx, obj);
michael@0 478 }
michael@0 479
michael@0 480 inline void
michael@0 481 MarkTypePropertyNonData(ExclusiveContext *cx, JSObject *obj, jsid id)
michael@0 482 {
michael@0 483 id = IdToTypeId(id);
michael@0 484 if (TrackPropertyTypes(cx, obj, id))
michael@0 485 obj->type()->markPropertyNonData(cx, id);
michael@0 486 }
michael@0 487
michael@0 488 inline void
michael@0 489 MarkTypePropertyNonWritable(ExclusiveContext *cx, JSObject *obj, jsid id)
michael@0 490 {
michael@0 491 id = IdToTypeId(id);
michael@0 492 if (TrackPropertyTypes(cx, obj, id))
michael@0 493 obj->type()->markPropertyNonWritable(cx, id);
michael@0 494 }
michael@0 495
michael@0 496 inline bool
michael@0 497 IsTypePropertyIdMarkedNonData(JSObject *obj, jsid id)
michael@0 498 {
michael@0 499 return obj->type()->isPropertyNonData(id);
michael@0 500 }
michael@0 501
michael@0 502 inline bool
michael@0 503 IsTypePropertyIdMarkedNonWritable(JSObject *obj, jsid id)
michael@0 504 {
michael@0 505 return obj->type()->isPropertyNonWritable(id);
michael@0 506 }
michael@0 507
michael@0 508 /* Mark a state change on a particular object. */
michael@0 509 inline void
michael@0 510 MarkObjectStateChange(ExclusiveContext *cx, JSObject *obj)
michael@0 511 {
michael@0 512 if (!obj->hasLazyType() && !obj->type()->unknownProperties())
michael@0 513 obj->type()->markStateChange(cx);
michael@0 514 }
michael@0 515
michael@0 516 /*
michael@0 517 * For an array or object which has not yet escaped and been referenced elsewhere,
michael@0 518 * pick a new type based on the object's current contents.
michael@0 519 */
michael@0 520
michael@0 521 inline void
michael@0 522 FixArrayType(ExclusiveContext *cx, HandleObject obj)
michael@0 523 {
michael@0 524 cx->compartment()->types.fixArrayType(cx, obj);
michael@0 525 }
michael@0 526
michael@0 527 inline void
michael@0 528 FixObjectType(ExclusiveContext *cx, HandleObject obj)
michael@0 529 {
michael@0 530 cx->compartment()->types.fixObjectType(cx, obj);
michael@0 531 }
michael@0 532
michael@0 533 /* Interface helpers for JSScript*. */
michael@0 534 extern void TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc,
michael@0 535 const js::Value &rval);
michael@0 536 extern void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc,
michael@0 537 js::types::Type type);
michael@0 538
michael@0 539 /////////////////////////////////////////////////////////////////////
michael@0 540 // Script interface functions
michael@0 541 /////////////////////////////////////////////////////////////////////
michael@0 542
michael@0 543 /* static */ inline unsigned
michael@0 544 TypeScript::NumTypeSets(JSScript *script)
michael@0 545 {
michael@0 546 return script->nTypeSets() + analyze::LocalSlot(script, 0);
michael@0 547 }
michael@0 548
michael@0 549 /* static */ inline StackTypeSet *
michael@0 550 TypeScript::ThisTypes(JSScript *script)
michael@0 551 {
michael@0 552 return script->types->typeArray() + script->nTypeSets() + analyze::ThisSlot();
michael@0 553 }
michael@0 554
michael@0 555 /*
michael@0 556 * Note: for non-escaping arguments and locals, argTypes/localTypes reflect
michael@0 557 * only the initial type of the variable (e.g. passed values for argTypes,
michael@0 558 * or undefined for localTypes) and not types from subsequent assignments.
michael@0 559 */
michael@0 560
michael@0 561 /* static */ inline StackTypeSet *
michael@0 562 TypeScript::ArgTypes(JSScript *script, unsigned i)
michael@0 563 {
michael@0 564 JS_ASSERT(i < script->functionNonDelazifying()->nargs());
michael@0 565 return script->types->typeArray() + script->nTypeSets() + analyze::ArgSlot(i);
michael@0 566 }
michael@0 567
michael@0 568 template <typename TYPESET>
michael@0 569 /* static */ inline TYPESET *
michael@0 570 TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *bytecodeMap,
michael@0 571 uint32_t *hint, TYPESET *typeArray)
michael@0 572 {
michael@0 573 JS_ASSERT(js_CodeSpec[*pc].format & JOF_TYPESET);
michael@0 574 uint32_t offset = script->pcToOffset(pc);
michael@0 575
michael@0 576 // See if this pc is the next typeset opcode after the last one looked up.
michael@0 577 if ((*hint + 1) < script->nTypeSets() && bytecodeMap[*hint + 1] == offset) {
michael@0 578 (*hint)++;
michael@0 579 return typeArray + *hint;
michael@0 580 }
michael@0 581
michael@0 582 // See if this pc is the same as the last one looked up.
michael@0 583 if (bytecodeMap[*hint] == offset)
michael@0 584 return typeArray + *hint;
michael@0 585
michael@0 586 // Fall back to a binary search.
michael@0 587 size_t bottom = 0;
michael@0 588 size_t top = script->nTypeSets() - 1;
michael@0 589 size_t mid = bottom + (top - bottom) / 2;
michael@0 590 while (mid < top) {
michael@0 591 if (bytecodeMap[mid] < offset)
michael@0 592 bottom = mid + 1;
michael@0 593 else if (bytecodeMap[mid] > offset)
michael@0 594 top = mid;
michael@0 595 else
michael@0 596 break;
michael@0 597 mid = bottom + (top - bottom) / 2;
michael@0 598 }
michael@0 599
michael@0 600 // We should have have zeroed in on either the exact offset, unless there
michael@0 601 // are more JOF_TYPESET opcodes than nTypeSets in the script (as can happen
michael@0 602 // if the script is very long).
michael@0 603 JS_ASSERT(bytecodeMap[mid] == offset || mid == top);
michael@0 604
michael@0 605 *hint = mid;
michael@0 606 return typeArray + *hint;
michael@0 607 }
michael@0 608
michael@0 609 /* static */ inline StackTypeSet *
michael@0 610 TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc)
michael@0 611 {
michael@0 612 JS_ASSERT(CurrentThreadCanAccessRuntime(script->runtimeFromMainThread()));
michael@0 613 #ifdef JS_ION
michael@0 614 uint32_t *hint = script->baselineScript()->bytecodeTypeMap() + script->nTypeSets();
michael@0 615 return BytecodeTypes(script, pc, script->baselineScript()->bytecodeTypeMap(),
michael@0 616 hint, script->types->typeArray());
michael@0 617 #else
michael@0 618 MOZ_CRASH();
michael@0 619 #endif
michael@0 620 }
michael@0 621
michael@0 622 struct AllocationSiteKey : public DefaultHasher<AllocationSiteKey> {
michael@0 623 JSScript *script;
michael@0 624
michael@0 625 uint32_t offset : 24;
michael@0 626 JSProtoKey kind : 8;
michael@0 627
michael@0 628 static const uint32_t OFFSET_LIMIT = (1 << 23);
michael@0 629
michael@0 630 AllocationSiteKey() { mozilla::PodZero(this); }
michael@0 631
michael@0 632 static inline uint32_t hash(AllocationSiteKey key) {
michael@0 633 return uint32_t(size_t(key.script->offsetToPC(key.offset)) ^ key.kind);
michael@0 634 }
michael@0 635
michael@0 636 static inline bool match(const AllocationSiteKey &a, const AllocationSiteKey &b) {
michael@0 637 return a.script == b.script && a.offset == b.offset && a.kind == b.kind;
michael@0 638 }
michael@0 639 };
michael@0 640
michael@0 641 /* Whether to use a new type object for an initializer opcode at script/pc. */
michael@0 642 js::NewObjectKind
michael@0 643 UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key);
michael@0 644
michael@0 645 js::NewObjectKind
michael@0 646 UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, const Class *clasp);
michael@0 647
michael@0 648 /* static */ inline TypeObject *
michael@0 649 TypeScript::InitObject(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoKey kind)
michael@0 650 {
michael@0 651 JS_ASSERT(!UseNewTypeForInitializer(script, pc, kind));
michael@0 652
michael@0 653 /* :XXX: Limit script->length so we don't need to check the offset up front? */
michael@0 654 uint32_t offset = script->pcToOffset(pc);
michael@0 655
michael@0 656 if (!script->compileAndGo() || offset >= AllocationSiteKey::OFFSET_LIMIT)
michael@0 657 return GetTypeNewObject(cx, kind);
michael@0 658
michael@0 659 AllocationSiteKey key;
michael@0 660 key.script = script;
michael@0 661 key.offset = offset;
michael@0 662 key.kind = kind;
michael@0 663
michael@0 664 if (!cx->compartment()->types.allocationSiteTable)
michael@0 665 return cx->compartment()->types.addAllocationSiteTypeObject(cx, key);
michael@0 666
michael@0 667 AllocationSiteTable::Ptr p = cx->compartment()->types.allocationSiteTable->lookup(key);
michael@0 668
michael@0 669 if (p)
michael@0 670 return p->value();
michael@0 671 return cx->compartment()->types.addAllocationSiteTypeObject(cx, key);
michael@0 672 }
michael@0 673
michael@0 674 /* Set the type to use for obj according to the site it was allocated at. */
michael@0 675 static inline bool
michael@0 676 SetInitializerObjectType(JSContext *cx, HandleScript script, jsbytecode *pc, HandleObject obj, NewObjectKind kind)
michael@0 677 {
michael@0 678 JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass());
michael@0 679 JS_ASSERT(key != JSProto_Null);
michael@0 680 JS_ASSERT(kind == UseNewTypeForInitializer(script, pc, key));
michael@0 681
michael@0 682 if (kind == SingletonObject) {
michael@0 683 JS_ASSERT(obj->hasSingletonType());
michael@0 684
michael@0 685 /*
michael@0 686 * Inference does not account for types of run-once initializer
michael@0 687 * objects, as these may not be created until after the script
michael@0 688 * has been analyzed.
michael@0 689 */
michael@0 690 TypeScript::Monitor(cx, script, pc, ObjectValue(*obj));
michael@0 691 } else {
michael@0 692 types::TypeObject *type = TypeScript::InitObject(cx, script, pc, key);
michael@0 693 if (!type)
michael@0 694 return false;
michael@0 695 obj->uninlinedSetType(type);
michael@0 696 }
michael@0 697
michael@0 698 return true;
michael@0 699 }
michael@0 700
michael@0 701 /* static */ inline void
michael@0 702 TypeScript::Monitor(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval)
michael@0 703 {
michael@0 704 TypeMonitorResult(cx, script, pc, rval);
michael@0 705 }
michael@0 706
michael@0 707 /* static */ inline void
michael@0 708 TypeScript::Monitor(JSContext *cx, const js::Value &rval)
michael@0 709 {
michael@0 710 jsbytecode *pc;
michael@0 711 RootedScript script(cx, cx->currentScript(&pc));
michael@0 712 Monitor(cx, script, pc, rval);
michael@0 713 }
michael@0 714
michael@0 715 /* static */ inline void
michael@0 716 TypeScript::MonitorAssign(JSContext *cx, HandleObject obj, jsid id)
michael@0 717 {
michael@0 718 if (!obj->hasSingletonType()) {
michael@0 719 /*
michael@0 720 * Mark as unknown any object which has had dynamic assignments to
michael@0 721 * non-integer properties at SETELEM opcodes. This avoids making large
michael@0 722 * numbers of type properties for hashmap-style objects. We don't need
michael@0 723 * to do this for objects with singleton type, because type properties
michael@0 724 * are only constructed for them when analyzed scripts depend on those
michael@0 725 * specific properties.
michael@0 726 */
michael@0 727 uint32_t i;
michael@0 728 if (js_IdIsIndex(id, &i))
michael@0 729 return;
michael@0 730
michael@0 731 // But if we don't have too many properties yet, don't do anything. The
michael@0 732 // idea here is that normal object initialization should not trigger
michael@0 733 // deoptimization in most cases, while actual usage as a hashmap should.
michael@0 734 TypeObject* type = obj->type();
michael@0 735 if (type->getPropertyCount() < 8)
michael@0 736 return;
michael@0 737 MarkTypeObjectUnknownProperties(cx, type);
michael@0 738 }
michael@0 739 }
michael@0 740
michael@0 741 /* static */ inline void
michael@0 742 TypeScript::SetThis(JSContext *cx, JSScript *script, Type type)
michael@0 743 {
michael@0 744 if (!script->types)
michael@0 745 return;
michael@0 746
michael@0 747 if (!ThisTypes(script)->hasType(type)) {
michael@0 748 AutoEnterAnalysis enter(cx);
michael@0 749
michael@0 750 InferSpew(ISpewOps, "externalType: setThis #%u: %s",
michael@0 751 script->id(), TypeString(type));
michael@0 752 ThisTypes(script)->addType(cx, type);
michael@0 753 }
michael@0 754 }
michael@0 755
michael@0 756 /* static */ inline void
michael@0 757 TypeScript::SetThis(JSContext *cx, JSScript *script, const js::Value &value)
michael@0 758 {
michael@0 759 SetThis(cx, script, GetValueType(value));
michael@0 760 }
michael@0 761
michael@0 762 /* static */ inline void
michael@0 763 TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type)
michael@0 764 {
michael@0 765 if (!script->types)
michael@0 766 return;
michael@0 767
michael@0 768 if (!ArgTypes(script, arg)->hasType(type)) {
michael@0 769 AutoEnterAnalysis enter(cx);
michael@0 770
michael@0 771 InferSpew(ISpewOps, "externalType: setArg #%u %u: %s",
michael@0 772 script->id(), arg, TypeString(type));
michael@0 773 ArgTypes(script, arg)->addType(cx, type);
michael@0 774 }
michael@0 775 }
michael@0 776
michael@0 777 /* static */ inline void
michael@0 778 TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value)
michael@0 779 {
michael@0 780 Type type = GetValueType(value);
michael@0 781 SetArgument(cx, script, arg, type);
michael@0 782 }
michael@0 783
michael@0 784 /////////////////////////////////////////////////////////////////////
michael@0 785 // TypeCompartment
michael@0 786 /////////////////////////////////////////////////////////////////////
michael@0 787
michael@0 788 inline JSCompartment *
michael@0 789 TypeCompartment::compartment()
michael@0 790 {
michael@0 791 return (JSCompartment *)((char *)this - offsetof(JSCompartment, types));
michael@0 792 }
michael@0 793
michael@0 794 /////////////////////////////////////////////////////////////////////
michael@0 795 // TypeSet
michael@0 796 /////////////////////////////////////////////////////////////////////
michael@0 797
michael@0 798 /*
michael@0 799 * The sets of objects and scripts in a type set grow monotonically, are usually
michael@0 800 * empty, almost always small, and sometimes big. For empty or singleton sets,
michael@0 801 * the pointer refers directly to the value. For sets fitting into SET_ARRAY_SIZE,
michael@0 802 * an array of this length is used to store the elements. For larger sets, a hash
michael@0 803 * table filled to 25%-50% of capacity is used, with collisions resolved by linear
michael@0 804 * probing. TODO: replace these with jshashtables.
michael@0 805 */
michael@0 806 const unsigned SET_ARRAY_SIZE = 8;
michael@0 807 const unsigned SET_CAPACITY_OVERFLOW = 1u << 30;
michael@0 808
michael@0 809 /* Get the capacity of a set with the given element count. */
michael@0 810 static inline unsigned
michael@0 811 HashSetCapacity(unsigned count)
michael@0 812 {
michael@0 813 JS_ASSERT(count >= 2);
michael@0 814 JS_ASSERT(count < SET_CAPACITY_OVERFLOW);
michael@0 815
michael@0 816 if (count <= SET_ARRAY_SIZE)
michael@0 817 return SET_ARRAY_SIZE;
michael@0 818
michael@0 819 return 1u << (mozilla::FloorLog2(count) + 2);
michael@0 820 }
michael@0 821
michael@0 822 /* Compute the FNV hash for the low 32 bits of v. */
michael@0 823 template <class T, class KEY>
michael@0 824 static inline uint32_t
michael@0 825 HashKey(T v)
michael@0 826 {
michael@0 827 uint32_t nv = KEY::keyBits(v);
michael@0 828
michael@0 829 uint32_t hash = 84696351 ^ (nv & 0xff);
michael@0 830 hash = (hash * 16777619) ^ ((nv >> 8) & 0xff);
michael@0 831 hash = (hash * 16777619) ^ ((nv >> 16) & 0xff);
michael@0 832 return (hash * 16777619) ^ ((nv >> 24) & 0xff);
michael@0 833 }
michael@0 834
michael@0 835 /*
michael@0 836 * Insert space for an element into the specified set and grow its capacity if needed.
michael@0 837 * returned value is an existing or new entry (nullptr if new).
michael@0 838 */
michael@0 839 template <class T, class U, class KEY>
michael@0 840 static U **
michael@0 841 HashSetInsertTry(LifoAlloc &alloc, U **&values, unsigned &count, T key)
michael@0 842 {
michael@0 843 unsigned capacity = HashSetCapacity(count);
michael@0 844 unsigned insertpos = HashKey<T,KEY>(key) & (capacity - 1);
michael@0 845
michael@0 846 /* Whether we are converting from a fixed array to hashtable. */
michael@0 847 bool converting = (count == SET_ARRAY_SIZE);
michael@0 848
michael@0 849 if (!converting) {
michael@0 850 while (values[insertpos] != nullptr) {
michael@0 851 if (KEY::getKey(values[insertpos]) == key)
michael@0 852 return &values[insertpos];
michael@0 853 insertpos = (insertpos + 1) & (capacity - 1);
michael@0 854 }
michael@0 855 }
michael@0 856
michael@0 857 if (count >= SET_CAPACITY_OVERFLOW)
michael@0 858 return nullptr;
michael@0 859
michael@0 860 count++;
michael@0 861 unsigned newCapacity = HashSetCapacity(count);
michael@0 862
michael@0 863 if (newCapacity == capacity) {
michael@0 864 JS_ASSERT(!converting);
michael@0 865 return &values[insertpos];
michael@0 866 }
michael@0 867
michael@0 868 U **newValues = alloc.newArray<U*>(newCapacity);
michael@0 869 if (!newValues)
michael@0 870 return nullptr;
michael@0 871 mozilla::PodZero(newValues, newCapacity);
michael@0 872
michael@0 873 for (unsigned i = 0; i < capacity; i++) {
michael@0 874 if (values[i]) {
michael@0 875 unsigned pos = HashKey<T,KEY>(KEY::getKey(values[i])) & (newCapacity - 1);
michael@0 876 while (newValues[pos] != nullptr)
michael@0 877 pos = (pos + 1) & (newCapacity - 1);
michael@0 878 newValues[pos] = values[i];
michael@0 879 }
michael@0 880 }
michael@0 881
michael@0 882 values = newValues;
michael@0 883
michael@0 884 insertpos = HashKey<T,KEY>(key) & (newCapacity - 1);
michael@0 885 while (values[insertpos] != nullptr)
michael@0 886 insertpos = (insertpos + 1) & (newCapacity - 1);
michael@0 887 return &values[insertpos];
michael@0 888 }
michael@0 889
michael@0 890 /*
michael@0 891 * Insert an element into the specified set if it is not already there, returning
michael@0 892 * an entry which is nullptr if the element was not there.
michael@0 893 */
michael@0 894 template <class T, class U, class KEY>
michael@0 895 static inline U **
michael@0 896 HashSetInsert(LifoAlloc &alloc, U **&values, unsigned &count, T key)
michael@0 897 {
michael@0 898 if (count == 0) {
michael@0 899 JS_ASSERT(values == nullptr);
michael@0 900 count++;
michael@0 901 return (U **) &values;
michael@0 902 }
michael@0 903
michael@0 904 if (count == 1) {
michael@0 905 U *oldData = (U*) values;
michael@0 906 if (KEY::getKey(oldData) == key)
michael@0 907 return (U **) &values;
michael@0 908
michael@0 909 values = alloc.newArray<U*>(SET_ARRAY_SIZE);
michael@0 910 if (!values) {
michael@0 911 values = (U **) oldData;
michael@0 912 return nullptr;
michael@0 913 }
michael@0 914 mozilla::PodZero(values, SET_ARRAY_SIZE);
michael@0 915 count++;
michael@0 916
michael@0 917 values[0] = oldData;
michael@0 918 return &values[1];
michael@0 919 }
michael@0 920
michael@0 921 if (count <= SET_ARRAY_SIZE) {
michael@0 922 for (unsigned i = 0; i < count; i++) {
michael@0 923 if (KEY::getKey(values[i]) == key)
michael@0 924 return &values[i];
michael@0 925 }
michael@0 926
michael@0 927 if (count < SET_ARRAY_SIZE) {
michael@0 928 count++;
michael@0 929 return &values[count - 1];
michael@0 930 }
michael@0 931 }
michael@0 932
michael@0 933 return HashSetInsertTry<T,U,KEY>(alloc, values, count, key);
michael@0 934 }
michael@0 935
michael@0 936 /* Lookup an entry in a hash set, return nullptr if it does not exist. */
michael@0 937 template <class T, class U, class KEY>
michael@0 938 static inline U *
michael@0 939 HashSetLookup(U **values, unsigned count, T key)
michael@0 940 {
michael@0 941 if (count == 0)
michael@0 942 return nullptr;
michael@0 943
michael@0 944 if (count == 1)
michael@0 945 return (KEY::getKey((U *) values) == key) ? (U *) values : nullptr;
michael@0 946
michael@0 947 if (count <= SET_ARRAY_SIZE) {
michael@0 948 for (unsigned i = 0; i < count; i++) {
michael@0 949 if (KEY::getKey(values[i]) == key)
michael@0 950 return values[i];
michael@0 951 }
michael@0 952 return nullptr;
michael@0 953 }
michael@0 954
michael@0 955 unsigned capacity = HashSetCapacity(count);
michael@0 956 unsigned pos = HashKey<T,KEY>(key) & (capacity - 1);
michael@0 957
michael@0 958 while (values[pos] != nullptr) {
michael@0 959 if (KEY::getKey(values[pos]) == key)
michael@0 960 return values[pos];
michael@0 961 pos = (pos + 1) & (capacity - 1);
michael@0 962 }
michael@0 963
michael@0 964 return nullptr;
michael@0 965 }
michael@0 966
michael@0 967 inline TypeObjectKey *
michael@0 968 Type::objectKey() const
michael@0 969 {
michael@0 970 JS_ASSERT(isObject());
michael@0 971 if (isTypeObject())
michael@0 972 TypeObject::readBarrier((TypeObject *) data);
michael@0 973 else
michael@0 974 JSObject::readBarrier((JSObject *) (data ^ 1));
michael@0 975 return (TypeObjectKey *) data;
michael@0 976 }
michael@0 977
michael@0 978 inline JSObject *
michael@0 979 Type::singleObject() const
michael@0 980 {
michael@0 981 JS_ASSERT(isSingleObject());
michael@0 982 JSObject::readBarrier((JSObject *) (data ^ 1));
michael@0 983 return (JSObject *) (data ^ 1);
michael@0 984 }
michael@0 985
michael@0 986 inline TypeObject *
michael@0 987 Type::typeObject() const
michael@0 988 {
michael@0 989 JS_ASSERT(isTypeObject());
michael@0 990 TypeObject::readBarrier((TypeObject *) data);
michael@0 991 return (TypeObject *) data;
michael@0 992 }
michael@0 993
michael@0 994 inline bool
michael@0 995 TypeSet::hasType(Type type) const
michael@0 996 {
michael@0 997 if (unknown())
michael@0 998 return true;
michael@0 999
michael@0 1000 if (type.isUnknown()) {
michael@0 1001 return false;
michael@0 1002 } else if (type.isPrimitive()) {
michael@0 1003 return !!(flags & PrimitiveTypeFlag(type.primitive()));
michael@0 1004 } else if (type.isAnyObject()) {
michael@0 1005 return !!(flags & TYPE_FLAG_ANYOBJECT);
michael@0 1006 } else {
michael@0 1007 return !!(flags & TYPE_FLAG_ANYOBJECT) ||
michael@0 1008 HashSetLookup<TypeObjectKey*,TypeObjectKey,TypeObjectKey>
michael@0 1009 (objectSet, baseObjectCount(), type.objectKey()) != nullptr;
michael@0 1010 }
michael@0 1011 }
michael@0 1012
michael@0 1013 inline void
michael@0 1014 TypeSet::setBaseObjectCount(uint32_t count)
michael@0 1015 {
michael@0 1016 JS_ASSERT(count <= TYPE_FLAG_OBJECT_COUNT_LIMIT);
michael@0 1017 flags = (flags & ~TYPE_FLAG_OBJECT_COUNT_MASK)
michael@0 1018 | (count << TYPE_FLAG_OBJECT_COUNT_SHIFT);
michael@0 1019 }
michael@0 1020
michael@0 1021 inline void
michael@0 1022 HeapTypeSet::newPropertyState(ExclusiveContext *cxArg)
michael@0 1023 {
michael@0 1024 /* Propagate the change to all constraints. */
michael@0 1025 if (JSContext *cx = cxArg->maybeJSContext()) {
michael@0 1026 TypeConstraint *constraint = constraintList;
michael@0 1027 while (constraint) {
michael@0 1028 constraint->newPropertyState(cx, this);
michael@0 1029 constraint = constraint->next;
michael@0 1030 }
michael@0 1031 } else {
michael@0 1032 JS_ASSERT(!constraintList);
michael@0 1033 }
michael@0 1034 }
michael@0 1035
michael@0 1036 inline void
michael@0 1037 HeapTypeSet::setNonDataPropertyIgnoringConstraints()
michael@0 1038 {
michael@0 1039 flags |= TYPE_FLAG_NON_DATA_PROPERTY;
michael@0 1040 }
michael@0 1041
michael@0 1042 inline void
michael@0 1043 HeapTypeSet::setNonDataProperty(ExclusiveContext *cx)
michael@0 1044 {
michael@0 1045 if (flags & TYPE_FLAG_NON_DATA_PROPERTY)
michael@0 1046 return;
michael@0 1047
michael@0 1048 setNonDataPropertyIgnoringConstraints();
michael@0 1049 newPropertyState(cx);
michael@0 1050 }
michael@0 1051
michael@0 1052 inline void
michael@0 1053 HeapTypeSet::setNonWritableProperty(ExclusiveContext *cx)
michael@0 1054 {
michael@0 1055 if (flags & TYPE_FLAG_NON_WRITABLE_PROPERTY)
michael@0 1056 return;
michael@0 1057
michael@0 1058 flags |= TYPE_FLAG_NON_WRITABLE_PROPERTY;
michael@0 1059 newPropertyState(cx);
michael@0 1060 }
michael@0 1061
michael@0 1062 inline unsigned
michael@0 1063 TypeSet::getObjectCount() const
michael@0 1064 {
michael@0 1065 JS_ASSERT(!unknownObject());
michael@0 1066 uint32_t count = baseObjectCount();
michael@0 1067 if (count > SET_ARRAY_SIZE)
michael@0 1068 return HashSetCapacity(count);
michael@0 1069 return count;
michael@0 1070 }
michael@0 1071
michael@0 1072 inline TypeObjectKey *
michael@0 1073 TypeSet::getObject(unsigned i) const
michael@0 1074 {
michael@0 1075 JS_ASSERT(i < getObjectCount());
michael@0 1076 if (baseObjectCount() == 1) {
michael@0 1077 JS_ASSERT(i == 0);
michael@0 1078 return (TypeObjectKey *) objectSet;
michael@0 1079 }
michael@0 1080 return objectSet[i];
michael@0 1081 }
michael@0 1082
michael@0 1083 inline JSObject *
michael@0 1084 TypeSet::getSingleObject(unsigned i) const
michael@0 1085 {
michael@0 1086 TypeObjectKey *key = getObject(i);
michael@0 1087 return (key && key->isSingleObject()) ? key->asSingleObject() : nullptr;
michael@0 1088 }
michael@0 1089
michael@0 1090 inline TypeObject *
michael@0 1091 TypeSet::getTypeObject(unsigned i) const
michael@0 1092 {
michael@0 1093 TypeObjectKey *key = getObject(i);
michael@0 1094 return (key && key->isTypeObject()) ? key->asTypeObject() : nullptr;
michael@0 1095 }
michael@0 1096
michael@0 1097 inline const Class *
michael@0 1098 TypeSet::getObjectClass(unsigned i) const
michael@0 1099 {
michael@0 1100 if (JSObject *object = getSingleObject(i))
michael@0 1101 return object->getClass();
michael@0 1102 if (TypeObject *object = getTypeObject(i))
michael@0 1103 return object->clasp();
michael@0 1104 return nullptr;
michael@0 1105 }
michael@0 1106
michael@0 1107 /////////////////////////////////////////////////////////////////////
michael@0 1108 // TypeObject
michael@0 1109 /////////////////////////////////////////////////////////////////////
michael@0 1110
michael@0 1111 inline TypeObject::TypeObject(const Class *clasp, TaggedProto proto, TypeObjectFlags initialFlags)
michael@0 1112 {
michael@0 1113 mozilla::PodZero(this);
michael@0 1114
michael@0 1115 /* Inner objects may not appear on prototype chains. */
michael@0 1116 JS_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject);
michael@0 1117
michael@0 1118 this->clasp_ = clasp;
michael@0 1119 this->proto_ = proto.raw();
michael@0 1120 this->flags_ = initialFlags;
michael@0 1121
michael@0 1122 InferSpew(ISpewOps, "newObject: %s", TypeObjectString(this));
michael@0 1123 }
michael@0 1124
michael@0 1125 inline uint32_t
michael@0 1126 TypeObject::basePropertyCount() const
michael@0 1127 {
michael@0 1128 return (flags() & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT;
michael@0 1129 }
michael@0 1130
michael@0 1131 inline void
michael@0 1132 TypeObject::setBasePropertyCount(uint32_t count)
michael@0 1133 {
michael@0 1134 // Note: Callers must ensure they are performing threadsafe operations.
michael@0 1135 JS_ASSERT(count <= OBJECT_FLAG_PROPERTY_COUNT_LIMIT);
michael@0 1136 flags_ = (flags() & ~OBJECT_FLAG_PROPERTY_COUNT_MASK)
michael@0 1137 | (count << OBJECT_FLAG_PROPERTY_COUNT_SHIFT);
michael@0 1138 }
michael@0 1139
michael@0 1140 inline HeapTypeSet *
michael@0 1141 TypeObject::getProperty(ExclusiveContext *cx, jsid id)
michael@0 1142 {
michael@0 1143 JS_ASSERT(cx->compartment()->activeAnalysis);
michael@0 1144
michael@0 1145 JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
michael@0 1146 JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id));
michael@0 1147 JS_ASSERT(!unknownProperties());
michael@0 1148
michael@0 1149 if (HeapTypeSet *types = maybeGetProperty(id))
michael@0 1150 return types;
michael@0 1151
michael@0 1152 Property *base = cx->typeLifoAlloc().new_<Property>(id);
michael@0 1153 if (!base) {
michael@0 1154 markUnknown(cx);
michael@0 1155 return nullptr;
michael@0 1156 }
michael@0 1157
michael@0 1158 uint32_t propertyCount = basePropertyCount();
michael@0 1159 Property **pprop = HashSetInsert<jsid,Property,Property>
michael@0 1160 (cx->typeLifoAlloc(), propertySet, propertyCount, id);
michael@0 1161 if (!pprop) {
michael@0 1162 markUnknown(cx);
michael@0 1163 return nullptr;
michael@0 1164 }
michael@0 1165
michael@0 1166 JS_ASSERT(!*pprop);
michael@0 1167
michael@0 1168 setBasePropertyCount(propertyCount);
michael@0 1169 *pprop = base;
michael@0 1170
michael@0 1171 updateNewPropertyTypes(cx, id, &base->types);
michael@0 1172
michael@0 1173 if (propertyCount == OBJECT_FLAG_PROPERTY_COUNT_LIMIT) {
michael@0 1174 // We hit the maximum number of properties the object can have, mark
michael@0 1175 // the object unknown so that new properties will not be added in the
michael@0 1176 // future.
michael@0 1177 markUnknown(cx);
michael@0 1178 }
michael@0 1179
michael@0 1180 return &base->types;
michael@0 1181 }
michael@0 1182
michael@0 1183 inline HeapTypeSet *
michael@0 1184 TypeObject::maybeGetProperty(jsid id)
michael@0 1185 {
michael@0 1186 JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
michael@0 1187 JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id));
michael@0 1188 JS_ASSERT(!unknownProperties());
michael@0 1189
michael@0 1190 Property *prop = HashSetLookup<jsid,Property,Property>
michael@0 1191 (propertySet, basePropertyCount(), id);
michael@0 1192
michael@0 1193 return prop ? &prop->types : nullptr;
michael@0 1194 }
michael@0 1195
michael@0 1196 inline unsigned
michael@0 1197 TypeObject::getPropertyCount()
michael@0 1198 {
michael@0 1199 uint32_t count = basePropertyCount();
michael@0 1200 if (count > SET_ARRAY_SIZE)
michael@0 1201 return HashSetCapacity(count);
michael@0 1202 return count;
michael@0 1203 }
michael@0 1204
michael@0 1205 inline Property *
michael@0 1206 TypeObject::getProperty(unsigned i)
michael@0 1207 {
michael@0 1208 JS_ASSERT(i < getPropertyCount());
michael@0 1209 if (basePropertyCount() == 1) {
michael@0 1210 JS_ASSERT(i == 0);
michael@0 1211 return (Property *) propertySet;
michael@0 1212 }
michael@0 1213 return propertySet[i];
michael@0 1214 }
michael@0 1215
michael@0 1216 inline void
michael@0 1217 TypeObjectAddendum::writeBarrierPre(TypeObjectAddendum *type)
michael@0 1218 {
michael@0 1219 #ifdef JSGC_INCREMENTAL
michael@0 1220 if (!type)
michael@0 1221 return;
michael@0 1222
michael@0 1223 switch (type->kind) {
michael@0 1224 case NewScript:
michael@0 1225 return TypeNewScript::writeBarrierPre(type->asNewScript());
michael@0 1226
michael@0 1227 case TypedObject:
michael@0 1228 return TypeTypedObject::writeBarrierPre(type->asTypedObject());
michael@0 1229 }
michael@0 1230 #endif
michael@0 1231 }
michael@0 1232
michael@0 1233 inline void
michael@0 1234 TypeNewScript::writeBarrierPre(TypeNewScript *newScript)
michael@0 1235 {
michael@0 1236 #ifdef JSGC_INCREMENTAL
michael@0 1237 if (!newScript || !newScript->fun->runtimeFromAnyThread()->needsBarrier())
michael@0 1238 return;
michael@0 1239
michael@0 1240 JS::Zone *zone = newScript->fun->zoneFromAnyThread();
michael@0 1241 if (zone->needsBarrier()) {
michael@0 1242 MarkObject(zone->barrierTracer(), &newScript->fun, "write barrier");
michael@0 1243 MarkObject(zone->barrierTracer(), &newScript->templateObject, "write barrier");
michael@0 1244 }
michael@0 1245 #endif
michael@0 1246 }
michael@0 1247
michael@0 1248 } } /* namespace js::types */
michael@0 1249
michael@0 1250 inline bool
michael@0 1251 JSScript::ensureHasTypes(JSContext *cx)
michael@0 1252 {
michael@0 1253 return types || makeTypes(cx);
michael@0 1254 }
michael@0 1255
michael@0 1256 namespace js {
michael@0 1257
michael@0 1258 template <>
michael@0 1259 struct GCMethods<const types::Type>
michael@0 1260 {
michael@0 1261 static types::Type initial() { return types::Type::UnknownType(); }
michael@0 1262 static ThingRootKind kind() { return THING_ROOT_TYPE; }
michael@0 1263 static bool poisoned(const types::Type &v) {
michael@0 1264 return (v.isTypeObject() && IsPoisonedPtr(v.typeObject()))
michael@0 1265 || (v.isSingleObject() && IsPoisonedPtr(v.singleObject()));
michael@0 1266 }
michael@0 1267 };
michael@0 1268
michael@0 1269 template <>
michael@0 1270 struct GCMethods<types::Type>
michael@0 1271 {
michael@0 1272 static types::Type initial() { return types::Type::UnknownType(); }
michael@0 1273 static ThingRootKind kind() { return THING_ROOT_TYPE; }
michael@0 1274 static bool poisoned(const types::Type &v) {
michael@0 1275 return (v.isTypeObject() && IsPoisonedPtr(v.typeObject()))
michael@0 1276 || (v.isSingleObject() && IsPoisonedPtr(v.singleObject()));
michael@0 1277 }
michael@0 1278 };
michael@0 1279
michael@0 1280 } // namespace js
michael@0 1281
michael@0 1282 namespace JS {
michael@0 1283 template<> class AnchorPermitted<js::types::TypeObject *> { };
michael@0 1284 } // namespace JS
michael@0 1285
michael@0 1286 #endif /* jsinferinlines_h */

mercurial