2010-10-29 08:05:55 -07:00
|
|
|
/* -*- Mode: c++; c-basic-offset: 4; tab-width: 40; indent-tabs-mode: nil -*- */
|
|
|
|
/* vim: set ts=40 sw=4 et tw=99: */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is the Mozilla SpiderMonkey bytecode type inference
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Mozilla Foundation
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2010
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Brian Hackett <bhackett@mozilla.com>
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
2010-11-01 20:03:46 -07:00
|
|
|
/* Inline members for javascript type inference. */
|
2010-10-29 08:05:55 -07:00
|
|
|
|
2011-05-16 16:15:37 -07:00
|
|
|
#include "jsarray.h"
|
2010-10-29 08:05:55 -07:00
|
|
|
#include "jsanalyze.h"
|
|
|
|
#include "jscompartment.h"
|
|
|
|
#include "jsinfer.h"
|
2010-10-29 11:44:30 -07:00
|
|
|
#include "jsprf.h"
|
2011-05-12 17:59:28 -07:00
|
|
|
#include "vm/GlobalObject.h"
|
2010-10-29 08:05:55 -07:00
|
|
|
|
2011-06-05 22:39:45 -07:00
|
|
|
#include "vm/Stack-inl.h"
|
|
|
|
|
2010-10-29 08:05:55 -07:00
|
|
|
#ifndef jsinferinlines_h___
|
|
|
|
#define jsinferinlines_h___
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
// Types
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
namespace js {
|
|
|
|
namespace types {
|
|
|
|
|
2011-07-15 10:14:07 -07:00
|
|
|
/* static */ inline Type
|
|
|
|
Type::ObjectType(JSObject *obj)
|
|
|
|
{
|
|
|
|
if (obj->hasSingletonType())
|
|
|
|
return Type((jsuword) obj | 1);
|
|
|
|
return Type((jsuword) obj->type());
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */ inline Type
|
|
|
|
Type::ObjectType(TypeObject *obj)
|
|
|
|
{
|
|
|
|
if (obj->singleton)
|
|
|
|
return Type((jsuword) obj->singleton | 1);
|
|
|
|
return Type((jsuword) obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */ inline Type
|
|
|
|
Type::ObjectType(TypeObjectKey *obj)
|
|
|
|
{
|
|
|
|
return Type((jsuword) obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline Type
|
2010-10-29 08:05:55 -07:00
|
|
|
GetValueType(JSContext *cx, const Value &val)
|
|
|
|
{
|
2011-03-03 14:07:48 -08:00
|
|
|
JS_ASSERT(cx->typeInferenceEnabled());
|
2010-10-29 08:05:55 -07:00
|
|
|
if (val.isDouble())
|
2011-07-15 10:14:07 -07:00
|
|
|
return Type::DoubleType();
|
|
|
|
if (val.isObject())
|
|
|
|
return Type::ObjectType(&val.toObject());
|
|
|
|
return Type::PrimitiveType(val.extractNonDoubleType());
|
|
|
|
}
|
|
|
|
|
|
|
|
inline TypeFlags
|
|
|
|
PrimitiveTypeFlag(JSValueType type)
|
|
|
|
{
|
|
|
|
switch (type) {
|
2010-10-29 08:05:55 -07:00
|
|
|
case JSVAL_TYPE_UNDEFINED:
|
2011-07-15 10:14:07 -07:00
|
|
|
return TYPE_FLAG_UNDEFINED;
|
|
|
|
case JSVAL_TYPE_NULL:
|
|
|
|
return TYPE_FLAG_NULL;
|
2010-10-29 08:05:55 -07:00
|
|
|
case JSVAL_TYPE_BOOLEAN:
|
2011-07-15 10:14:07 -07:00
|
|
|
return TYPE_FLAG_BOOLEAN;
|
|
|
|
case JSVAL_TYPE_INT32:
|
|
|
|
return TYPE_FLAG_INT32;
|
|
|
|
case JSVAL_TYPE_DOUBLE:
|
|
|
|
return TYPE_FLAG_DOUBLE;
|
2010-10-29 08:05:55 -07:00
|
|
|
case JSVAL_TYPE_STRING:
|
2011-07-15 10:14:07 -07:00
|
|
|
return TYPE_FLAG_STRING;
|
2011-05-26 12:28:19 -07:00
|
|
|
case JSVAL_TYPE_MAGIC:
|
2011-07-15 10:14:07 -07:00
|
|
|
return TYPE_FLAG_LAZYARGS;
|
2010-10-29 08:05:55 -07:00
|
|
|
default:
|
2011-07-15 10:14:07 -07:00
|
|
|
JS_NOT_REACHED("Bad type");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline JSValueType
|
|
|
|
TypeFlagPrimitive(TypeFlags flags)
|
|
|
|
{
|
|
|
|
switch (flags) {
|
|
|
|
case TYPE_FLAG_UNDEFINED:
|
|
|
|
return JSVAL_TYPE_UNDEFINED;
|
|
|
|
case TYPE_FLAG_NULL:
|
|
|
|
return JSVAL_TYPE_NULL;
|
|
|
|
case TYPE_FLAG_BOOLEAN:
|
|
|
|
return JSVAL_TYPE_BOOLEAN;
|
|
|
|
case TYPE_FLAG_INT32:
|
|
|
|
return JSVAL_TYPE_INT32;
|
|
|
|
case TYPE_FLAG_DOUBLE:
|
|
|
|
return JSVAL_TYPE_DOUBLE;
|
|
|
|
case TYPE_FLAG_STRING:
|
|
|
|
return JSVAL_TYPE_STRING;
|
|
|
|
case TYPE_FLAG_LAZYARGS:
|
|
|
|
return JSVAL_TYPE_MAGIC;
|
|
|
|
default:
|
|
|
|
JS_NOT_REACHED("Bad type");
|
|
|
|
return (JSValueType) 0;
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the canonical representation of an id to use when doing inference. This
|
|
|
|
* maintains the constraint that if two different jsids map to the same property
|
|
|
|
* in JS (e.g. 3 and "3"), they have the same type representation.
|
|
|
|
*/
|
|
|
|
inline jsid
|
2010-12-18 20:44:51 -08:00
|
|
|
MakeTypeId(JSContext *cx, jsid id)
|
2010-10-29 08:05:55 -07:00
|
|
|
{
|
2011-05-20 19:33:06 -07:00
|
|
|
JS_ASSERT(!JSID_IS_EMPTY(id));
|
|
|
|
|
2010-10-29 08:05:55 -07:00
|
|
|
/*
|
|
|
|
* All integers must map to the aggregate property for index types, including
|
|
|
|
* negative integers.
|
|
|
|
*/
|
|
|
|
if (JSID_IS_INT(id))
|
|
|
|
return JSID_VOID;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for numeric strings, as in js_StringIsIndex, but allow negative
|
2010-12-18 20:44:51 -08:00
|
|
|
* and overflowing integers.
|
2010-10-29 08:05:55 -07:00
|
|
|
*/
|
|
|
|
if (JSID_IS_STRING(id)) {
|
2010-12-20 09:06:43 -08:00
|
|
|
JSFlatString *str = JSID_TO_FLAT_STRING(id);
|
|
|
|
const jschar *cp = str->getCharsZ(cx);
|
2010-10-29 08:05:55 -07:00
|
|
|
if (JS7_ISDEC(*cp) || *cp == '-') {
|
|
|
|
cp++;
|
|
|
|
while (JS7_ISDEC(*cp))
|
|
|
|
cp++;
|
2011-05-15 21:27:12 -07:00
|
|
|
if (*cp == 0)
|
2010-10-29 08:05:55 -07:00
|
|
|
return JSID_VOID;
|
|
|
|
}
|
2011-03-03 14:07:48 -08:00
|
|
|
return id;
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return JSID_VOID;
|
|
|
|
}
|
|
|
|
|
2010-12-19 08:09:45 -08:00
|
|
|
const char * TypeIdStringImpl(jsid id);
|
|
|
|
|
2010-11-18 15:18:23 -08:00
|
|
|
/* Convert an id for printing during debug. */
|
|
|
|
static inline const char *
|
2010-11-20 15:45:52 -08:00
|
|
|
TypeIdString(jsid id)
|
2010-11-18 15:18:23 -08:00
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
2010-12-07 17:11:37 -08:00
|
|
|
return TypeIdStringImpl(id);
|
2010-11-18 15:18:23 -08:00
|
|
|
#else
|
2010-11-24 17:41:52 -08:00
|
|
|
return "(missing)";
|
2010-11-18 15:18:23 -08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2011-03-03 14:07:48 -08:00
|
|
|
/*
|
|
|
|
* Structure for type inference entry point functions. All functions which can
|
|
|
|
* change type information must use this, and functions which depend on
|
|
|
|
* intermediate types (i.e. JITs) can use this to ensure that intermediate
|
|
|
|
* information is not collected and does not change.
|
|
|
|
*
|
|
|
|
* Pins inference results so that intermediate type information, TypeObjects
|
|
|
|
* and JSScripts won't be collected during GC. Does additional sanity checking
|
|
|
|
* that inference is not reentrant and that recompilations occur properly.
|
|
|
|
*/
|
|
|
|
struct AutoEnterTypeInference
|
|
|
|
{
|
|
|
|
JSContext *cx;
|
2011-04-22 07:59:45 -07:00
|
|
|
bool oldActiveAnalysis;
|
|
|
|
bool oldActiveInference;
|
2011-03-03 14:07:48 -08:00
|
|
|
|
|
|
|
AutoEnterTypeInference(JSContext *cx, bool compiling = false)
|
2011-04-22 07:59:45 -07:00
|
|
|
: cx(cx), oldActiveAnalysis(cx->compartment->activeAnalysis),
|
|
|
|
oldActiveInference(cx->compartment->activeInference)
|
2011-03-03 14:07:48 -08:00
|
|
|
{
|
|
|
|
JS_ASSERT_IF(!compiling, cx->compartment->types.inferenceEnabled);
|
2011-04-22 07:59:45 -07:00
|
|
|
cx->compartment->activeAnalysis = true;
|
|
|
|
cx->compartment->activeInference = true;
|
2011-03-03 14:07:48 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
~AutoEnterTypeInference()
|
|
|
|
{
|
2011-04-22 07:59:45 -07:00
|
|
|
cx->compartment->activeAnalysis = oldActiveAnalysis;
|
|
|
|
cx->compartment->activeInference = oldActiveInference;
|
2011-03-03 14:07:48 -08:00
|
|
|
|
|
|
|
/*
|
2011-04-22 07:59:45 -07:00
|
|
|
* If there are no more type inference activations on the stack,
|
|
|
|
* process any triggered recompilations. Note that we should not be
|
|
|
|
* invoking any scripted code while type inference is running.
|
|
|
|
* :TODO: assert this.
|
2011-03-03 14:07:48 -08:00
|
|
|
*/
|
2011-04-22 07:59:45 -07:00
|
|
|
if (!cx->compartment->activeInference) {
|
|
|
|
TypeCompartment *types = &cx->compartment->types;
|
|
|
|
if (types->pendingNukeTypes)
|
|
|
|
types->nukeTypes(cx);
|
|
|
|
else if (types->pendingRecompiles)
|
|
|
|
types->processPendingRecompiles(cx);
|
|
|
|
}
|
2011-03-03 14:07:48 -08:00
|
|
|
}
|
2011-04-22 07:59:45 -07:00
|
|
|
};
|
2011-03-03 14:07:48 -08:00
|
|
|
|
2011-03-30 14:10:16 -07:00
|
|
|
/*
|
|
|
|
* Structure marking the currently compiled script, for constraints which can
|
|
|
|
* trigger recompilation.
|
|
|
|
*/
|
|
|
|
struct AutoEnterCompilation
|
|
|
|
{
|
|
|
|
JSContext *cx;
|
|
|
|
JSScript *script;
|
|
|
|
|
|
|
|
AutoEnterCompilation(JSContext *cx, JSScript *script)
|
|
|
|
: cx(cx), script(script)
|
|
|
|
{
|
|
|
|
JS_ASSERT(!cx->compartment->types.compiledScript);
|
|
|
|
cx->compartment->types.compiledScript = script;
|
|
|
|
}
|
|
|
|
|
|
|
|
~AutoEnterCompilation()
|
|
|
|
{
|
|
|
|
JS_ASSERT(cx->compartment->types.compiledScript == script);
|
|
|
|
cx->compartment->types.compiledScript = NULL;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2010-10-29 08:05:55 -07:00
|
|
|
/////////////////////////////////////////////////////////////////////
|
2011-06-02 10:40:27 -07:00
|
|
|
// Interface functions
|
2010-10-29 08:05:55 -07:00
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
2011-06-02 10:40:27 -07:00
|
|
|
/*
|
|
|
|
* These functions check whether inference is enabled before performing some
|
|
|
|
* action on the type state. To avoid checking cx->typeInferenceEnabled()
|
|
|
|
* everywhere, it is generally preferred to use one of these functions or
|
|
|
|
* a type function on JSScript to perform inference operations.
|
|
|
|
*/
|
2011-03-03 14:07:48 -08:00
|
|
|
|
2011-06-02 10:40:27 -07:00
|
|
|
/*
|
|
|
|
* Get the default 'new' object for a given standard class, per the currently
|
|
|
|
* active global.
|
|
|
|
*/
|
|
|
|
inline TypeObject *
|
|
|
|
GetTypeNewObject(JSContext *cx, JSProtoKey key)
|
2010-11-24 17:41:52 -08:00
|
|
|
{
|
2010-12-18 20:44:51 -08:00
|
|
|
JSObject *proto;
|
2011-06-02 10:40:27 -07:00
|
|
|
if (!js_GetClassPrototype(cx, NULL, key, &proto, NULL))
|
2010-12-18 20:44:51 -08:00
|
|
|
return NULL;
|
2011-06-02 10:40:27 -07:00
|
|
|
return proto->getNewType(cx);
|
2010-11-24 17:41:52 -08:00
|
|
|
}
|
|
|
|
|
2011-06-02 10:40:27 -07:00
|
|
|
/* Get a type object for the immediate allocation site within a native. */
|
|
|
|
inline TypeObject *
|
2011-06-15 11:26:12 -07:00
|
|
|
GetTypeCallerInitObject(JSContext *cx, JSProtoKey key)
|
2010-10-29 08:05:55 -07:00
|
|
|
{
|
2011-06-02 10:40:27 -07:00
|
|
|
if (cx->typeInferenceEnabled()) {
|
2011-06-05 22:39:45 -07:00
|
|
|
jsbytecode *pc;
|
|
|
|
JSScript *script = cx->stack.currentScript(&pc);
|
|
|
|
if (script && script->compartment == cx->compartment)
|
2011-06-15 11:26:12 -07:00
|
|
|
return script->types.initObject(cx, pc, key);
|
2011-03-03 14:07:48 -08:00
|
|
|
}
|
2011-06-15 11:26:12 -07:00
|
|
|
return GetTypeNewObject(cx, key);
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
2011-06-10 19:03:57 -07:00
|
|
|
/*
|
|
|
|
* When using a custom iterator within the initialization of a 'for in' loop,
|
|
|
|
* mark the iterator values as unknown.
|
|
|
|
*/
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-06-10 19:03:57 -07:00
|
|
|
MarkIteratorUnknown(JSContext *cx)
|
2010-10-29 08:05:55 -07:00
|
|
|
{
|
2011-06-10 19:03:57 -07:00
|
|
|
extern void MarkIteratorUnknownSlow(JSContext *cx);
|
2011-06-02 10:40:27 -07:00
|
|
|
|
|
|
|
if (cx->typeInferenceEnabled())
|
2011-06-10 19:03:57 -07:00
|
|
|
MarkIteratorUnknownSlow(cx);
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
2011-06-02 10:40:27 -07:00
|
|
|
/*
|
|
|
|
* Monitor a javascript call, either on entry to the interpreter or made
|
|
|
|
* from within the interpreter.
|
|
|
|
*/
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-06-02 10:40:27 -07:00
|
|
|
TypeMonitorCall(JSContext *cx, const js::CallArgs &args, bool constructing)
|
|
|
|
{
|
|
|
|
extern void TypeMonitorCallSlow(JSContext *cx, JSObject *callee,
|
|
|
|
const CallArgs &args, bool constructing);
|
|
|
|
|
|
|
|
if (cx->typeInferenceEnabled()) {
|
|
|
|
JSObject *callee = &args.callee();
|
|
|
|
if (callee->isFunction() && callee->getFunctionPrivate()->isInterpreted())
|
|
|
|
TypeMonitorCallSlow(cx, callee, args, constructing);
|
2011-03-03 14:07:48 -08:00
|
|
|
}
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
2011-07-15 10:14:07 -07:00
|
|
|
inline bool
|
|
|
|
TrackPropertyTypes(JSContext *cx, JSObject *obj, jsid id)
|
|
|
|
{
|
2011-07-21 07:28:01 -07:00
|
|
|
if (!cx->typeInferenceEnabled() || obj->hasLazyType() || obj->type()->unknownProperties())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (obj->hasSingletonType() && !obj->type()->maybeGetProperty(cx, id))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
2011-07-15 10:14:07 -07:00
|
|
|
}
|
|
|
|
|
2011-06-02 10:40:27 -07:00
|
|
|
/* Add a possible type for a property of obj. */
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-07-15 10:14:07 -07:00
|
|
|
AddTypePropertyId(JSContext *cx, JSObject *obj, jsid id, Type type)
|
2010-10-29 08:05:55 -07:00
|
|
|
{
|
2011-07-21 07:28:01 -07:00
|
|
|
if (cx->typeInferenceEnabled())
|
|
|
|
id = MakeTypeId(cx, id);
|
2011-07-15 10:14:07 -07:00
|
|
|
if (TrackPropertyTypes(cx, obj, id))
|
|
|
|
obj->type()->addPropertyType(cx, id, type);
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-07-15 10:14:07 -07:00
|
|
|
AddTypePropertyId(JSContext *cx, JSObject *obj, jsid id, const Value &value)
|
2010-10-29 08:05:55 -07:00
|
|
|
{
|
2011-07-21 07:28:01 -07:00
|
|
|
if (cx->typeInferenceEnabled())
|
|
|
|
id = MakeTypeId(cx, id);
|
2011-07-15 10:14:07 -07:00
|
|
|
if (TrackPropertyTypes(cx, obj, id))
|
|
|
|
obj->type()->addPropertyType(cx, id, value);
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-07-15 10:14:07 -07:00
|
|
|
AddTypeProperty(JSContext *cx, TypeObject *obj, const char *name, Type type)
|
2010-10-29 08:05:55 -07:00
|
|
|
{
|
2011-06-02 10:40:27 -07:00
|
|
|
if (cx->typeInferenceEnabled() && !obj->unknownProperties())
|
|
|
|
obj->addPropertyType(cx, name, type);
|
2010-12-21 18:26:09 -08:00
|
|
|
}
|
|
|
|
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-06-02 10:40:27 -07:00
|
|
|
AddTypeProperty(JSContext *cx, TypeObject *obj, const char *name, const Value &value)
|
2011-03-15 23:50:44 -07:00
|
|
|
{
|
2011-06-02 10:40:27 -07:00
|
|
|
if (cx->typeInferenceEnabled() && !obj->unknownProperties())
|
|
|
|
obj->addPropertyType(cx, name, value);
|
2011-03-15 23:50:44 -07:00
|
|
|
}
|
|
|
|
|
2011-06-02 10:40:27 -07:00
|
|
|
/* Set one or more dynamic flags on a type object. */
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-07-15 10:14:07 -07:00
|
|
|
MarkTypeObjectFlags(JSContext *cx, JSObject *obj, TypeObjectFlags flags)
|
2011-04-11 20:10:46 -07:00
|
|
|
{
|
2011-07-21 07:28:01 -07:00
|
|
|
if (TrackPropertyTypes(cx, obj, JSID_EMPTY)) {
|
|
|
|
if (!obj->type()->hasAllFlags(flags))
|
|
|
|
obj->type()->setFlags(cx, flags);
|
2011-07-15 10:14:07 -07:00
|
|
|
}
|
2011-04-11 20:10:46 -07:00
|
|
|
}
|
|
|
|
|
2011-07-15 10:14:07 -07:00
|
|
|
/*
|
|
|
|
* Mark all properties of a type object as unknown. If markSetsUnknown is set,
|
|
|
|
* scan the entire compartment and mark all type sets containing it as having
|
|
|
|
* an unknown object. This is needed for correctness in dealing with mutable
|
|
|
|
* __proto__, which can change the type of an object dynamically.
|
|
|
|
*/
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-07-15 10:14:07 -07:00
|
|
|
MarkTypeObjectUnknownProperties(JSContext *cx, TypeObject *obj,
|
|
|
|
bool markSetsUnknown = false)
|
2011-04-11 20:10:46 -07:00
|
|
|
{
|
2011-07-15 10:14:07 -07:00
|
|
|
if (cx->typeInferenceEnabled()) {
|
|
|
|
if (!obj->unknownProperties())
|
|
|
|
obj->markUnknown(cx);
|
2011-07-21 07:28:01 -07:00
|
|
|
if (markSetsUnknown && !(obj->flags & OBJECT_FLAG_SETS_MARKED_UNKNOWN))
|
2011-07-15 10:14:07 -07:00
|
|
|
cx->compartment->types.markSetsUnknown(cx, obj);
|
|
|
|
}
|
2011-04-11 20:10:46 -07:00
|
|
|
}
|
|
|
|
|
2011-06-02 10:40:27 -07:00
|
|
|
/*
|
|
|
|
* Mark any property which has been deleted or configured to be non-writable or
|
|
|
|
* have a getter/setter.
|
|
|
|
*/
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-07-15 10:14:07 -07:00
|
|
|
MarkTypePropertyConfigured(JSContext *cx, JSObject *obj, jsid id)
|
2010-11-09 14:40:10 -08:00
|
|
|
{
|
2011-07-21 07:28:01 -07:00
|
|
|
if (cx->typeInferenceEnabled())
|
|
|
|
id = MakeTypeId(cx, id);
|
|
|
|
if (TrackPropertyTypes(cx, obj, id))
|
2011-07-15 10:14:07 -07:00
|
|
|
obj->type()->markPropertyConfigured(cx, id);
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
2011-06-02 10:40:27 -07:00
|
|
|
/* Mark a global object as having had its slots reallocated. */
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-06-02 10:40:27 -07:00
|
|
|
MarkGlobalReallocation(JSContext *cx, JSObject *obj)
|
2010-11-24 17:41:52 -08:00
|
|
|
{
|
2011-06-02 10:40:27 -07:00
|
|
|
JS_ASSERT(obj->isGlobal());
|
2011-07-15 10:14:07 -07:00
|
|
|
|
|
|
|
if (obj->hasLazyType()) {
|
|
|
|
/* No constraints listening to changes on this object. */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cx->typeInferenceEnabled() && !obj->type()->unknownProperties())
|
|
|
|
obj->type()->markSlotReallocation(cx);
|
2010-11-24 17:41:52 -08:00
|
|
|
}
|
|
|
|
|
2011-06-02 10:40:27 -07:00
|
|
|
/*
|
|
|
|
* For an array or object which has not yet escaped and been referenced elsewhere,
|
|
|
|
* pick a new type based on the object's current contents.
|
|
|
|
*/
|
|
|
|
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-06-02 10:40:27 -07:00
|
|
|
FixArrayType(JSContext *cx, JSObject *obj)
|
2011-03-10 16:17:39 -08:00
|
|
|
{
|
2011-06-02 10:40:27 -07:00
|
|
|
if (cx->typeInferenceEnabled())
|
|
|
|
cx->compartment->types.fixArrayType(cx, obj);
|
2011-03-10 16:17:39 -08:00
|
|
|
}
|
|
|
|
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-06-02 10:40:27 -07:00
|
|
|
FixObjectType(JSContext *cx, JSObject *obj)
|
2011-03-10 16:17:39 -08:00
|
|
|
{
|
2011-06-02 10:40:27 -07:00
|
|
|
if (cx->typeInferenceEnabled())
|
|
|
|
cx->compartment->types.fixObjectType(cx, obj);
|
2011-03-10 16:17:39 -08:00
|
|
|
}
|
|
|
|
|
2011-06-02 10:40:27 -07:00
|
|
|
/* Interface helpers for JSScript */
|
|
|
|
extern void TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval);
|
2011-07-15 10:14:07 -07:00
|
|
|
extern void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, js::types::Type type);
|
2011-06-02 10:40:27 -07:00
|
|
|
|
2011-07-07 21:02:57 -07:00
|
|
|
inline bool
|
|
|
|
UseNewTypeAtEntry(JSContext *cx, StackFrame *fp)
|
|
|
|
{
|
|
|
|
return fp->isConstructing() && cx->typeInferenceEnabled() &&
|
|
|
|
fp->prev() && fp->prev()->isScriptFrame() &&
|
|
|
|
UseNewType(cx, fp->prev()->script(), fp->prev()->pcQuadratic(cx->stack, fp));
|
|
|
|
}
|
|
|
|
|
2010-10-29 08:05:55 -07:00
|
|
|
/////////////////////////////////////////////////////////////////////
|
2011-06-02 10:40:27 -07:00
|
|
|
// Script interface functions
|
2010-10-29 08:05:55 -07:00
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
2011-06-06 08:32:41 -07:00
|
|
|
inline JSScript *
|
|
|
|
TypeScript::script()
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Each TypeScript is embedded as the 'types' field of a JSScript. They
|
|
|
|
* have the same lifetime, the distinction is made for code separation.
|
|
|
|
* Obtain the base pointer of the outer JSScript.
|
|
|
|
*/
|
|
|
|
return (JSScript *)((char *)this - offsetof(JSScript, types));
|
|
|
|
}
|
|
|
|
|
|
|
|
inline unsigned
|
|
|
|
TypeScript::numTypeSets()
|
|
|
|
{
|
|
|
|
return script()->nTypeSets + analyze::TotalSlots(script()) + script()->bindings.countUpvars();
|
|
|
|
}
|
|
|
|
|
2011-03-01 13:10:05 -08:00
|
|
|
inline bool
|
2011-06-06 08:32:41 -07:00
|
|
|
TypeScript::ensureTypeArray(JSContext *cx)
|
2011-03-01 13:10:05 -08:00
|
|
|
{
|
2011-05-14 05:45:13 -07:00
|
|
|
if (typeArray)
|
2011-03-01 13:10:05 -08:00
|
|
|
return true;
|
2011-05-14 05:45:13 -07:00
|
|
|
return makeTypeArray(cx);
|
|
|
|
}
|
|
|
|
|
2011-06-06 08:32:41 -07:00
|
|
|
inline TypeSet *
|
|
|
|
TypeScript::bytecodeTypes(const jsbytecode *pc)
|
2011-05-14 05:45:13 -07:00
|
|
|
{
|
|
|
|
JS_ASSERT(typeArray);
|
|
|
|
|
|
|
|
JSOp op = JSOp(*pc);
|
2011-05-19 10:09:17 -07:00
|
|
|
JS_ASSERT(op != JSOP_TRAP);
|
2011-05-14 05:45:13 -07:00
|
|
|
JS_ASSERT(js_CodeSpec[op].format & JOF_TYPESET);
|
|
|
|
|
|
|
|
/* All bytecodes with type sets are JOF_ATOM, except JSOP_{GET,CALL}ELEM */
|
2011-06-06 08:32:41 -07:00
|
|
|
const jsbytecode *npc = (op == JSOP_GETELEM || op == JSOP_CALLELEM) ? pc : pc + 2;
|
|
|
|
JS_ASSERT(npc - pc + 3 == js_CodeSpec[op].length);
|
|
|
|
|
|
|
|
uint16 index = GET_UINT16(npc);
|
|
|
|
JS_ASSERT(index < script()->nTypeSets);
|
2011-05-14 05:45:13 -07:00
|
|
|
|
|
|
|
return &typeArray[index];
|
2011-03-01 13:10:05 -08:00
|
|
|
}
|
|
|
|
|
2011-06-06 08:32:41 -07:00
|
|
|
inline TypeSet *
|
|
|
|
TypeScript::returnTypes()
|
2011-03-01 13:10:05 -08:00
|
|
|
{
|
2011-05-14 05:45:13 -07:00
|
|
|
JS_ASSERT(typeArray);
|
2011-06-06 08:32:41 -07:00
|
|
|
return &typeArray[script()->nTypeSets + js::analyze::CalleeSlot()];
|
2011-03-01 13:10:05 -08:00
|
|
|
}
|
|
|
|
|
2011-06-06 08:32:41 -07:00
|
|
|
inline TypeSet *
|
|
|
|
TypeScript::thisTypes()
|
2011-03-01 13:10:05 -08:00
|
|
|
{
|
2011-05-14 05:45:13 -07:00
|
|
|
JS_ASSERT(typeArray);
|
2011-06-06 08:32:41 -07:00
|
|
|
return &typeArray[script()->nTypeSets + js::analyze::ThisSlot()];
|
2011-03-01 13:10:05 -08:00
|
|
|
}
|
|
|
|
|
2011-04-22 07:59:45 -07:00
|
|
|
/*
|
|
|
|
* Note: for non-escaping arguments and locals, argTypes/localTypes reflect
|
|
|
|
* only the initial type of the variable (e.g. passed values for argTypes,
|
|
|
|
* or undefined for localTypes) and not types from subsequent assignments.
|
|
|
|
*/
|
|
|
|
|
2011-06-06 08:32:41 -07:00
|
|
|
inline TypeSet *
|
|
|
|
TypeScript::argTypes(unsigned i)
|
2011-03-01 13:10:05 -08:00
|
|
|
{
|
2011-06-06 08:32:41 -07:00
|
|
|
JS_ASSERT(typeArray && script()->fun && i < script()->fun->nargs);
|
|
|
|
return &typeArray[script()->nTypeSets + js::analyze::ArgSlot(i)];
|
2011-04-22 07:59:45 -07:00
|
|
|
}
|
|
|
|
|
2011-06-06 08:32:41 -07:00
|
|
|
inline TypeSet *
|
|
|
|
TypeScript::localTypes(unsigned i)
|
2011-04-22 07:59:45 -07:00
|
|
|
{
|
2011-06-06 08:32:41 -07:00
|
|
|
JS_ASSERT(typeArray && i < script()->nfixed);
|
|
|
|
return &typeArray[script()->nTypeSets + js::analyze::LocalSlot(script(), i)];
|
2011-03-01 13:10:05 -08:00
|
|
|
}
|
|
|
|
|
2011-06-06 08:32:41 -07:00
|
|
|
inline TypeSet *
|
|
|
|
TypeScript::upvarTypes(unsigned i)
|
2010-12-28 11:53:50 -08:00
|
|
|
{
|
2011-06-06 08:32:41 -07:00
|
|
|
JS_ASSERT(typeArray && i < script()->bindings.countUpvars());
|
|
|
|
return &typeArray[script()->nTypeSets + js::analyze::TotalSlots(script()) + i];
|
2010-12-28 11:53:50 -08:00
|
|
|
}
|
|
|
|
|
2011-06-06 08:32:41 -07:00
|
|
|
inline TypeSet *
|
|
|
|
TypeScript::slotTypes(unsigned slot)
|
2010-12-28 11:53:50 -08:00
|
|
|
{
|
2011-06-06 08:32:41 -07:00
|
|
|
JS_ASSERT(typeArray && slot < js::analyze::TotalSlots(script()));
|
|
|
|
return &typeArray[script()->nTypeSets + slot];
|
2010-12-28 11:53:50 -08:00
|
|
|
}
|
|
|
|
|
2011-06-06 08:32:41 -07:00
|
|
|
inline TypeObject *
|
|
|
|
TypeScript::standardType(JSContext *cx, JSProtoKey key)
|
2010-12-28 11:53:50 -08:00
|
|
|
{
|
|
|
|
JSObject *proto;
|
2011-06-06 08:32:41 -07:00
|
|
|
if (!js_GetClassPrototype(cx, script()->global(), key, &proto, NULL))
|
2010-12-28 11:53:50 -08:00
|
|
|
return NULL;
|
|
|
|
return proto->getNewType(cx);
|
|
|
|
}
|
|
|
|
|
2011-07-21 07:28:01 -07:00
|
|
|
struct AllocationSiteKey {
|
|
|
|
JSScript *script;
|
|
|
|
uint32 offset : 23;
|
|
|
|
JSProtoKey kind : 8;
|
|
|
|
bool uncached : 1;
|
|
|
|
|
|
|
|
static const uint32 OFFSET_LIMIT = (1 << 23);
|
|
|
|
|
|
|
|
AllocationSiteKey() { PodZero(this); }
|
|
|
|
|
|
|
|
typedef AllocationSiteKey Lookup;
|
|
|
|
|
|
|
|
static inline uint32 hash(AllocationSiteKey key) {
|
2011-07-21 17:17:25 -07:00
|
|
|
return (uint32) size_t(key.script->code + key.offset) ^ key.kind;
|
2011-07-21 07:28:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool match(const AllocationSiteKey &a, const AllocationSiteKey &b) {
|
|
|
|
return a.script == b.script && a.offset == b.offset && a.kind == b.kind;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-06-06 08:32:41 -07:00
|
|
|
inline TypeObject *
|
2011-07-21 07:28:01 -07:00
|
|
|
TypeScript::initObject(JSContext *cx, const jsbytecode *pc, JSProtoKey kind)
|
2010-10-29 08:05:55 -07:00
|
|
|
{
|
2011-07-21 07:28:01 -07:00
|
|
|
/* :XXX: Limit script->length so we don't need to check the offset up front? */
|
2011-06-06 08:32:41 -07:00
|
|
|
uint32 offset = pc - script()->code;
|
2010-12-28 11:53:50 -08:00
|
|
|
|
2011-07-21 07:28:01 -07:00
|
|
|
if (!cx->typeInferenceEnabled() || !script()->hasGlobal() || offset >= AllocationSiteKey::OFFSET_LIMIT)
|
|
|
|
return GetTypeNewObject(cx, kind);
|
|
|
|
|
|
|
|
AllocationSiteKey key;
|
|
|
|
key.script = script();
|
|
|
|
key.offset = offset;
|
|
|
|
key.uncached = script()->isUncachedEval;
|
|
|
|
key.kind = kind;
|
|
|
|
|
|
|
|
if (!cx->compartment->types.allocationSiteTable)
|
|
|
|
return cx->compartment->types.newAllocationSiteTypeObject(cx, key);
|
|
|
|
|
|
|
|
AllocationSiteTable::Ptr p = cx->compartment->types.allocationSiteTable->lookup(key);
|
|
|
|
|
|
|
|
if (p)
|
|
|
|
return p->value;
|
|
|
|
return cx->compartment->types.newAllocationSiteTypeObject(cx, key);
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-06-06 08:32:41 -07:00
|
|
|
TypeScript::monitor(JSContext *cx, jsbytecode *pc, const js::Value &rval)
|
2010-10-29 08:05:55 -07:00
|
|
|
{
|
2011-06-02 10:40:27 -07:00
|
|
|
if (cx->typeInferenceEnabled())
|
2011-06-06 08:32:41 -07:00
|
|
|
TypeMonitorResult(cx, script(), pc, rval);
|
2011-03-01 13:10:05 -08:00
|
|
|
}
|
|
|
|
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-06-06 08:32:41 -07:00
|
|
|
TypeScript::monitorOverflow(JSContext *cx, jsbytecode *pc)
|
2010-11-15 18:13:05 -08:00
|
|
|
{
|
2011-05-14 05:45:13 -07:00
|
|
|
if (cx->typeInferenceEnabled())
|
2011-07-15 10:14:07 -07:00
|
|
|
TypeDynamicResult(cx, script(), pc, Type::DoubleType());
|
2010-11-15 18:13:05 -08:00
|
|
|
}
|
|
|
|
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-06-06 08:32:41 -07:00
|
|
|
TypeScript::monitorString(JSContext *cx, jsbytecode *pc)
|
2011-03-17 21:34:36 -07:00
|
|
|
{
|
2011-05-14 05:45:13 -07:00
|
|
|
if (cx->typeInferenceEnabled())
|
2011-07-15 10:14:07 -07:00
|
|
|
TypeDynamicResult(cx, script(), pc, Type::StringType());
|
2011-03-17 21:34:36 -07:00
|
|
|
}
|
|
|
|
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-06-06 08:32:41 -07:00
|
|
|
TypeScript::monitorUnknown(JSContext *cx, jsbytecode *pc)
|
2010-11-15 18:13:05 -08:00
|
|
|
{
|
2011-05-14 05:45:13 -07:00
|
|
|
if (cx->typeInferenceEnabled())
|
2011-07-15 10:14:07 -07:00
|
|
|
TypeDynamicResult(cx, script(), pc, Type::UnknownType());
|
2010-11-15 18:13:05 -08:00
|
|
|
}
|
|
|
|
|
2011-05-16 16:15:37 -07:00
|
|
|
inline void
|
2011-06-06 08:32:41 -07:00
|
|
|
TypeScript::monitorAssign(JSContext *cx, jsbytecode *pc,
|
|
|
|
JSObject *obj, jsid id, const js::Value &rval)
|
2011-05-16 16:15:37 -07:00
|
|
|
{
|
2011-07-15 10:14:07 -07:00
|
|
|
if (cx->typeInferenceEnabled() && !obj->hasLazyType()) {
|
2011-05-16 16:15:37 -07:00
|
|
|
/*
|
|
|
|
* Mark as unknown any object which has had dynamic assignments to
|
|
|
|
* non-integer properties at SETELEM opcodes. This avoids making large
|
|
|
|
* numbers of type properties for hashmap-style objects. :FIXME: this
|
|
|
|
* is too aggressive for things like prototype library initialization.
|
|
|
|
*/
|
|
|
|
uint32 i;
|
|
|
|
if (js_IdIsIndex(id, &i))
|
|
|
|
return;
|
2011-07-15 10:14:07 -07:00
|
|
|
MarkTypeObjectUnknownProperties(cx, obj->type());
|
2011-05-16 16:15:37 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-07-15 10:14:07 -07:00
|
|
|
TypeScript::setThis(JSContext *cx, Type type)
|
2010-10-29 08:05:55 -07:00
|
|
|
{
|
2011-03-03 14:07:48 -08:00
|
|
|
JS_ASSERT(cx->typeInferenceEnabled());
|
2011-05-14 05:45:13 -07:00
|
|
|
if (!ensureTypeArray(cx))
|
2011-05-09 07:12:47 -07:00
|
|
|
return;
|
2011-03-04 11:28:52 -08:00
|
|
|
|
|
|
|
/* Analyze the script regardless if -a was used. */
|
2011-06-06 08:32:41 -07:00
|
|
|
bool analyze = cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) && !script()->isUncachedEval;
|
2011-03-04 11:28:52 -08:00
|
|
|
|
|
|
|
if (!thisTypes()->hasType(type) || analyze) {
|
2011-06-06 08:32:41 -07:00
|
|
|
AutoEnterTypeInference enter(cx);
|
2010-10-29 08:05:55 -07:00
|
|
|
|
2011-06-06 08:32:41 -07:00
|
|
|
InferSpew(ISpewOps, "externalType: setThis #%u: %s",
|
|
|
|
script()->id(), TypeString(type));
|
2011-03-03 14:07:48 -08:00
|
|
|
thisTypes()->addType(cx, type);
|
|
|
|
|
2011-06-06 08:32:41 -07:00
|
|
|
if (analyze)
|
|
|
|
script()->ensureRanInference(cx);
|
2011-03-03 14:07:48 -08:00
|
|
|
}
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-06-06 08:32:41 -07:00
|
|
|
TypeScript::setThis(JSContext *cx, const js::Value &value)
|
2011-03-15 18:22:23 -07:00
|
|
|
{
|
|
|
|
if (cx->typeInferenceEnabled())
|
2011-06-06 08:32:41 -07:00
|
|
|
setThis(cx, GetValueType(cx, value));
|
2011-03-15 18:22:23 -07:00
|
|
|
}
|
|
|
|
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-07-15 10:14:07 -07:00
|
|
|
TypeScript::setLocal(JSContext *cx, unsigned local, Type type)
|
2010-10-29 08:05:55 -07:00
|
|
|
{
|
2011-05-14 05:45:13 -07:00
|
|
|
if (!cx->typeInferenceEnabled() || !ensureTypeArray(cx))
|
2011-05-09 07:12:47 -07:00
|
|
|
return;
|
2011-03-03 14:07:48 -08:00
|
|
|
if (!localTypes(local)->hasType(type)) {
|
2011-06-06 08:32:41 -07:00
|
|
|
AutoEnterTypeInference enter(cx);
|
2011-03-03 14:07:48 -08:00
|
|
|
|
2011-06-06 08:32:41 -07:00
|
|
|
InferSpew(ISpewOps, "externalType: setLocal #%u %u: %s",
|
|
|
|
script()->id(), local, TypeString(type));
|
2011-03-03 14:07:48 -08:00
|
|
|
localTypes(local)->addType(cx, type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-06-06 08:32:41 -07:00
|
|
|
TypeScript::setLocal(JSContext *cx, unsigned local, const js::Value &value)
|
2011-03-11 16:29:38 -08:00
|
|
|
{
|
|
|
|
if (cx->typeInferenceEnabled()) {
|
2011-07-15 10:14:07 -07:00
|
|
|
Type type = GetValueType(cx, value);
|
2011-06-06 08:32:41 -07:00
|
|
|
setLocal(cx, local, type);
|
2011-03-11 16:29:38 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-07-15 10:14:07 -07:00
|
|
|
TypeScript::setArgument(JSContext *cx, unsigned arg, Type type)
|
2011-03-03 14:07:48 -08:00
|
|
|
{
|
2011-05-14 05:45:13 -07:00
|
|
|
if (!cx->typeInferenceEnabled() || !ensureTypeArray(cx))
|
2011-05-09 07:12:47 -07:00
|
|
|
return;
|
2011-03-01 13:10:05 -08:00
|
|
|
if (!argTypes(arg)->hasType(type)) {
|
2011-06-06 08:32:41 -07:00
|
|
|
AutoEnterTypeInference enter(cx);
|
2011-03-03 14:07:48 -08:00
|
|
|
|
2011-06-06 08:32:41 -07:00
|
|
|
InferSpew(ISpewOps, "externalType: setArg #%u %u: %s",
|
|
|
|
script()->id(), arg, TypeString(type));
|
2011-03-03 14:07:48 -08:00
|
|
|
argTypes(arg)->addType(cx, type);
|
2011-03-01 13:10:05 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-06-06 08:32:41 -07:00
|
|
|
TypeScript::setArgument(JSContext *cx, unsigned arg, const js::Value &value)
|
2011-03-03 14:07:48 -08:00
|
|
|
{
|
|
|
|
if (cx->typeInferenceEnabled()) {
|
2011-07-15 10:14:07 -07:00
|
|
|
Type type = GetValueType(cx, value);
|
2011-06-06 08:32:41 -07:00
|
|
|
setArgument(cx, arg, type);
|
2011-03-03 14:07:48 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-09 07:12:47 -07:00
|
|
|
inline void
|
2011-06-06 08:32:41 -07:00
|
|
|
TypeScript::setUpvar(JSContext *cx, unsigned upvar, const js::Value &value)
|
2011-03-01 13:10:05 -08:00
|
|
|
{
|
2011-05-14 05:45:13 -07:00
|
|
|
if (!cx->typeInferenceEnabled() || !ensureTypeArray(cx))
|
2011-05-09 07:12:47 -07:00
|
|
|
return;
|
2011-07-15 10:14:07 -07:00
|
|
|
Type type = GetValueType(cx, value);
|
2011-03-01 13:10:05 -08:00
|
|
|
if (!upvarTypes(upvar)->hasType(type)) {
|
2011-06-06 08:32:41 -07:00
|
|
|
AutoEnterTypeInference enter(cx);
|
2011-03-03 14:07:48 -08:00
|
|
|
|
2011-06-06 08:32:41 -07:00
|
|
|
InferSpew(ISpewOps, "externalType: setUpvar #%u %u: %s",
|
|
|
|
script()->id(), upvar, TypeString(type));
|
2011-03-03 14:07:48 -08:00
|
|
|
upvarTypes(upvar)->addType(cx, type);
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
// TypeCompartment
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
2011-07-21 07:28:01 -07:00
|
|
|
inline JSCompartment *
|
|
|
|
TypeCompartment::compartment()
|
|
|
|
{
|
|
|
|
return (JSCompartment *)((char *)this - offsetof(JSCompartment, types));
|
|
|
|
}
|
|
|
|
|
2010-10-29 08:05:55 -07:00
|
|
|
inline void
|
2011-07-15 10:14:07 -07:00
|
|
|
TypeCompartment::addPending(JSContext *cx, TypeConstraint *constraint, TypeSet *source, Type type)
|
2010-10-29 08:05:55 -07:00
|
|
|
{
|
|
|
|
JS_ASSERT(this == &cx->compartment->types);
|
2011-03-08 10:21:54 -08:00
|
|
|
JS_ASSERT(!cx->runtime->gcRunning);
|
2010-10-29 08:05:55 -07:00
|
|
|
|
2011-06-15 19:22:27 -07:00
|
|
|
InferSpew(ISpewOps, "pending: %sC%p%s %s",
|
|
|
|
InferSpewColor(constraint), constraint, InferSpewColorReset(),
|
|
|
|
TypeString(type));
|
2010-10-29 08:05:55 -07:00
|
|
|
|
|
|
|
if (pendingCount == pendingCapacity)
|
2011-03-01 13:10:05 -08:00
|
|
|
growPendingArray(cx);
|
2010-10-29 08:05:55 -07:00
|
|
|
|
|
|
|
PendingWork &pending = pendingArray[pendingCount++];
|
|
|
|
pending.constraint = constraint;
|
|
|
|
pending.source = source;
|
|
|
|
pending.type = type;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
TypeCompartment::resolvePending(JSContext *cx)
|
|
|
|
{
|
|
|
|
JS_ASSERT(this == &cx->compartment->types);
|
|
|
|
|
|
|
|
if (resolving) {
|
|
|
|
/* There is an active call further up resolving the worklist. */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
resolving = true;
|
|
|
|
|
|
|
|
/* Handle all pending type registrations. */
|
|
|
|
while (pendingCount) {
|
|
|
|
const PendingWork &pending = pendingArray[--pendingCount];
|
2011-06-15 19:22:27 -07:00
|
|
|
InferSpew(ISpewOps, "resolve: %sC%p%s %s",
|
|
|
|
InferSpewColor(pending.constraint), pending.constraint,
|
|
|
|
InferSpewColorReset(), TypeString(pending.type));
|
2010-10-29 08:05:55 -07:00
|
|
|
pending.constraint->newType(cx, pending.source, pending.type);
|
|
|
|
}
|
|
|
|
|
|
|
|
resolving = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
// TypeSet
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The sets of objects and scripts in a type set grow monotonically, are usually
|
|
|
|
* empty, almost always small, and sometimes big. For empty or singleton sets,
|
|
|
|
* the pointer refers directly to the value. For sets fitting into SET_ARRAY_SIZE,
|
|
|
|
* an array of this length is used to store the elements. For larger sets, a hash
|
|
|
|
* table filled to 25%-50% of capacity is used, with collisions resolved by linear
|
|
|
|
* probing. TODO: replace these with jshashtables.
|
|
|
|
*/
|
|
|
|
const unsigned SET_ARRAY_SIZE = 8;
|
|
|
|
|
|
|
|
/* Get the capacity of a set with the given element count. */
|
|
|
|
static inline unsigned
|
|
|
|
HashSetCapacity(unsigned count)
|
|
|
|
{
|
|
|
|
JS_ASSERT(count >= 2);
|
|
|
|
|
|
|
|
if (count <= SET_ARRAY_SIZE)
|
|
|
|
return SET_ARRAY_SIZE;
|
|
|
|
|
|
|
|
unsigned log2;
|
|
|
|
JS_FLOOR_LOG2(log2, count);
|
|
|
|
return 1 << (log2 + 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compute the FNV hash for the low 32 bits of v. */
|
2010-11-24 17:41:52 -08:00
|
|
|
template <class T, class KEY>
|
2010-10-29 08:05:55 -07:00
|
|
|
static inline uint32
|
2010-11-24 17:41:52 -08:00
|
|
|
HashKey(T v)
|
2010-10-29 08:05:55 -07:00
|
|
|
{
|
2010-11-24 17:41:52 -08:00
|
|
|
uint32 nv = KEY::keyBits(v);
|
2010-10-29 08:05:55 -07:00
|
|
|
|
|
|
|
uint32 hash = 84696351 ^ (nv & 0xff);
|
|
|
|
hash = (hash * 16777619) ^ ((nv >> 8) & 0xff);
|
|
|
|
hash = (hash * 16777619) ^ ((nv >> 16) & 0xff);
|
|
|
|
return (hash * 16777619) ^ ((nv >> 24) & 0xff);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-11-24 17:41:52 -08:00
|
|
|
* Insert space for an element into the specified set and grow its capacity if needed.
|
|
|
|
* returned value is an existing or new entry (NULL if new).
|
2010-10-29 08:05:55 -07:00
|
|
|
*/
|
2010-11-24 17:41:52 -08:00
|
|
|
template <class T, class U, class KEY>
|
2011-03-03 14:07:48 -08:00
|
|
|
static U **
|
|
|
|
HashSetInsertTry(JSContext *cx, U **&values, unsigned &count, T key, bool pool)
|
2010-10-29 08:05:55 -07:00
|
|
|
{
|
|
|
|
unsigned capacity = HashSetCapacity(count);
|
2010-11-24 17:41:52 -08:00
|
|
|
unsigned insertpos = HashKey<T,KEY>(key) & (capacity - 1);
|
2010-10-29 08:05:55 -07:00
|
|
|
|
2010-11-24 17:41:52 -08:00
|
|
|
/* Whether we are converting from a fixed array to hashtable. */
|
|
|
|
bool converting = (count == SET_ARRAY_SIZE);
|
2010-10-29 08:05:55 -07:00
|
|
|
|
2010-11-24 17:41:52 -08:00
|
|
|
if (!converting) {
|
|
|
|
while (values[insertpos] != NULL) {
|
|
|
|
if (KEY::getKey(values[insertpos]) == key)
|
2011-03-03 14:07:48 -08:00
|
|
|
return &values[insertpos];
|
2010-11-24 17:41:52 -08:00
|
|
|
insertpos = (insertpos + 1) & (capacity - 1);
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
count++;
|
|
|
|
unsigned newCapacity = HashSetCapacity(count);
|
|
|
|
|
2010-11-24 17:41:52 -08:00
|
|
|
if (newCapacity == capacity) {
|
|
|
|
JS_ASSERT(!converting);
|
2011-03-03 14:07:48 -08:00
|
|
|
return &values[insertpos];
|
2010-11-24 17:41:52 -08:00
|
|
|
}
|
2010-10-29 08:05:55 -07:00
|
|
|
|
2011-03-03 14:07:48 -08:00
|
|
|
U **newValues = pool
|
2011-04-22 07:59:45 -07:00
|
|
|
? ArenaArray<U*>(cx->compartment->pool, newCapacity)
|
2011-04-01 19:57:28 -07:00
|
|
|
: (U **) js::OffTheBooks::malloc_(newCapacity * sizeof(U*));
|
2011-03-03 14:07:48 -08:00
|
|
|
if (!newValues) {
|
|
|
|
cx->compartment->types.setPendingNukeTypes(cx);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
PodZero(newValues, newCapacity);
|
2010-10-29 08:05:55 -07:00
|
|
|
|
2010-11-24 17:41:52 -08:00
|
|
|
for (unsigned i = 0; i < capacity; i++) {
|
|
|
|
if (values[i]) {
|
|
|
|
unsigned pos = HashKey<T,KEY>(KEY::getKey(values[i])) & (newCapacity - 1);
|
|
|
|
while (newValues[pos] != NULL)
|
|
|
|
pos = (pos + 1) & (newCapacity - 1);
|
|
|
|
newValues[pos] = values[i];
|
|
|
|
}
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
2011-03-03 14:07:48 -08:00
|
|
|
if (values && !pool)
|
2011-05-04 04:49:14 -07:00
|
|
|
Foreground::free_(values);
|
2010-11-24 17:41:52 -08:00
|
|
|
values = newValues;
|
2010-10-29 08:05:55 -07:00
|
|
|
|
2010-11-24 17:41:52 -08:00
|
|
|
insertpos = HashKey<T,KEY>(key) & (newCapacity - 1);
|
|
|
|
while (values[insertpos] != NULL)
|
|
|
|
insertpos = (insertpos + 1) & (newCapacity - 1);
|
2011-03-03 14:07:48 -08:00
|
|
|
return &values[insertpos];
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-11-24 17:41:52 -08:00
|
|
|
* Insert an element into the specified set if it is not already there, returning
|
|
|
|
* an entry which is NULL if the element was not there.
|
2010-10-29 08:05:55 -07:00
|
|
|
*/
|
2010-11-24 17:41:52 -08:00
|
|
|
template <class T, class U, class KEY>
|
2011-03-03 14:07:48 -08:00
|
|
|
static inline U **
|
|
|
|
HashSetInsert(JSContext *cx, U **&values, unsigned &count, T key, bool pool)
|
2010-10-29 08:05:55 -07:00
|
|
|
{
|
|
|
|
if (count == 0) {
|
2010-11-24 17:41:52 -08:00
|
|
|
JS_ASSERT(values == NULL);
|
2010-10-29 08:05:55 -07:00
|
|
|
count++;
|
2011-03-03 14:07:48 -08:00
|
|
|
return (U **) &values;
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (count == 1) {
|
2010-11-24 17:41:52 -08:00
|
|
|
U *oldData = (U*) values;
|
2011-03-03 14:07:48 -08:00
|
|
|
if (KEY::getKey(oldData) == key)
|
|
|
|
return (U **) &values;
|
|
|
|
|
|
|
|
values = pool
|
2011-04-22 07:59:45 -07:00
|
|
|
? ArenaArray<U*>(cx->compartment->pool, SET_ARRAY_SIZE)
|
2011-04-01 19:57:28 -07:00
|
|
|
: (U **) js::OffTheBooks::malloc_(SET_ARRAY_SIZE * sizeof(U*));
|
2011-03-03 14:07:48 -08:00
|
|
|
if (!values) {
|
|
|
|
values = (U **) oldData;
|
|
|
|
cx->compartment->types.setPendingNukeTypes(cx);
|
|
|
|
return NULL;
|
2010-11-24 17:41:52 -08:00
|
|
|
}
|
2011-03-03 14:07:48 -08:00
|
|
|
PodZero(values, SET_ARRAY_SIZE);
|
2010-11-24 17:41:52 -08:00
|
|
|
count++;
|
2010-10-29 08:05:55 -07:00
|
|
|
|
|
|
|
values[0] = oldData;
|
2011-03-03 14:07:48 -08:00
|
|
|
return &values[1];
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (count <= SET_ARRAY_SIZE) {
|
|
|
|
for (unsigned i = 0; i < count; i++) {
|
2010-11-24 17:41:52 -08:00
|
|
|
if (KEY::getKey(values[i]) == key)
|
2011-03-03 14:07:48 -08:00
|
|
|
return &values[i];
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (count < SET_ARRAY_SIZE) {
|
2010-11-24 17:41:52 -08:00
|
|
|
count++;
|
2011-03-03 14:07:48 -08:00
|
|
|
return &values[count - 1];
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-03 14:07:48 -08:00
|
|
|
return HashSetInsertTry<T,U,KEY>(cx, values, count, key, pool);
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
2010-11-24 17:41:52 -08:00
|
|
|
/* Lookup an entry in a hash set, return NULL if it does not exist. */
|
|
|
|
template <class T, class U, class KEY>
|
|
|
|
static inline U *
|
|
|
|
HashSetLookup(U **values, unsigned count, T key)
|
2010-10-29 08:05:55 -07:00
|
|
|
{
|
|
|
|
if (count == 0)
|
2010-11-24 17:41:52 -08:00
|
|
|
return NULL;
|
2010-10-29 08:05:55 -07:00
|
|
|
|
|
|
|
if (count == 1)
|
2010-11-24 17:41:52 -08:00
|
|
|
return (KEY::getKey((U *) values) == key) ? (U *) values : NULL;
|
2010-10-29 08:05:55 -07:00
|
|
|
|
|
|
|
if (count <= SET_ARRAY_SIZE) {
|
|
|
|
for (unsigned i = 0; i < count; i++) {
|
2010-11-24 17:41:52 -08:00
|
|
|
if (KEY::getKey(values[i]) == key)
|
|
|
|
return values[i];
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
2010-11-24 17:41:52 -08:00
|
|
|
return NULL;
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned capacity = HashSetCapacity(count);
|
2010-11-24 17:41:52 -08:00
|
|
|
unsigned pos = HashKey<T,KEY>(key) & (capacity - 1);
|
2010-10-29 08:05:55 -07:00
|
|
|
|
|
|
|
while (values[pos] != NULL) {
|
2010-11-24 17:41:52 -08:00
|
|
|
if (KEY::getKey(values[pos]) == key)
|
|
|
|
return values[pos];
|
2010-10-29 08:05:55 -07:00
|
|
|
pos = (pos + 1) & (capacity - 1);
|
|
|
|
}
|
|
|
|
|
2010-11-24 17:41:52 -08:00
|
|
|
return NULL;
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
2011-07-21 07:28:01 -07:00
|
|
|
inline uint32
|
|
|
|
TypeSet::baseObjectCount() const
|
|
|
|
{
|
|
|
|
return (flags & TYPE_FLAG_OBJECT_COUNT_MASK) >> TYPE_FLAG_OBJECT_COUNT_SHIFT;
|
|
|
|
}
|
|
|
|
|
2010-10-29 08:05:55 -07:00
|
|
|
inline bool
|
2011-07-15 10:14:07 -07:00
|
|
|
TypeSet::hasType(Type type)
|
2010-10-29 08:05:55 -07:00
|
|
|
{
|
2010-12-21 07:32:21 -08:00
|
|
|
if (unknown())
|
2010-10-29 08:05:55 -07:00
|
|
|
return true;
|
|
|
|
|
2011-07-15 10:14:07 -07:00
|
|
|
if (type.isUnknown()) {
|
2011-03-01 13:10:05 -08:00
|
|
|
return false;
|
2011-07-15 10:14:07 -07:00
|
|
|
} else if (type.isPrimitive()) {
|
2011-07-21 07:28:01 -07:00
|
|
|
return !!(flags & PrimitiveTypeFlag(type.primitive()));
|
2011-07-15 10:14:07 -07:00
|
|
|
} else if (type.isAnyObject()) {
|
2011-07-21 07:28:01 -07:00
|
|
|
return !!(flags & TYPE_FLAG_ANYOBJECT);
|
2010-11-24 17:41:52 -08:00
|
|
|
} else {
|
2011-07-21 07:28:01 -07:00
|
|
|
return !!(flags & TYPE_FLAG_ANYOBJECT) ||
|
2011-07-15 10:14:07 -07:00
|
|
|
HashSetLookup<TypeObjectKey*,TypeObjectKey,TypeObjectKey>
|
2011-07-21 07:28:01 -07:00
|
|
|
(objectSet, baseObjectCount(), type.objectKey()) != NULL;
|
2010-11-24 17:41:52 -08:00
|
|
|
}
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
2011-07-21 07:28:01 -07:00
|
|
|
inline void
|
|
|
|
TypeSet::setBaseObjectCount(uint32 count)
|
|
|
|
{
|
|
|
|
JS_ASSERT(count <= TYPE_FLAG_OBJECT_COUNT_LIMIT);
|
|
|
|
flags = (flags & ~TYPE_FLAG_OBJECT_COUNT_MASK)
|
|
|
|
| (count << TYPE_FLAG_OBJECT_COUNT_SHIFT);
|
|
|
|
}
|
|
|
|
|
2011-03-09 11:04:36 -08:00
|
|
|
inline void
|
2011-07-15 10:14:07 -07:00
|
|
|
TypeSet::clearObjects()
|
2011-03-09 11:04:36 -08:00
|
|
|
{
|
2011-07-21 07:28:01 -07:00
|
|
|
if (baseObjectCount() >= 2 && !intermediate())
|
2011-07-15 10:14:07 -07:00
|
|
|
Foreground::free_(objectSet);
|
2011-07-21 07:28:01 -07:00
|
|
|
setBaseObjectCount(0);
|
2011-03-09 11:04:36 -08:00
|
|
|
objectSet = NULL;
|
|
|
|
}
|
|
|
|
|
2010-10-29 08:05:55 -07:00
|
|
|
inline void
|
2011-07-15 10:14:07 -07:00
|
|
|
TypeSet::addType(JSContext *cx, Type type)
|
2010-10-29 08:05:55 -07:00
|
|
|
{
|
2011-04-22 07:59:45 -07:00
|
|
|
JS_ASSERT(cx->compartment->activeInference);
|
2010-10-29 08:05:55 -07:00
|
|
|
|
2010-12-21 07:32:21 -08:00
|
|
|
if (unknown())
|
2010-10-29 08:05:55 -07:00
|
|
|
return;
|
|
|
|
|
2011-07-15 10:14:07 -07:00
|
|
|
if (type.isUnknown()) {
|
2011-07-21 07:28:01 -07:00
|
|
|
flags = TYPE_FLAG_UNKNOWN | (flags & ~baseFlags());
|
2011-07-15 10:14:07 -07:00
|
|
|
clearObjects();
|
|
|
|
} else if (type.isPrimitive()) {
|
|
|
|
TypeFlags flag = PrimitiveTypeFlag(type.primitive());
|
2011-07-21 07:28:01 -07:00
|
|
|
if (flags & flag)
|
2010-10-29 08:05:55 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* If we add float to a type set it is also considered to contain int. */
|
|
|
|
if (flag == TYPE_FLAG_DOUBLE)
|
|
|
|
flag |= TYPE_FLAG_INT32;
|
|
|
|
|
2011-07-21 07:28:01 -07:00
|
|
|
flags |= flag;
|
2010-10-29 08:05:55 -07:00
|
|
|
} else {
|
2011-07-21 07:28:01 -07:00
|
|
|
if (flags & TYPE_FLAG_ANYOBJECT)
|
2011-07-15 10:14:07 -07:00
|
|
|
return;
|
|
|
|
if (type.isAnyObject())
|
|
|
|
goto unknownObject;
|
2011-07-21 07:28:01 -07:00
|
|
|
uint32 objectCount = baseObjectCount();
|
2011-07-15 10:14:07 -07:00
|
|
|
TypeObjectKey *object = type.objectKey();
|
|
|
|
TypeObjectKey **pentry = HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
|
|
|
|
(cx, objectSet, objectCount, object, intermediate());
|
2011-03-03 14:07:48 -08:00
|
|
|
if (!pentry || *pentry)
|
2010-10-29 08:05:55 -07:00
|
|
|
return;
|
2011-03-03 14:07:48 -08:00
|
|
|
*pentry = object;
|
2011-03-09 11:04:36 -08:00
|
|
|
|
2011-07-21 07:28:01 -07:00
|
|
|
if (objectCount > TYPE_FLAG_OBJECT_COUNT_LIMIT)
|
|
|
|
goto unknownObject;
|
|
|
|
|
|
|
|
setBaseObjectCount(objectCount);
|
|
|
|
|
2011-07-15 10:14:07 -07:00
|
|
|
if (type.isTypeObject()) {
|
|
|
|
TypeObject *nobject = type.typeObject();
|
|
|
|
JS_ASSERT(!nobject->singleton);
|
|
|
|
if (nobject->unknownProperties())
|
|
|
|
goto unknownObject;
|
|
|
|
if (objectCount > 1) {
|
|
|
|
nobject->contribution += (objectCount - 1) * (objectCount - 1);
|
|
|
|
if (nobject->contribution >= TypeObject::CONTRIBUTION_LIMIT) {
|
|
|
|
InferSpew(ISpewOps, "limitUnknown: %sT%p%s",
|
|
|
|
InferSpewColor(this), this, InferSpewColorReset());
|
|
|
|
goto unknownObject;
|
|
|
|
}
|
2011-06-11 09:46:48 -07:00
|
|
|
}
|
2011-03-09 11:04:36 -08:00
|
|
|
}
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
2011-07-15 10:14:07 -07:00
|
|
|
if (false) {
|
|
|
|
unknownObject:
|
|
|
|
type = Type::AnyObjectType();
|
2011-07-21 07:28:01 -07:00
|
|
|
flags |= TYPE_FLAG_ANYOBJECT;
|
2011-07-15 10:14:07 -07:00
|
|
|
clearObjects();
|
|
|
|
}
|
|
|
|
|
2011-06-15 19:22:27 -07:00
|
|
|
InferSpew(ISpewOps, "addType: %sT%p%s %s",
|
|
|
|
InferSpewColor(this), this, InferSpewColorReset(),
|
|
|
|
TypeString(type));
|
2011-03-17 09:42:56 -07:00
|
|
|
|
2010-10-29 08:05:55 -07:00
|
|
|
/* Propagate the type to all constraints. */
|
|
|
|
TypeConstraint *constraint = constraintList;
|
|
|
|
while (constraint) {
|
|
|
|
cx->compartment->types.addPending(cx, constraint, this, type);
|
|
|
|
constraint = constraint->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
cx->compartment->types.resolvePending(cx);
|
|
|
|
}
|
|
|
|
|
2011-04-11 20:10:46 -07:00
|
|
|
inline void
|
|
|
|
TypeSet::setOwnProperty(JSContext *cx, bool configured)
|
|
|
|
{
|
|
|
|
TypeFlags nflags = TYPE_FLAG_OWN_PROPERTY | (configured ? TYPE_FLAG_CONFIGURED_PROPERTY : 0);
|
|
|
|
|
2011-07-21 07:28:01 -07:00
|
|
|
if ((flags & nflags) == nflags)
|
2011-04-11 20:10:46 -07:00
|
|
|
return;
|
|
|
|
|
2011-07-21 07:28:01 -07:00
|
|
|
flags |= nflags;
|
2011-04-11 20:10:46 -07:00
|
|
|
|
|
|
|
/* Propagate the change to all constraints. */
|
|
|
|
TypeConstraint *constraint = constraintList;
|
|
|
|
while (constraint) {
|
|
|
|
constraint->newPropertyState(cx, this);
|
|
|
|
constraint = constraint->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-05 18:12:03 -07:00
|
|
|
inline unsigned
|
|
|
|
TypeSet::getObjectCount()
|
|
|
|
{
|
2011-07-15 10:14:07 -07:00
|
|
|
JS_ASSERT(!unknownObject());
|
2011-07-21 07:28:01 -07:00
|
|
|
uint32 count = baseObjectCount();
|
|
|
|
if (count > SET_ARRAY_SIZE)
|
|
|
|
return HashSetCapacity(count);
|
|
|
|
return count;
|
2011-04-05 18:12:03 -07:00
|
|
|
}
|
|
|
|
|
2011-07-15 10:14:07 -07:00
|
|
|
inline TypeObjectKey *
|
2011-04-05 18:12:03 -07:00
|
|
|
TypeSet::getObject(unsigned i)
|
|
|
|
{
|
2011-07-15 10:14:07 -07:00
|
|
|
JS_ASSERT(i < getObjectCount());
|
2011-07-21 07:28:01 -07:00
|
|
|
if (baseObjectCount() == 1) {
|
2011-04-05 18:12:03 -07:00
|
|
|
JS_ASSERT(i == 0);
|
2011-07-15 10:14:07 -07:00
|
|
|
return (TypeObjectKey *) objectSet;
|
2011-04-05 18:12:03 -07:00
|
|
|
}
|
|
|
|
return objectSet[i];
|
|
|
|
}
|
|
|
|
|
2011-07-15 10:14:07 -07:00
|
|
|
inline JSObject *
|
|
|
|
TypeSet::getSingleObject(unsigned i)
|
|
|
|
{
|
|
|
|
TypeObjectKey *key = getObject(i);
|
|
|
|
return ((jsuword) key & 1) ? (JSObject *)((jsuword) key ^ 1) : NULL;
|
|
|
|
}
|
|
|
|
|
2011-05-12 20:07:23 -07:00
|
|
|
inline TypeObject *
|
2011-07-15 10:14:07 -07:00
|
|
|
TypeSet::getTypeObject(unsigned i)
|
2011-05-12 20:07:23 -07:00
|
|
|
{
|
2011-07-15 10:14:07 -07:00
|
|
|
TypeObjectKey *key = getObject(i);
|
|
|
|
return (key && !((jsuword) key & 1)) ? (TypeObject *) key : NULL;
|
2011-05-12 20:07:23 -07:00
|
|
|
}
|
|
|
|
|
2010-10-29 08:05:55 -07:00
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
// TypeCallsite
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
inline
|
2011-05-20 19:33:06 -07:00
|
|
|
TypeCallsite::TypeCallsite(JSContext *cx, JSScript *script, jsbytecode *pc,
|
2010-12-28 11:53:50 -08:00
|
|
|
bool isNew, unsigned argumentCount)
|
|
|
|
: script(script), pc(pc), isNew(isNew), argumentCount(argumentCount),
|
2011-05-20 19:33:06 -07:00
|
|
|
thisTypes(NULL), returnTypes(NULL)
|
2010-10-29 08:05:55 -07:00
|
|
|
{
|
2011-03-03 14:07:48 -08:00
|
|
|
/* Caller must check for failure. */
|
2011-04-22 07:59:45 -07:00
|
|
|
argumentTypes = ArenaArray<TypeSet*>(cx->compartment->pool, argumentCount);
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
2010-11-24 17:41:52 -08:00
|
|
|
// TypeObject
|
2010-10-29 08:05:55 -07:00
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
2011-06-15 11:26:12 -07:00
|
|
|
inline const char *
|
|
|
|
TypeObject::name()
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
return TypeIdString(name_);
|
|
|
|
#else
|
|
|
|
return NULL;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2011-07-21 07:28:01 -07:00
|
|
|
inline TypeObject::TypeObject(jsid name, JSObject *proto, bool function, bool unknown)
|
2011-06-15 11:26:12 -07:00
|
|
|
{
|
2011-07-15 10:14:07 -07:00
|
|
|
PodZero(this);
|
|
|
|
|
|
|
|
this->proto = proto;
|
2011-07-21 07:28:01 -07:00
|
|
|
if (function)
|
|
|
|
flags |= OBJECT_FLAG_FUNCTION;
|
|
|
|
if (unknown)
|
|
|
|
flags |= OBJECT_FLAG_UNKNOWN_MASK;
|
2011-07-15 10:14:07 -07:00
|
|
|
|
2011-06-15 11:26:12 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
this->name_ = name;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
InferSpew(ISpewOps, "newObject: %s", this->name());
|
|
|
|
}
|
|
|
|
|
2011-07-21 07:28:01 -07:00
|
|
|
inline uint32
|
|
|
|
TypeObject::basePropertyCount() const
|
|
|
|
{
|
|
|
|
return (flags & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
TypeObject::setBasePropertyCount(uint32 count)
|
|
|
|
{
|
|
|
|
JS_ASSERT(count <= OBJECT_FLAG_PROPERTY_COUNT_LIMIT);
|
|
|
|
flags = (flags & ~OBJECT_FLAG_PROPERTY_COUNT_MASK)
|
|
|
|
| (count << OBJECT_FLAG_PROPERTY_COUNT_SHIFT);
|
|
|
|
}
|
|
|
|
|
2010-10-29 08:05:55 -07:00
|
|
|
inline TypeSet *
|
2010-11-24 17:41:52 -08:00
|
|
|
TypeObject::getProperty(JSContext *cx, jsid id, bool assign)
|
2010-10-29 08:05:55 -07:00
|
|
|
{
|
2011-04-22 07:59:45 -07:00
|
|
|
JS_ASSERT(cx->compartment->activeInference);
|
2010-12-18 20:44:51 -08:00
|
|
|
JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
|
2011-05-20 19:33:06 -07:00
|
|
|
JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == MakeTypeId(cx, id));
|
2011-04-05 18:12:03 -07:00
|
|
|
JS_ASSERT(!unknownProperties());
|
2010-10-29 08:05:55 -07:00
|
|
|
|
2011-07-21 07:28:01 -07:00
|
|
|
uint32 propertyCount = basePropertyCount();
|
2011-03-03 14:07:48 -08:00
|
|
|
Property **pprop = HashSetInsert<jsid,Property,Property>
|
2011-07-21 07:28:01 -07:00
|
|
|
(cx, propertySet, propertyCount, id, singleton != NULL);
|
|
|
|
if (!pprop)
|
2011-03-03 14:07:48 -08:00
|
|
|
return NULL;
|
2010-11-20 15:45:52 -08:00
|
|
|
|
2011-07-21 07:28:01 -07:00
|
|
|
if (!*pprop) {
|
|
|
|
if (propertyCount > OBJECT_FLAG_PROPERTY_COUNT_LIMIT) {
|
|
|
|
markUnknown(cx);
|
|
|
|
TypeSet *types = TypeSet::make(cx, "propertyOverflow");
|
|
|
|
types->addType(cx, Type::UnknownType());
|
|
|
|
return types;
|
|
|
|
}
|
|
|
|
setBasePropertyCount(propertyCount);
|
|
|
|
if (!addProperty(cx, id, pprop))
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
TypeSet *types = &(*pprop)->types;
|
|
|
|
|
2011-04-11 20:10:46 -07:00
|
|
|
if (assign)
|
2011-07-21 07:28:01 -07:00
|
|
|
types->setOwnProperty(cx, false);
|
2011-04-11 20:10:46 -07:00
|
|
|
|
2011-07-21 07:28:01 -07:00
|
|
|
return types;
|
2010-10-29 08:05:55 -07:00
|
|
|
}
|
|
|
|
|
2011-07-21 07:28:01 -07:00
|
|
|
inline TypeSet *
|
|
|
|
TypeObject::maybeGetProperty(JSContext *cx, jsid id)
|
2011-07-15 10:14:07 -07:00
|
|
|
{
|
2011-07-21 07:28:01 -07:00
|
|
|
JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
|
|
|
|
JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == MakeTypeId(cx, id));
|
2011-07-15 10:14:07 -07:00
|
|
|
JS_ASSERT(!unknownProperties());
|
|
|
|
|
2011-07-21 07:28:01 -07:00
|
|
|
Property *prop = HashSetLookup<jsid,Property,Property>
|
|
|
|
(propertySet, basePropertyCount(), id);
|
|
|
|
|
|
|
|
return prop ? &prop->types : NULL;
|
2011-07-15 10:14:07 -07:00
|
|
|
}
|
|
|
|
|
2011-04-05 18:12:03 -07:00
|
|
|
inline unsigned
|
|
|
|
TypeObject::getPropertyCount()
|
|
|
|
{
|
2011-07-21 07:28:01 -07:00
|
|
|
uint32 count = basePropertyCount();
|
|
|
|
if (count > SET_ARRAY_SIZE)
|
|
|
|
return HashSetCapacity(count);
|
|
|
|
return count;
|
2011-04-05 18:12:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
inline Property *
|
|
|
|
TypeObject::getProperty(unsigned i)
|
|
|
|
{
|
2011-07-21 07:28:01 -07:00
|
|
|
JS_ASSERT(i < getPropertyCount());
|
|
|
|
if (basePropertyCount() == 1) {
|
2011-04-05 18:12:03 -07:00
|
|
|
JS_ASSERT(i == 0);
|
|
|
|
return (Property *) propertySet;
|
|
|
|
}
|
|
|
|
return propertySet[i];
|
|
|
|
}
|
|
|
|
|
2011-06-15 11:26:12 -07:00
|
|
|
inline void
|
|
|
|
TypeObject::setFlagsFromKey(JSContext *cx, JSProtoKey key)
|
2010-12-18 20:44:51 -08:00
|
|
|
{
|
2011-06-15 11:26:12 -07:00
|
|
|
TypeObjectFlags flags = 0;
|
2010-12-18 20:44:51 -08:00
|
|
|
|
2011-06-15 11:26:12 -07:00
|
|
|
switch (key) {
|
|
|
|
case JSProto_Function:
|
2011-07-21 07:28:01 -07:00
|
|
|
JS_ASSERT(isFunction());
|
2011-06-15 11:26:12 -07:00
|
|
|
/* FALLTHROUGH */
|
2010-12-18 20:44:51 -08:00
|
|
|
|
2011-06-15 11:26:12 -07:00
|
|
|
case JSProto_Object:
|
|
|
|
flags = OBJECT_FLAG_NON_DENSE_ARRAY
|
|
|
|
| OBJECT_FLAG_NON_PACKED_ARRAY
|
|
|
|
| OBJECT_FLAG_NON_TYPED_ARRAY;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JSProto_Array:
|
|
|
|
flags = OBJECT_FLAG_NON_TYPED_ARRAY;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* :XXX: abstract */
|
|
|
|
JS_ASSERT(key == JSProto_Int8Array ||
|
|
|
|
key == JSProto_Uint8Array ||
|
|
|
|
key == JSProto_Int16Array ||
|
|
|
|
key == JSProto_Uint16Array ||
|
|
|
|
key == JSProto_Int32Array ||
|
|
|
|
key == JSProto_Uint32Array ||
|
|
|
|
key == JSProto_Float32Array ||
|
|
|
|
key == JSProto_Float64Array ||
|
|
|
|
key == JSProto_Uint8ClampedArray);
|
|
|
|
flags = OBJECT_FLAG_NON_DENSE_ARRAY
|
|
|
|
| OBJECT_FLAG_NON_PACKED_ARRAY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hasAllFlags(flags))
|
|
|
|
setFlags(cx, flags);
|
2010-12-18 20:44:51 -08:00
|
|
|
}
|
|
|
|
|
2011-04-12 20:39:16 -07:00
|
|
|
class AutoTypeRooter : private AutoGCRooter {
|
|
|
|
public:
|
|
|
|
AutoTypeRooter(JSContext *cx, TypeObject *type
|
|
|
|
JS_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
|
|
: AutoGCRooter(cx, TYPE), type(type)
|
|
|
|
{
|
|
|
|
JS_GUARD_OBJECT_NOTIFIER_INIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
friend void AutoGCRooter::trace(JSTracer *trc);
|
|
|
|
friend void MarkRuntime(JSTracer *trc);
|
|
|
|
|
|
|
|
private:
|
|
|
|
TypeObject *type;
|
|
|
|
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
|
|
};
|
|
|
|
|
2010-12-18 20:44:51 -08:00
|
|
|
} } /* namespace js::types */
|
|
|
|
|
2011-06-06 08:32:41 -07:00
|
|
|
inline bool
|
|
|
|
JSScript::isAboutToBeFinalized(JSContext *cx)
|
|
|
|
{
|
|
|
|
return isCachedEval ||
|
|
|
|
(u.object && IsAboutToBeFinalized(cx, u.object)) ||
|
|
|
|
(fun && IsAboutToBeFinalized(cx, fun));
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool
|
|
|
|
JSScript::ensureRanInference(JSContext *cx)
|
|
|
|
{
|
|
|
|
js::analyze::ScriptAnalysis *analysis = this->analysis(cx);
|
|
|
|
if (analysis && !analysis->ranInference()) {
|
|
|
|
js::types::AutoEnterTypeInference enter(cx);
|
|
|
|
analysis->analyzeTypes(cx);
|
|
|
|
}
|
|
|
|
return analysis && !analysis->OOM();
|
|
|
|
}
|
|
|
|
|
2011-04-22 07:59:45 -07:00
|
|
|
inline void
|
|
|
|
js::analyze::ScriptAnalysis::addPushedType(JSContext *cx, uint32 offset, uint32 which,
|
2011-07-15 10:14:07 -07:00
|
|
|
js::types::Type type)
|
2011-04-22 07:59:45 -07:00
|
|
|
{
|
|
|
|
js::types::TypeSet *pushed = pushedTypes(offset, which);
|
|
|
|
pushed->addType(cx, type);
|
|
|
|
}
|
|
|
|
|
2010-10-29 08:05:55 -07:00
|
|
|
#endif // jsinferinlines_h___
|