Merge mozilla-central and mozilla-inbound

This commit is contained in:
Ehsan Akhgari 2011-09-07 10:53:26 -04:00
commit 7531e87587
71 changed files with 4287 additions and 414 deletions

View File

@ -665,7 +665,8 @@ static nsDOMClassInfoData sClassInfoData[] = {
NS_DEFINE_CLASSINFO_DATA(Navigator, nsNavigatorSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS |
nsIXPCScriptable::WANT_PRECREATE)
nsIXPCScriptable::WANT_PRECREATE |
nsIXPCScriptable::WANT_NEWRESOLVE)
NS_DEFINE_CLASSINFO_DATA(Plugin, nsPluginSH,
ARRAY_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(PluginArray, nsPluginArraySH,
@ -2014,7 +2015,8 @@ CutPrefix(const char *aName) {
nsresult
nsDOMClassInfo::RegisterClassName(PRInt32 aClassInfoID)
{
nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
nsScriptNameSpaceManager *nameSpaceManager =
nsJSRuntime::GetNameSpaceManager();
NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
nameSpaceManager->RegisterClassName(sClassInfoData[aClassInfoID].mName,
@ -2030,7 +2032,8 @@ nsDOMClassInfo::RegisterClassName(PRInt32 aClassInfoID)
nsresult
nsDOMClassInfo::RegisterClassProtos(PRInt32 aClassInfoID)
{
nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
nsScriptNameSpaceManager *nameSpaceManager =
nsJSRuntime::GetNameSpaceManager();
NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
PRBool found_old;
@ -2082,7 +2085,8 @@ nsDOMClassInfo::RegisterClassProtos(PRInt32 aClassInfoID)
nsresult
nsDOMClassInfo::RegisterExternalClasses()
{
nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
nsScriptNameSpaceManager *nameSpaceManager =
nsJSRuntime::GetNameSpaceManager();
NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsIComponentRegistrar> registrar;
@ -5531,7 +5535,8 @@ private:
{
*aNameStruct = nsnull;
nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
nsScriptNameSpaceManager *nameSpaceManager =
nsJSRuntime::GetNameSpaceManager();
if (!nameSpaceManager) {
NS_ERROR("Can't get namespace manager.");
return NS_ERROR_UNEXPECTED;
@ -5739,7 +5744,8 @@ nsDOMConstructor::HasInstance(nsIXPConnectWrappedNative *wrapper,
return NS_OK;
}
nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
nsScriptNameSpaceManager *nameSpaceManager =
nsJSRuntime::GetNameSpaceManager();
NS_ASSERTION(nameSpaceManager, "Can't get namespace manager?");
const nsIID *class_iid;
@ -6092,7 +6098,8 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
{
*did_resolve = PR_FALSE;
nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
nsScriptNameSpaceManager *nameSpaceManager =
nsJSRuntime::GetNameSpaceManager();
NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
nsDependentJSString name(id);
@ -7031,6 +7038,77 @@ nsLocationSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
}
// DOM Navigator helper
NS_IMETHODIMP
nsNavigatorSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj, jsid id, PRUint32 flags,
JSObject **objp, PRBool *_retval)
{
if (!JSID_IS_STRING(id) || (flags & JSRESOLVE_ASSIGNING)) {
return NS_OK;
}
nsScriptNameSpaceManager *nameSpaceManager =
nsJSRuntime::GetNameSpaceManager();
NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
nsDependentJSString name(id);
const nsGlobalNameStruct *name_struct = nsnull;
nameSpaceManager->LookupNavigatorName(name, &name_struct);
if (!name_struct) {
return NS_OK;
}
NS_ASSERTION(name_struct->mType == nsGlobalNameStruct::eTypeNavigatorProperty,
"unexpected type");
nsresult rv = NS_OK;
nsCOMPtr<nsISupports> native(do_CreateInstance(name_struct->mCID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
jsval prop_val = JSVAL_VOID; // Property value.
nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi(do_QueryInterface(native));
if (gpi) {
JSObject *global = JS_GetGlobalForObject(cx, obj);
nsISupports *globalNative = XPConnect()->GetNativeOfWrapper(cx, global);
nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(globalNative);
if (!window) {
return NS_ERROR_UNEXPECTED;
}
rv = gpi->Init(window, &prop_val);
NS_ENSURE_SUCCESS(rv, rv);
}
if (JSVAL_IS_PRIMITIVE(prop_val)) {
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
rv = WrapNative(cx, obj, native, PR_TRUE, &prop_val,
getter_AddRefs(holder));
NS_ENSURE_SUCCESS(rv, rv);
}
if (!JS_WrapValue(cx, &prop_val)) {
return NS_ERROR_UNEXPECTED;
}
JSBool ok = ::JS_DefinePropertyById(cx, obj, id, prop_val, nsnull, nsnull,
JSPROP_ENUMERATE);
*_retval = PR_TRUE;
*objp = obj;
return ok ? NS_OK : NS_ERROR_FAILURE;
}
// static
nsresult
nsNavigatorSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
JSObject *globalObj, JSObject **parentObj)

View File

@ -481,6 +481,9 @@ protected:
public:
NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx,
JSObject *globalObj, JSObject **parentObj);
NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj, jsid id, PRUint32 flags,
JSObject **objp, PRBool *_retval);
static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
{

View File

@ -51,6 +51,9 @@
#define JAVASCRIPT_GLOBAL_PRIVILEGED_PROPERTY_CATEGORY \
"JavaScript-global-privileged-property"
#define JAVASCRIPT_NAVIGATOR_PROPERTY_CATEGORY \
"JavaScript-navigator-property"
#define JAVASCRIPT_GLOBAL_STATIC_NAMESET_CATEGORY \
"JavaScript-global-static-nameset"

View File

@ -148,18 +148,19 @@ nsScriptNameSpaceManager::~nsScriptNameSpaceManager()
if (mIsInitialized) {
// Destroy the hash
PL_DHashTableFinish(&mGlobalNames);
PL_DHashTableFinish(&mNavigatorNames);
}
MOZ_COUNT_DTOR(nsScriptNameSpaceManager);
}
nsGlobalNameStruct *
nsScriptNameSpaceManager::AddToHash(const char *aKey,
nsScriptNameSpaceManager::AddToHash(PLDHashTable *aTable, const char *aKey,
const PRUnichar **aClassName)
{
NS_ConvertASCIItoUTF16 key(aKey);
GlobalNameMapEntry *entry =
static_cast<GlobalNameMapEntry *>
(PL_DHashTableOperate(&mGlobalNames, &key, PL_DHASH_ADD));
(PL_DHashTableOperate(aTable, &key, PL_DHASH_ADD));
if (!entry) {
return nsnull;
@ -371,7 +372,7 @@ nsScriptNameSpaceManager::RegisterInterface(const char* aIfName,
{
*aFoundOld = PR_FALSE;
nsGlobalNameStruct *s = AddToHash(aIfName);
nsGlobalNameStruct *s = AddToHash(&mGlobalNames, aIfName);
NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
if (s->mType != nsGlobalNameStruct::eTypeNotInitialized) {
@ -408,6 +409,15 @@ nsScriptNameSpaceManager::Init()
GLOBALNAME_HASHTABLE_INITIAL_SIZE);
NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_OUT_OF_MEMORY);
mIsInitialized = PL_DHashTableInit(&mNavigatorNames, &hash_table_ops, nsnull,
sizeof(GlobalNameMapEntry),
GLOBALNAME_HASHTABLE_INITIAL_SIZE);
if (!mIsInitialized) {
PL_DHashTableFinish(&mGlobalNames);
return NS_ERROR_OUT_OF_MEMORY;
}
nsresult rv = NS_OK;
rv = FillHashWithDOMInterfaces();
@ -432,6 +442,9 @@ nsScriptNameSpaceManager::Init()
rv = FillHash(cm, JAVASCRIPT_GLOBAL_DYNAMIC_NAMESET_CATEGORY);
NS_ENSURE_SUCCESS(rv, rv);
rv = FillHash(cm, JAVASCRIPT_NAVIGATOR_PROPERTY_CATEGORY);
NS_ENSURE_SUCCESS(rv, rv);
// Initial filling of the has table has been done.
// Now, listen for changes.
nsCOMPtr<nsIObserverService> serv =
@ -509,6 +522,25 @@ nsScriptNameSpaceManager::LookupName(const nsAString& aName,
return NS_OK;
}
nsresult
nsScriptNameSpaceManager::LookupNavigatorName(const nsAString& aName,
const nsGlobalNameStruct **aNameStruct)
{
GlobalNameMapEntry *entry =
static_cast<GlobalNameMapEntry *>
(PL_DHashTableOperate(&mNavigatorNames, &aName,
PL_DHASH_LOOKUP));
if (PL_DHASH_ENTRY_IS_BUSY(entry) &&
!((&entry->mGlobalName)->mDisabled)) {
*aNameStruct = &entry->mGlobalName;
} else {
*aNameStruct = nsnull;
}
return NS_OK;
}
nsresult
nsScriptNameSpaceManager::RegisterClassName(const char *aClassName,
PRInt32 aDOMClassInfoID,
@ -520,7 +552,7 @@ nsScriptNameSpaceManager::RegisterClassName(const char *aClassName,
NS_ERROR("Trying to register a non-ASCII class name");
return NS_OK;
}
nsGlobalNameStruct *s = AddToHash(aClassName, aResult);
nsGlobalNameStruct *s = AddToHash(&mGlobalNames, aClassName, aResult);
NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
if (s->mType == nsGlobalNameStruct::eTypeClassConstructor) {
@ -555,7 +587,7 @@ nsScriptNameSpaceManager::RegisterClassProto(const char *aClassName,
*aFoundOld = PR_FALSE;
nsGlobalNameStruct *s = AddToHash(aClassName);
nsGlobalNameStruct *s = AddToHash(&mGlobalNames, aClassName);
NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
if (s->mType != nsGlobalNameStruct::eTypeNotInitialized &&
@ -575,7 +607,7 @@ nsresult
nsScriptNameSpaceManager::RegisterExternalClassName(const char *aClassName,
nsCID& aCID)
{
nsGlobalNameStruct *s = AddToHash(aClassName);
nsGlobalNameStruct *s = AddToHash(&mGlobalNames, aClassName);
NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
// If an external constructor is already defined with aClassName we
@ -605,7 +637,7 @@ nsScriptNameSpaceManager::RegisterDOMCIData(const char *aName,
const nsCID *aConstructorCID)
{
const PRUnichar* className;
nsGlobalNameStruct *s = AddToHash(aName, &className);
nsGlobalNameStruct *s = AddToHash(&mGlobalNames, aName, &className);
NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
// If an external constructor is already defined with aClassName we
@ -657,6 +689,8 @@ nsScriptNameSpaceManager::AddCategoryEntryToHash(nsICategoryManager* aCategoryMa
} else if (strcmp(aCategory, JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY) == 0 ||
strcmp(aCategory, JAVASCRIPT_GLOBAL_PRIVILEGED_PROPERTY_CATEGORY) == 0) {
type = nsGlobalNameStruct::eTypeProperty;
} else if (strcmp(aCategory, JAVASCRIPT_NAVIGATOR_PROPERTY_CATEGORY) == 0) {
type = nsGlobalNameStruct::eTypeNavigatorProperty;
} else if (strcmp(aCategory, JAVASCRIPT_GLOBAL_STATIC_NAMESET_CATEGORY) == 0) {
type = nsGlobalNameStruct::eTypeStaticNameSet;
} else if (strcmp(aCategory, JAVASCRIPT_GLOBAL_DYNAMIC_NAMESET_CATEGORY) == 0) {
@ -704,7 +738,7 @@ nsScriptNameSpaceManager::AddCategoryEntryToHash(nsICategoryManager* aCategoryMa
categoryEntry.get(),
getter_Copies(constructorProto));
if (NS_SUCCEEDED(rv)) {
nsGlobalNameStruct *s = AddToHash(categoryEntry.get());
nsGlobalNameStruct *s = AddToHash(&mGlobalNames, categoryEntry.get());
NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
if (s->mType == nsGlobalNameStruct::eTypeNotInitialized) {
@ -722,7 +756,14 @@ nsScriptNameSpaceManager::AddCategoryEntryToHash(nsICategoryManager* aCategoryMa
}
}
nsGlobalNameStruct *s = AddToHash(categoryEntry.get());
PLDHashTable *table;
if (type == nsGlobalNameStruct::eTypeNavigatorProperty) {
table = &mNavigatorNames;
} else {
table = &mGlobalNames;
}
nsGlobalNameStruct *s = AddToHash(table, categoryEntry.get());
NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
if (s->mType == nsGlobalNameStruct::eTypeNotInitialized) {

View File

@ -75,6 +75,7 @@ struct nsGlobalNameStruct
eTypeNotInitialized,
eTypeInterface,
eTypeProperty,
eTypeNavigatorProperty,
eTypeExternalConstructor,
eTypeStaticNameSet,
eTypeDynamicNameSet,
@ -128,6 +129,12 @@ public:
nsresult LookupName(const nsAString& aName,
const nsGlobalNameStruct **aNameStruct,
const PRUnichar **aClassName = nsnull);
// Returns a nsGlobalNameStruct for the navigator property aName, or
// null if one is not found. The returned nsGlobalNameStruct is only
// guaranteed to be valid until the next call to any of the methods
// in this class.
nsresult LookupNavigatorName(const nsAString& aName,
const nsGlobalNameStruct **aNameStruct);
nsresult RegisterClassName(const char *aClassName,
PRInt32 aDOMClassInfoID,
@ -161,7 +168,7 @@ protected:
// that aKey will be mapped to. If mType in the returned
// nsGlobalNameStruct is != eTypeNotInitialized, an entry for aKey
// already existed.
nsGlobalNameStruct *AddToHash(const char *aKey,
nsGlobalNameStruct *AddToHash(PLDHashTable *aTable, const char *aKey,
const PRUnichar **aClassName = nsnull);
nsresult FillHash(nsICategoryManager *aCategoryManager,
@ -184,9 +191,8 @@ protected:
const char* aCategory,
nsISupports* aEntry);
// Inline PLDHashTable, init with PL_DHashTableInit() and delete
// with PL_DHashTableFinish().
PLDHashTable mGlobalNames;
PLDHashTable mNavigatorNames;
PRPackedBool mIsInitialized;
};

View File

@ -985,7 +985,7 @@ PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow)
#ifdef MOZ_WIDGET_GTK2
if (gtk_check_version(2,18,7) != NULL) { // older
if (aWindow.type == NPWindowTypeWindow) {
GdkWindow* socket_window = gdk_window_lookup(aWindow.window);
GdkWindow* socket_window = gdk_window_lookup(static_cast<GdkNativeWindow>(aWindow.window));
if (socket_window) {
// A GdkWindow for the socket already exists. Need to
// workaround https://bugzilla.gnome.org/show_bug.cgi?id=607061
@ -1039,7 +1039,7 @@ PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow)
if (!CreatePluginWindow())
return false;
ReparentPluginWindow((HWND)aWindow.window);
ReparentPluginWindow(reinterpret_cast<HWND>(aWindow.window));
SizePluginWindow(aWindow.width, aWindow.height);
mWindow.window = (void*)mPluginWindowHWND;

View File

@ -604,7 +604,7 @@ PluginInstanceParent::AsyncSetWindow(NPWindow* aWindow)
{
NPRemoteWindow window;
mWindowType = aWindow->type;
window.window = reinterpret_cast<uintptr_t>(aWindow->window);
window.window = reinterpret_cast<uint64_t>(aWindow->window);
window.x = aWindow->x;
window.y = aWindow->y;
window.width = aWindow->width;
@ -911,7 +911,7 @@ PluginInstanceParent::NPP_SetWindow(const NPWindow* aWindow)
else {
SubclassPluginWindow(reinterpret_cast<HWND>(aWindow->window));
window.window = reinterpret_cast<uintptr_t>(aWindow->window);
window.window = reinterpret_cast<uint64_t>(aWindow->window);
window.x = aWindow->x;
window.y = aWindow->y;
window.width = aWindow->width;
@ -919,7 +919,7 @@ PluginInstanceParent::NPP_SetWindow(const NPWindow* aWindow)
window.type = aWindow->type;
}
#else
window.window = reinterpret_cast<unsigned long>(aWindow->window);
window.window = reinterpret_cast<uint64_t>(aWindow->window);
window.x = aWindow->x;
window.y = aWindow->y;
window.width = aWindow->width;

View File

@ -110,7 +110,7 @@ typedef nsCString Buffer;
struct NPRemoteWindow
{
unsigned long window;
uint64_t window;
int32_t x;
int32_t y;
uint32_t width;
@ -363,7 +363,7 @@ struct ParamTraits<mozilla::plugins::NPRemoteWindow>
static void Write(Message* aMsg, const paramType& aParam)
{
aMsg->WriteULong(aParam.window);
aMsg->WriteUInt64(aParam.window);
WriteParam(aMsg, aParam.x);
WriteParam(aMsg, aParam.y);
WriteParam(aMsg, aParam.width);
@ -381,12 +381,12 @@ struct ParamTraits<mozilla::plugins::NPRemoteWindow>
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
{
unsigned long window;
uint64_t window;
int32_t x, y;
uint32_t width, height;
NPRect clipRect;
NPWindowType type;
if (!(aMsg->ReadULong(aIter, &window) &&
if (!(aMsg->ReadUInt64(aIter, &window) &&
ReadParam(aMsg, aIter, &x) &&
ReadParam(aMsg, aIter, &y) &&
ReadParam(aMsg, aIter, &width) &&

View File

@ -137,6 +137,7 @@ _TEST_FILES = \
test_bug620947.html \
test_bug622361.html \
test_bug633133.html \
test_bug641552.html \
test_bug642026.html \
test_bug648465.html \
test_bug654137.html \

View File

@ -18,14 +18,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=597809
SimpleTest.waitForExplicitFinish();
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var cm = Components.classes["@mozilla.org/categorymanager;1"]
.getService(Components.interfaces.nsICategoryManager);
cm.addCategoryEntry("JavaScript-global-property", "testSNSM", "@mozilla.org/embedcomp/prompt-service;1",
SpecialPowers.addCategoryEntry("JavaScript-global-property", "testSNSM", "@mozilla.org/embedcomp/prompt-service;1",
false, true);
SimpleTest.executeSoon(function () {
ok(window.testSNSM, "testSNSM should returns an object");
ok(window.testSNSM, "testSNSM should return an object");
SimpleTest.finish();
});

View File

@ -0,0 +1,42 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=641552
-->
<head>
<title>Test for Bug 641552</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=641552">Mozilla Bug 641552</a>
<p id="display"></p>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 641552 **/
SimpleTest.waitForExplicitFinish();
SpecialPowers.addCategoryEntry("JavaScript-global-property", "randomname", "@mozilla.org/embedcomp/prompt-service;1",
false, true);
SpecialPowers.addCategoryEntry("JavaScript-navigator-property", "randomname1", "@mozilla.org/embedcomp/prompt-service;1",
false, true);
SpecialPowers.addCategoryEntry("JavaScript-navigator-property", "randomname2", "@mozilla.org/embedcomp/prompt-service;1",
false, true);
SimpleTest.executeSoon(function () {
ok(window.randomname, "window.randomname should return an object");
is(typeof(window.navigator.randomname1), 'object', "navigator.randomname1 should return an object");
is(typeof(window.navigator.randomname2), 'object', "navigator.randomname1 should return an object");
SimpleTest.finish();
});
</script>
</pre>
</body>
</html>

View File

@ -10,7 +10,7 @@
#endif
>
<uses-sdk android:minSdkVersion="5"
android:targetSdkVersion="11"/>
android:targetSdkVersion="5"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

View File

@ -155,20 +155,20 @@ void GetPathToBinary(FilePath& exePath)
nsCString path;
greDir->GetNativePath(path);
exePath = FilePath(path.get());
#ifdef OS_MACOSX
// We need to use an App Bundle on OS X so that we can hide
// the dock icon. See Bug 557225.
exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_BUNDLE);
#endif
}
}
}
if (exePath.empty()) {
exePath = FilePath(CommandLine::ForCurrentProcess()->argv()[0]);
exePath = exePath.DirName();
}
#ifdef OS_MACOSX
// We need to use an App Bundle on OS X so that we can hide
// the dock icon. See Bug 557225
exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_BUNDLE);
#endif
exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_NAME);
#endif
}

View File

@ -1116,19 +1116,24 @@ namespace JSC {
static void relinkCall(void* from, void* to)
{
js::JaegerSpew(js::JSpew_Insns,
ISPFX "##linkCall ((from=%p)) ((to=%p))\n",
ISPFX "##relinkCall ((from=%p)) ((to=%p))\n",
from, to);
int disp = ((int)to - (int)from)/4;
*(uint32_t *)((int)from) &= 0x40000000;
*(uint32_t *)((int)from) |= disp & 0x3fffffff;
ExecutableAllocator::cacheFlush(from, 4);
void * where= (void *)((intptr_t)from - 20);
patchPointerInternal(where, (int)to);
ExecutableAllocator::cacheFlush(where, 8);
}
static void linkCall(void* code, JmpSrc where, void* to)
{
void *from = (void *)((intptr_t)code + where.m_offset);
relinkCall(from, to);
js::JaegerSpew(js::JSpew_Insns,
ISPFX "##linkCall ((from=%p)) ((to=%p))\n",
from, to);
int disp = ((int)to - (int)from)/4;
*(uint32_t *)((int)from) &= 0x40000000;
*(uint32_t *)((int)from) |= disp & 0x3fffffff;
ExecutableAllocator::cacheFlush(from, 4);
}
static void linkPointer(void* code, JmpDst where, void* value)

View File

@ -0,0 +1,4 @@
var x = Proxy.create({ fix: function() { return []; } });
Object.__proto__ = x;
Object.freeze(x);
quit();

View File

@ -0,0 +1,7 @@
// |jit-test| error: TypeError
function Integer( value, exception ) {
try { } catch ( e ) { }
new (value = this)( this.value );
if ( Math.floor(value) != value || isNaN(value) ) { }
}
new Integer( 3, false );

View File

@ -0,0 +1,10 @@
// |jit-test| error: TypeError
function f0(p0,p1) {
var v3;
do {
p1 > v3
v3=1.7
} while (((p0[p1][5]==1)||(p0[p1][5]==2)||(p0[p1][5] == 3)) + 0 > p0);
+ (v3(f0));
}
f0(4105,8307);

View File

@ -0,0 +1,18 @@
/* Non-reentrant call on an inner and outer closure. */
function foo() {
var x = 0;
function bar() {
var y = 0;
function baz() {
return ++x + ++y;
}
return baz;
}
return bar();
}
var a = foo();
var b = foo();
assertEq(a() + a() + b() + b(), 12);

View File

@ -0,0 +1,14 @@
/* Non-reentrant closure used in an invoke session. */
var last = null;
var a = [1,2,3,4,5,6,7,8];
var b = a.map(function(x) {
x++;
var res = last ? last() : 0;
last = function() { return x; };
return res;
});
assertEq("" + b, "0,2,3,4,5,6,7,8");

View File

@ -0,0 +1,15 @@
/* Recovering non-reentrant information on singletons after a GC. */
function foo(a) {
return function() {
gc();
var n = 0;
for (var i = 0; i < 20; i++)
n = a++;
assertEq(n, 29);
};
}
var a = foo(10);
var b = foo(20);
a();

View File

@ -0,0 +1,23 @@
test();
function test() {
var catch1, catch2, catch3, finally1, finally2, finally3;
function gen() {
yield 1;
try {
try {
try {
yield 1;
} finally {
test();
}
} catch (e) {
finally2 = true;
}
} catch (e) { }
}
iter = gen();
iter.next();
iter.next();
iter.close();
gc();
}

View File

@ -0,0 +1,17 @@
var gTestcases = new Array();
var gTc = gTestcases.length;
function TestCase(n, d, e, a) {
gTestcases[gTc++] = this;
}
new TestCase("SECTION", "with MyObject, eval should return square of ");
test();
function test() {
for (gTc = 0; gTc < gTestcases.length; gTc++) {
var MYOBJECT = (function isPrototypeOf(message) {
delete input;
})();
with({}) {
gTestcases[gTc].actual = eval("");
}
}
}

View File

@ -0,0 +1,15 @@
function runRichards() {
queue = new Packet;
Packet(queue, ID_DEVICE_A, KIND_DEVICE);
new Packet;
}
var ID_DEVICE_A = 4;
var KIND_DEVICE = 0;
Packet = function (queue) {
this.link = null
if (queue == null) return;
var peek, next = queue;
while ((peek = next.link) != null)
ID_HANDLER_B
};
runRichards()

View File

@ -342,7 +342,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
* any safe point.
*/
if (cx->compartment->debugMode())
usesRval = true;
usesReturnValue_ = true;
isInlineable = true;
if (script->nClosedArgs || script->nClosedVars || script->nfixed >= LOCAL_LIMIT ||
@ -498,7 +498,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
case JSOP_SETRVAL:
case JSOP_POPV:
usesRval = true;
usesReturnValue_ = true;
isInlineable = false;
break;
@ -510,7 +510,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
case JSOP_QNAMEPART:
case JSOP_QNAMECONST:
checkAliasedName(cx, pc);
usesScope = true;
usesScopeChain_ = true;
isInlineable = false;
break;
@ -519,20 +519,34 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
case JSOP_DEFCONST:
case JSOP_SETCONST:
checkAliasedName(cx, pc);
/* FALLTHROUGH */
case JSOP_ENTERWITH:
extendsScope_ = true;
isInlineable = canTrackVars = false;
break;
case JSOP_EVAL:
extendsScope_ = true;
isInlineable = canTrackVars = false;
break;
case JSOP_ENTERWITH:
addsScopeObjects_ = true;
isInlineable = canTrackVars = false;
break;
case JSOP_ENTERBLOCK:
case JSOP_LEAVEBLOCK:
addsScopeObjects_ = true;
isInlineable = false;
break;
case JSOP_THIS:
usesThis = true;
usesThisValue_ = true;
break;
case JSOP_CALL:
case JSOP_NEW:
/* Only consider potentially inlineable calls here. */
hasCalls = true;
hasFunctionCalls_ = true;
break;
case JSOP_TABLESWITCH:
@ -717,7 +731,6 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
/* Additional opcodes which can be compiled but which can't be inlined. */
case JSOP_ARGUMENTS:
case JSOP_EVAL:
case JSOP_THROW:
case JSOP_EXCEPTION:
case JSOP_DEFLOCALFUN:
@ -729,8 +742,6 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
case JSOP_ARGSUB:
case JSOP_ARGCNT:
case JSOP_DEBUGGER:
case JSOP_ENTERBLOCK:
case JSOP_LEAVEBLOCK:
case JSOP_FUNCALL:
case JSOP_FUNAPPLY:
isInlineable = false;

View File

@ -886,15 +886,18 @@ class ScriptAnalysis
/* --------- Bytecode analysis --------- */
bool usesRval;
bool usesScope;
bool usesThis;
bool hasCalls;
bool canTrackVars;
bool isInlineable;
bool usesReturnValue_:1;
bool usesScopeChain_:1;
bool usesThisValue_:1;
bool hasFunctionCalls_:1;
bool modifiesArguments_:1;
bool extendsScope_:1;
bool addsScopeObjects_:1;
bool localsAliasStack_:1;
bool isInlineable:1;
bool canTrackVars:1;
uint32 numReturnSites_;
bool modifiesArguments_;
bool localsAliasStack_;
/* Offsets at which each local becomes unconditionally defined, or a value below. */
uint32 *definedLocals;
@ -928,13 +931,13 @@ class ScriptAnalysis
bool inlineable(uint32 argc) { return isInlineable && argc == script->function()->nargs; }
/* Whether there are POPV/SETRVAL bytecodes which can write to the frame's rval. */
bool usesReturnValue() const { return usesRval; }
bool usesReturnValue() const { return usesReturnValue_; }
/* Whether there are NAME bytecodes which can access the frame's scope chain. */
bool usesScopeChain() const { return usesScope; }
bool usesScopeChain() const { return usesScopeChain_; }
bool usesThisValue() const { return usesThis; }
bool hasFunctionCalls() const { return hasCalls; }
bool usesThisValue() const { return usesThisValue_; }
bool hasFunctionCalls() const { return hasFunctionCalls_; }
uint32 numReturnSites() const { return numReturnSites_; }
/*
@ -943,6 +946,15 @@ class ScriptAnalysis
*/
bool modifiesArguments() { return modifiesArguments_; }
/*
* True if the script may extend declarations in its top level scope with
* dynamic fun/var declarations or through eval.
*/
bool extendsScope() { return extendsScope_; }
/* True if the script may add block or with objects to its scope chain. */
bool addsScopeObjects() { return addsScopeObjects_; }
/*
* True if there are any LOCAL opcodes aliasing values on the stack (above
* script->nfixed).
@ -1168,6 +1180,21 @@ class ScriptAnalysis
return lifetimes[slot];
}
/*
* If a NAME or similar opcode is definitely accessing a particular slot
* of a script this one is nested in, get that script/slot.
*/
struct NameAccess {
JSScript *script;
types::TypeScriptNesting *nesting;
uint32 slot;
/* Decompose the slot above. */
bool arg;
uint32 index;
};
NameAccess resolveNameAccess(JSContext *cx, jsid id, bool addDependency = false);
void printSSA(JSContext *cx);
void printTypes(JSContext *cx);

View File

@ -1488,8 +1488,7 @@ class AutoGCRooter {
DESCRIPTOR = -13, /* js::AutoPropertyDescriptorRooter */
STRING = -14, /* js::AutoStringRooter */
IDVECTOR = -15, /* js::AutoIdVector */
OBJVECTOR = -16, /* js::AutoObjectVector */
TYPE = -17 /* js::types::AutoTypeRooter */
OBJVECTOR = -16 /* js::AutoObjectVector */
};
private:

View File

@ -585,9 +585,26 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
#endif
if (!activeAnalysis) {
#ifdef JS_METHODJIT
if (types.inferenceEnabled)
mjit::ClearAllFrames(this);
#endif
if (activeAnalysis) {
/*
* Clear the analysis pool, but don't releas its data yet. While
* Analysis information is in use, so don't clear the analysis pool.
* jitcode still needs to be released, if this is a shape-regenerating
* GC then shape numbers baked into the code may change.
*/
if (types.inferenceEnabled) {
for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
mjit::ReleaseScriptCode(cx, script);
}
}
} else {
/*
* Clear the analysis pool, but don't release its data yet. While
* sweeping types any live data will be allocated into the pool.
*/
JSArenaPool oldPool;
@ -599,9 +616,6 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
* enabled in the compartment.
*/
if (types.inferenceEnabled) {
#ifdef JS_METHODJIT
mjit::ClearAllFrames(this);
#endif
for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->types) {
@ -615,6 +629,7 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
if (discardScripts) {
script->types->destroy();
script->types = NULL;
script->typesPurged = true;
}
}
}
@ -624,8 +639,7 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->types)
script->types->analysis = NULL;
script->clearAnalysis();
}
/* Reset the analysis pool, releasing all analysis and intermediate type data. */

View File

@ -771,6 +771,17 @@ NewCallObject(JSContext *cx, JSScript *script, JSObject &scopeChain, JSObject *c
size_t slots = JSObject::CALL_RESERVED_SLOTS + argsVars;
gc::AllocKind kind = gc::GetGCObjectKind(slots);
/*
* Make sure that the arguments and variables in the call object all end up
* in a contiguous range of slots. We need this to be able to embed the
* args/vars arrays in the TypeScriptNesting for the function, after the
* call object's frame has finished.
*/
if (cx->typeInferenceEnabled() && gc::GetGCKindSlots(kind) < slots) {
kind = gc::GetGCObjectKind(JSObject::CALL_RESERVED_SLOTS);
JS_ASSERT(gc::GetGCKindSlots(kind) == JSObject::CALL_RESERVED_SLOTS);
}
JSObject *callobj = js_NewGCObject(cx, kind);
if (!callobj)
return NULL;
@ -829,7 +840,7 @@ CreateFunCallObject(JSContext *cx, StackFrame *fp)
* For a named function expression Call's parent points to an environment
* object holding function's name.
*/
if (JSAtom *lambdaName = (fp->fun()->flags & JSFUN_LAMBDA) ? fp->fun()->atom : NULL) {
if (JSAtom *lambdaName = CallObjectLambdaName(fp->fun())) {
scopeChain = NewDeclEnvObject(cx, fp);
if (!scopeChain)
return NULL;
@ -946,6 +957,16 @@ js_PutCallObject(StackFrame *fp)
callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + nargs + e, fp->slots()[e]);
}
}
/*
* Update the args and vars for the active call if this is an outer
* function in a script nesting.
*/
types::TypeScriptNesting *nesting = script->nesting();
if (nesting && script->isOuterFunction) {
nesting->argArray = callobj.callObjArgArray();
nesting->varArray = callobj.callObjVarArray();
}
}
/* Clear private pointers to fp, which is about to go away. */
@ -1028,7 +1049,11 @@ SetCallArg(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
else
obj->setCallObjArg(i, *vp);
JSScript *script = obj->getCallObjCalleeFunction()->script();
JSFunction *fun = obj->getCallObjCalleeFunction();
JSScript *script = fun->script();
if (!script->ensureHasTypes(cx, fun))
return false;
TypeScript::SetArgument(cx, script, i, *vp);
return true;
@ -1095,7 +1120,11 @@ SetCallVar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
else
obj->setCallObjVar(i, *vp);
JSScript *script = obj->getCallObjCalleeFunction()->script();
JSFunction *fun = obj->getCallObjCalleeFunction();
JSScript *script = fun->script();
if (!script->ensureHasTypes(cx, fun))
return false;
TypeScript::SetLocal(cx, script, i, *vp);
return true;
@ -2384,9 +2413,8 @@ js_InitFunctionClass(JSContext *cx, JSObject *obj)
script->code[0] = JSOP_STOP;
script->code[1] = SRC_NULL;
fun->u.i.script = script;
fun->getType(cx)->functionScript = script;
fun->getType(cx)->interpretedFunction = fun;
script->hasFunction = true;
script->where.fun = fun;
script->setOwnerObject(fun);
js_CallNewScriptHook(cx, script, fun);

View File

@ -234,8 +234,8 @@ MarkTypeObject(JSTracer *trc, types::TypeObject *type, const char *name)
if (IS_GC_MARKING_TRACER(trc)) {
if (type->singleton)
MarkObject(trc, *type->singleton, "type_singleton");
if (type->functionScript)
MarkScript(trc, type->functionScript, "functionScript");
if (type->interpretedFunction)
MarkObject(trc, *type->interpretedFunction, "type_function");
}
}
@ -415,7 +415,7 @@ MarkKind(JSTracer *trc, void *thing, JSGCTraceKind kind)
Mark(trc, reinterpret_cast<Shape *>(thing));
break;
case JSTRACE_TYPE_OBJECT:
Mark(trc, reinterpret_cast<types::TypeObject *>(thing));
MarkTypeObject(trc, reinterpret_cast<types::TypeObject *>(thing), "type_stack");
break;
#if JS_HAS_XML_SUPPORT
case JSTRACE_XML:
@ -851,14 +851,15 @@ MarkChildren(JSTracer *trc, JSScript *script)
if (!script->isCachedEval && script->u.object)
MarkObject(trc, *script->u.object, "object");
if (script->hasFunction)
MarkObject(trc, *script->function(), "script_fun");
if (IS_GC_MARKING_TRACER(trc) && script->filename)
js_MarkScriptFilename(script->filename);
script->bindings.trace(trc);
if (script->types)
script->types->trace(trc);
#ifdef JS_METHODJIT
if (script->jitNormal)
script->jitNormal->trace(trc);
@ -913,7 +914,7 @@ ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type)
PushMarkStack(gcmarker, type->proto);
if (type->newScript) {
PushMarkStack(gcmarker, type->newScript->script);
PushMarkStack(gcmarker, type->newScript->fun);
PushMarkStack(gcmarker, type->newScript->shape);
}
@ -953,12 +954,12 @@ MarkChildren(JSTracer *trc, types::TypeObject *type)
MarkObject(trc, *type->singleton, "type_singleton");
if (type->newScript) {
MarkScript(trc, type->newScript->script, "type_new_script");
MarkObject(trc, *type->newScript->fun, "type_new_function");
MarkShape(trc, type->newScript->shape, "type_new_shape");
}
if (type->functionScript)
MarkScript(trc, type->functionScript, "functionScript");
if (type->interpretedFunction)
MarkObject(trc, *type->interpretedFunction, "type_function");
}
#ifdef JS_HAS_XML_SUPPORT

View File

@ -50,9 +50,6 @@
namespace js {
namespace gc {
template<typename T>
void Mark(JSTracer *trc, T *thing);
void
MarkString(JSTracer *trc, JSString *str);

File diff suppressed because it is too large Load Diff

View File

@ -55,6 +55,7 @@ namespace js {
namespace analyze {
class ScriptAnalysis;
}
struct GlobalObject;
}
namespace js {
@ -311,37 +312,40 @@ enum {
OBJECT_FLAG_PROPERTY_COUNT_MASK >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT,
/*
* Whether any objects this represents are not dense arrays. This also
* includes dense arrays whose length property does not fit in an int32.
* Some objects are not dense arrays, or are dense arrays whose length
* property does not fit in an int32.
*/
OBJECT_FLAG_NON_DENSE_ARRAY = 0x010000,
OBJECT_FLAG_NON_DENSE_ARRAY = 0x0010000,
/* Whether any objects this represents are not packed arrays. */
OBJECT_FLAG_NON_PACKED_ARRAY = 0x020000,
OBJECT_FLAG_NON_PACKED_ARRAY = 0x0020000,
/* Whether any objects this represents are not typed arrays. */
OBJECT_FLAG_NON_TYPED_ARRAY = 0x040000,
OBJECT_FLAG_NON_TYPED_ARRAY = 0x0040000,
/* Whether any represented script has had arguments objects created. */
OBJECT_FLAG_CREATED_ARGUMENTS = 0x080000,
OBJECT_FLAG_CREATED_ARGUMENTS = 0x0080000,
/* Whether any represented script is considered uninlineable. */
OBJECT_FLAG_UNINLINEABLE = 0x100000,
OBJECT_FLAG_UNINLINEABLE = 0x0100000,
/* Whether any objects have an equality hook. */
OBJECT_FLAG_SPECIAL_EQUALITY = 0x200000,
OBJECT_FLAG_SPECIAL_EQUALITY = 0x0200000,
/* Whether any objects have been iterated over. */
OBJECT_FLAG_ITERATED = 0x400000,
OBJECT_FLAG_ITERATED = 0x0400000,
/* Outer function which has been marked reentrant. */
OBJECT_FLAG_REENTRANT_FUNCTION = 0x0800000,
/* Flags which indicate dynamic properties of represented objects. */
OBJECT_FLAG_DYNAMIC_MASK = 0x7f0000,
OBJECT_FLAG_DYNAMIC_MASK = 0x0ff0000,
/*
* Whether all properties of this object are considered unknown.
* If set, all flags in DYNAMIC_MASK will also be set.
*/
OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x800000,
OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x1000000,
/* Mask for objects created with unknown properties. */
OBJECT_FLAG_UNKNOWN_MASK =
@ -429,6 +433,12 @@ class TypeSet
bool hasPropagatedProperty() { return !!(flags & TYPE_FLAG_PROPAGATED_PROPERTY); }
void setPropagatedProperty() { flags |= TYPE_FLAG_PROPAGATED_PROPERTY; }
enum FilterKind {
FILTER_ALL_PRIMITIVES,
FILTER_NULL_VOID,
FILTER_VOID
};
/* Add specific kinds of constraints to this set. */
inline void add(JSContext *cx, TypeConstraint *constraint, bool callExisting = true);
void addSubset(JSContext *cx, TypeSet *target);
@ -443,7 +453,7 @@ class TypeSet
void addArith(JSContext *cx, TypeSet *target, TypeSet *other = NULL);
void addTransformThis(JSContext *cx, JSScript *script, TypeSet *target);
void addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type type);
void addFilterPrimitives(JSContext *cx, TypeSet *target, bool onlyNullVoid);
void addFilterPrimitives(JSContext *cx, TypeSet *target, FilterKind filter);
void addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target);
void addLazyArguments(JSContext *cx, TypeSet *target);
@ -500,6 +510,9 @@ class TypeSet
/* Get the single value which can appear in this type set, otherwise NULL. */
JSObject *getSingleton(JSContext *cx, bool freeze = true);
/* Whether all objects in this set are parented to a particular global. */
bool hasGlobalObject(JSContext *cx, JSObject *global);
inline void clearObjects();
private:
@ -644,7 +657,7 @@ struct Property
*/
struct TypeNewScript
{
JSScript *script;
JSFunction *fun;
/* Allocation kind to use for newly constructed objects. */
gc::AllocKind allocKind;
@ -776,8 +789,8 @@ struct TypeObject : gc::Cell
*/
Property **propertySet;
/* If this is an interpreted function, the corresponding script. */
JSScript *functionScript;
/* If this is an interpreted function, the function object. */
JSFunction *interpretedFunction;
inline TypeObject(JSObject *proto, bool isFunction, bool unknown);
@ -825,6 +838,12 @@ struct TypeObject : gc::Cell
/* Set flags on this object which are implied by the specified key. */
inline void setFlagsFromKey(JSContext *cx, JSProtoKey kind);
/*
* Get the global object which all objects of this type are parented to,
* or NULL if there is none known.
*/
inline JSObject *getGlobal();
/* Helpers */
bool addProperty(JSContext *cx, jsid id, Property **pprop);
@ -902,19 +921,131 @@ struct TypeCallsite
bool isNew, unsigned argumentCount);
};
/* Persistent type information for a script, retained across GCs. */
struct TypeScript
/*
* Information attached to outer and inner function scripts nested in one
* another for tracking the reentrance state for outer functions. This state is
* used to generate fast accesses to the args and vars of the outer function.
*
* A function is non-reentrant if, at any point in time, only the most recent
* activation (i.e. call object) is live. An activation is live if either the
* activation is on the stack, or a transitive inner function parented to the
* activation is on the stack.
*
* Because inner functions can be (and, quite often, are) stored in object
* properties and it is difficult to build a fast and robust escape analysis
* to cope with such flow, we detect reentrance dynamically. For the outer
* function, we keep track of the call object for the most recent activation,
* and the number of frames for the function and its inner functions which are
* on the stack.
*
* If the outer function is called while frames associated with a previous
* activation are on the stack, the outer function is reentrant. If an inner
* function is called whose scope does not match the most recent activation,
* the outer function is reentrant.
*
* The situation gets trickier when there are several levels of nesting.
*
* function foo() {
* var a;
* function bar() {
* var b;
* function baz() { return a + b; }
* }
* }
*
* At calls to 'baz', we don't want to do the scope check for the activations
* of both 'foo' and 'bar', but rather 'bar' only. For this to work, a call to
* 'baz' which is a reentrant call on 'foo' must also be a reentrant call on
* 'bar'. When 'foo' is called, we clear the most recent call object for 'bar'.
*/
struct TypeScriptNesting
{
/*
* If this is an inner function, the outer function. If non-NULL, this will
* be the immediate nested parent of the script (even if that parent has
* been marked reentrant). May be NULL even if the script has a nested
* parent, if NAME accesses cannot be tracked into the parent (either the
* script extends its scope with eval() etc., or the parent can make new
* scope chain objects with 'let' or 'with').
*/
JSScript *parent;
/* If this is an outer function, list of inner functions. */
JSScript *children;
/* Link for children list of parent. */
JSScript *next;
/* If this is an outer function, the most recent activation. */
JSObject *activeCall;
/*
* If this is an outer function, pointers to the most recent activation's
* arguments and variables arrays. These could be referring either to stack
* values in activeCall's frame (if it has not finished yet) or to the
* internal slots of activeCall (if the frame has finished). Pointers to
* these fields can be embedded directly in JIT code (though remember to
* use 'addDependency == true' when calling resolveNameAccess).
*/
Value *argArray;
Value *varArray;
/* Number of frames for this function on the stack. */
uint32 activeFrames;
TypeScriptNesting() { PodZero(this); }
~TypeScriptNesting();
};
/* Construct nesting information for script wrt its parent. */
bool CheckScriptNesting(JSContext *cx, JSScript *script);
/* Track nesting state when calling or finishing an outer/inner function. */
void NestingPrologue(JSContext *cx, StackFrame *fp);
void NestingEpilogue(StackFrame *fp);
/* Persistent type information for a script, retained across GCs. */
class TypeScript
{
friend struct ::JSScript;
/* Analysis information for the script, cleared on each GC. */
analyze::ScriptAnalysis *analysis;
/* Function for the script, if it has one. */
JSFunction *function;
/*
* Information about the scope in which a script executes. This information
* is not set until the script has executed at least once and SetScope
* called, before that 'global' will be poisoned per GLOBAL_MISSING_SCOPE.
*/
static const size_t GLOBAL_MISSING_SCOPE = 0x1;
/* Global object for the script, if compileAndGo. */
js::GlobalObject *global;
/* Nesting state for outer or inner function scripts. */
TypeScriptNesting *nesting;
public:
/* Dynamic types generated at points within this script. */
TypeResult *dynamicList;
TypeScript(JSFunction *fun) {
this->function = fun;
this->global = (js::GlobalObject *) GLOBAL_MISSING_SCOPE;
}
bool hasScope() { return size_t(global) != GLOBAL_MISSING_SCOPE; }
/* Array of type type sets for variables and JOF_TYPESET ops. */
TypeSet *typeArray() { return (TypeSet *) (jsuword(this) + sizeof(TypeScript)); }
static inline unsigned NumTypeSets(JSScript *script);
/* Dynamic types generated at points within this script. */
TypeResult *dynamicList;
static bool SetScope(JSContext *cx, JSScript *script, JSObject *scope);
static inline TypeSet *ReturnTypes(JSScript *script);
static inline TypeSet *ThisTypes(JSScript *script);
@ -966,6 +1097,7 @@ struct TypeScript
static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value);
static void Sweep(JSContext *cx, JSScript *script);
inline void trace(JSTracer *trc);
void destroy();
};

View File

@ -42,6 +42,7 @@
#include "jsarray.h"
#include "jsanalyze.h"
#include "jscompartment.h"
#include "jsgcmark.h"
#include "jsinfer.h"
#include "jsprf.h"
#include "vm/GlobalObject.h"
@ -318,10 +319,16 @@ 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);
JSObject *callee = &args.callee();
if (callee->isFunction()) {
JSFunction *fun = callee->getFunctionPrivate();
if (fun->isInterpreted()) {
JSScript *script = fun->script();
if (!script->ensureRanAnalysis(cx, fun, callee->getParent()))
return;
if (cx->typeInferenceEnabled())
TypeMonitorCallSlow(cx, callee, args, constructing);
}
}
}
@ -508,6 +515,7 @@ TypeScript::StandardType(JSContext *cx, JSScript *script, JSProtoKey key)
struct AllocationSiteKey {
JSScript *script;
uint32 offset : 24;
JSProtoKey kind : 8;
@ -601,8 +609,9 @@ TypeScript::MonitorAssign(JSContext *cx, JSScript *script, jsbytecode *pc,
/* static */ inline void
TypeScript::SetThis(JSContext *cx, JSScript *script, Type type)
{
if (!cx->typeInferenceEnabled() || !script->ensureHasTypes(cx))
if (!cx->typeInferenceEnabled())
return;
JS_ASSERT(script->types);
/* Analyze the script regardless if -a was used. */
bool analyze = cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS);
@ -614,7 +623,7 @@ TypeScript::SetThis(JSContext *cx, JSScript *script, Type type)
script->id(), TypeString(type));
ThisTypes(script)->addType(cx, type);
if (analyze)
if (analyze && script->types->hasScope())
script->ensureRanInference(cx);
}
}
@ -629,8 +638,10 @@ TypeScript::SetThis(JSContext *cx, JSScript *script, const js::Value &value)
/* static */ inline void
TypeScript::SetLocal(JSContext *cx, JSScript *script, unsigned local, Type type)
{
if (!cx->typeInferenceEnabled() || !script->ensureHasTypes(cx))
if (!cx->typeInferenceEnabled())
return;
JS_ASSERT(script->types);
if (!LocalTypes(script, local)->hasType(type)) {
AutoEnterTypeInference enter(cx);
@ -652,8 +663,10 @@ TypeScript::SetLocal(JSContext *cx, JSScript *script, unsigned local, const js::
/* static */ inline void
TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type)
{
if (!cx->typeInferenceEnabled() || !script->ensureHasTypes(cx))
if (!cx->typeInferenceEnabled())
return;
JS_ASSERT(script->types);
if (!ArgTypes(script, arg)->hasType(type)) {
AutoEnterTypeInference enter(cx);
@ -672,6 +685,17 @@ TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js:
}
}
void
TypeScript::trace(JSTracer *trc)
{
if (function)
gc::MarkObject(trc, *function, "script_fun");
if (hasScope() && global)
gc::MarkObject(trc, *global, "script_global");
/* Note: nesting does not keep anything alive. */
}
/////////////////////////////////////////////////////////////////////
// TypeCompartment
/////////////////////////////////////////////////////////////////////
@ -1085,6 +1109,7 @@ inline TypeObject::TypeObject(JSObject *proto, bool function, bool unknown)
PodZero(this);
this->proto = proto;
if (function)
flags |= OBJECT_FLAG_FUNCTION;
if (unknown)
@ -1216,23 +1241,33 @@ TypeObject::setFlagsFromKey(JSContext *cx, JSProtoKey key)
setFlags(cx, flags);
}
inline JSObject *
TypeObject::getGlobal()
{
if (singleton)
return singleton->getGlobal();
if (interpretedFunction && interpretedFunction->script()->compileAndGo)
return interpretedFunction->getGlobal();
return NULL;
}
} } /* namespace js::types */
inline bool
JSScript::ensureHasTypes(JSContext *cx)
JSScript::ensureHasTypes(JSContext *cx, JSFunction *fun)
{
return types || makeTypes(cx);
return types || makeTypes(cx, fun);
}
inline bool
JSScript::ensureRanBytecode(JSContext *cx)
JSScript::ensureRanAnalysis(JSContext *cx, JSFunction *fun, JSObject *scope)
{
if (!ensureHasTypes(cx))
if (!ensureHasTypes(cx, fun))
return false;
if (!types->hasScope() && !js::types::TypeScript::SetScope(cx, this, scope))
return false;
if (!hasAnalysis() && !makeAnalysis(cx))
return false;
if (!hasAnalysis()) {
if (!makeAnalysis(cx))
return false;
}
JS_ASSERT(analysis()->ranBytecode());
return true;
}
@ -1240,7 +1275,7 @@ JSScript::ensureRanBytecode(JSContext *cx)
inline bool
JSScript::ensureRanInference(JSContext *cx)
{
if (!ensureRanBytecode(cx))
if (!ensureRanAnalysis(cx))
return false;
if (!analysis()->ranInference()) {
js::types::AutoEnterTypeInference enter(cx);
@ -1262,6 +1297,13 @@ JSScript::analysis()
return types->analysis;
}
inline void
JSScript::clearAnalysis()
{
if (types)
types->analysis = NULL;
}
inline void
js::analyze::ScriptAnalysis::addPushedType(JSContext *cx, uint32 offset, uint32 which,
js::types::Type type)

View File

@ -668,7 +668,7 @@ js::InvokeKernel(JSContext *cx, const CallArgs &argsRef, MaybeConstruct construc
/* Now that the new frame is rooted, maybe create a call object. */
StackFrame *fp = ifg.fp();
if (fun->isHeavyweight() && !CreateFunCallObject(cx, fp))
if (!fp->functionPrologue(cx))
return false;
/* Run function until JSOP_STOP, JSOP_RETURN or error. */
@ -683,6 +683,106 @@ js::InvokeKernel(JSContext *cx, const CallArgs &argsRef, MaybeConstruct construc
return ok;
}
bool
InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &thisv, uintN argc)
{
#ifdef JS_TRACER
if (TRACE_RECORDER(cx))
AbortRecording(cx, "attempt to reenter VM while recording");
LeaveTrace(cx);
#endif
/* Always push arguments, regardless of optimized/normal invoke. */
ContextStack &stack = cx->stack;
if (!stack.pushInvokeArgs(cx, argc, &args_))
return false;
/* Callees may clobber 'this' or 'callee'. */
savedCallee_ = args_.calleev() = calleev;
savedThis_ = args_.thisv() = thisv;
/* If anyone (through jsdbgapi) finds this frame, make it safe. */
MakeRangeGCSafe(args_.argv(), args_.argc());
do {
/* Hoist dynamic checks from scripted Invoke. */
if (!calleev.isObject())
break;
JSObject &callee = calleev.toObject();
if (callee.getClass() != &FunctionClass)
break;
JSFunction *fun = callee.getFunctionPrivate();
if (fun->isNative())
break;
script_ = fun->script();
if (!script_->ensureRanAnalysis(cx, fun, callee.getParent()))
return false;
if (FunctionNeedsPrologue(cx, fun) || script_->isEmpty())
break;
/*
* The frame will remain pushed even when the callee isn't active which
* will affect the observable current global, so avoid any change.
*/
if (callee.getGlobal() != GetGlobalForScopeChain(cx))
break;
/* Push the stack frame once for the session. */
if (!stack.pushInvokeFrame(cx, args_, INITIAL_NONE, &ifg_))
return false;
/*
* Update the 'this' type of the callee according to the value given,
* along with the types of any missing arguments. These will be the
* same across all calls.
*/
TypeScript::SetThis(cx, script_, thisv);
for (unsigned i = argc; i < fun->nargs; i++)
TypeScript::SetArgument(cx, script_, i, types::Type::UndefinedType());
StackFrame *fp = ifg_.fp();
#ifdef JS_METHODJIT
/* Hoist dynamic checks from RunScript. */
mjit::CompileStatus status = mjit::CanMethodJIT(cx, script_, false,
mjit::CompileRequest_JIT);
if (status == mjit::Compile_Error)
return false;
if (status != mjit::Compile_Okay)
break;
/* Cannot also cache the raw code pointer; it can change. */
/* Hoist dynamic checks from CheckStackAndEnterMethodJIT. */
JS_CHECK_RECURSION(cx, return false);
stackLimit_ = stack.space().getStackLimit(cx, REPORT_ERROR);
if (!stackLimit_)
return false;
stop_ = script_->code + script_->length - 1;
JS_ASSERT(*stop_ == JSOP_STOP);
#endif
/* Cached to avoid canonicalActualArg in InvokeSessionGuard::operator[]. */
nformals_ = fp->numFormalArgs();
formals_ = fp->formalArgs();
actuals_ = args_.argv();
JS_ASSERT(actuals_ == fp->actualArgs());
return true;
} while (0);
/*
* Use the normal invoke path.
*
* The callee slot gets overwritten during an unoptimized Invoke, so we
* cache it here and restore it before every Invoke call. The 'this' value
* does not get overwritten, so we can fill it here once.
*/
if (ifg_.pushed())
ifg_.pop();
formals_ = actuals_ = args_.argv();
nformals_ = (unsigned)-1;
return true;
}
bool
js::Invoke(JSContext *cx, const Value &thisv, const Value &fval, uintN argc, Value *argv,
Value *rval)
@ -805,6 +905,9 @@ js::ExecuteKernel(JSContext *cx, JSScript *script, JSObject &scopeChain, const V
Probes::startExecution(cx, script);
if (!script->ensureRanAnalysis(cx, NULL, &scopeChain))
return false;
TypeScript::SetThis(cx, script, fp->thisValue());
AutoPreserveEnumerators preserve(cx);
@ -812,6 +915,9 @@ js::ExecuteKernel(JSContext *cx, JSScript *script, JSObject &scopeChain, const V
if (result && ok)
*result = fp->returnValue();
if (fp->isStrictEvalFrame())
js_PutCallObject(fp);
Probes::stopExecution(cx, script);
return !!ok;
@ -4070,15 +4176,16 @@ BEGIN_CASE(JSOP_FUNAPPLY)
RESTORE_INTERP_VARS();
/* Only create call object after frame is rooted. */
if (fun->isHeavyweight() && !CreateFunCallObject(cx, regs.fp()))
if (!regs.fp()->functionPrologue(cx))
goto error;
RESET_USE_METHODJIT();
TRACE_0(EnterFrame);
bool newType = cx->typeInferenceEnabled() && UseNewType(cx, script, regs.pc);
#ifdef JS_METHODJIT
{
if (!newType) {
/* Try to ensure methods are method JIT'd. */
mjit::CompileRequest request = (interpMode == JSINTERP_NORMAL)
? mjit::CompileRequest_Interpreter
@ -4096,7 +4203,6 @@ BEGIN_CASE(JSOP_FUNAPPLY)
}
#endif
bool newType = cx->typeInferenceEnabled() && UseNewType(cx, script, regs.pc);
if (!ScriptPrologue(cx, regs.fp(), newType))
goto error;

View File

@ -71,6 +71,127 @@ class AutoPreserveEnumerators {
}
};
class InvokeSessionGuard
{
InvokeArgsGuard args_;
InvokeFrameGuard ifg_;
Value savedCallee_, savedThis_;
Value *formals_, *actuals_;
unsigned nformals_;
JSScript *script_;
Value *stackLimit_;
jsbytecode *stop_;
bool optimized() const { return ifg_.pushed(); }
public:
InvokeSessionGuard() : args_(), ifg_() {}
~InvokeSessionGuard() {}
bool start(JSContext *cx, const Value &callee, const Value &thisv, uintN argc);
bool invoke(JSContext *cx);
bool started() const {
return args_.pushed();
}
Value &operator[](unsigned i) const {
JS_ASSERT(i < argc());
Value &arg = i < nformals_ ? formals_[i] : actuals_[i];
JS_ASSERT_IF(optimized(), &arg == &ifg_.fp()->canonicalActualArg(i));
JS_ASSERT_IF(!optimized(), &arg == &args_[i]);
return arg;
}
uintN argc() const {
return args_.argc();
}
const Value &rval() const {
return optimized() ? ifg_.fp()->returnValue() : args_.rval();
}
};
inline bool
InvokeSessionGuard::invoke(JSContext *cx)
{
/* N.B. Must be kept in sync with Invoke */
/* Refer to canonical (callee, this) for optimized() sessions. */
formals_[-2] = savedCallee_;
formals_[-1] = savedThis_;
/* Prevent spurious accessing-callee-after-rval assert. */
args_.calleeHasBeenReset();
if (!optimized())
return Invoke(cx, args_);
/*
* Update the types of each argument. The 'this' type and missing argument
* types were handled when the invoke session was created.
*/
for (unsigned i = 0; i < Min(argc(), nformals_); i++)
types::TypeScript::SetArgument(cx, script_, i, (*this)[i]);
#ifdef JS_METHODJIT
mjit::JITScript *jit = script_->getJIT(false /* !constructing */);
if (!jit) {
/* Watch in case the code was thrown away due a recompile. */
mjit::CompileStatus status = mjit::TryCompile(cx, script_, false);
if (status == mjit::Compile_Error)
return false;
JS_ASSERT(status == mjit::Compile_Okay);
jit = script_->getJIT(false);
}
void *code;
if (!(code = jit->invokeEntry))
return Invoke(cx, args_);
#endif
StackFrame *fp = ifg_.fp();
/*
* Clear any activation objects on the frame. Normally the frame should not
* have any, but since we leave it on the stack between calls to invoke()
* the debugger can start operating on it. See markFunctionEpilogueDone()
* calls below. :XXX: this is pretty gross, and slows us down. Can the
* debugger be prevented from observing this frame?
*/
fp->functionEpilogue(/* activationOnly = */ true);
fp->markFunctionEpilogueDone(/* activationOnly = */ true);
fp->resetCallFrame(script_);
JSBool ok;
{
AutoPreserveEnumerators preserve(cx);
args_.setActive(); /* From js::Invoke(InvokeArgsGuard) overload. */
Probes::enterJSFun(cx, fp->fun(), script_);
#ifdef JS_METHODJIT
ok = mjit::EnterMethodJIT(cx, fp, code, stackLimit_, /* partial = */ false);
cx->regs().pc = stop_;
#else
cx->regs().pc = script_->code;
ok = Interpret(cx, cx->fp());
/* Interpret does not perform the entry frame's epilogue, unlike EnterMethodJIT. */
cx->fp()->functionEpilogue();
#endif
Probes::exitJSFun(cx, fp->fun(), script_);
args_.setInactive();
}
/*
* Clear activation object flags, for the functionEpilogue() call in the
* next invoke().
*/
fp->markFunctionEpilogueDone(/* activationOnly = */ true);
/* Don't clobber callee with rval; rval gets read from fp->rval. */
return ok;
}
namespace detail {
template<typename T> class PrimitiveBehavior { };
@ -229,6 +350,20 @@ ValuePropertyBearer(JSContext *cx, const Value &v, int spindex)
return pobj;
}
inline bool
FunctionNeedsPrologue(JSContext *cx, JSFunction *fun)
{
/* Heavyweight functions need call objects created. */
if (fun->isHeavyweight())
return true;
/* Outer and inner functions need to preserve nesting invariants. */
if (cx->typeInferenceEnabled() && fun->script()->nesting())
return true;
return false;
}
inline bool
ScriptPrologue(JSContext *cx, StackFrame *fp, bool newType)
{

View File

@ -3018,11 +3018,10 @@ CreateThisForFunctionWithType(JSContext *cx, types::TypeObject *type, JSObject *
JSObject *
js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto)
{
JSScript *calleeScript = callee->getFunctionPrivate()->script();
JSObject *res;
if (proto) {
types::TypeObject *type = proto->getNewType(cx, calleeScript);
types::TypeObject *type = proto->getNewType(cx, callee->getFunctionPrivate());
if (!type)
return NULL;
res = CreateThisForFunctionWithType(cx, type, callee->getParent());
@ -3032,7 +3031,7 @@ js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *pro
}
if (res && cx->typeInferenceEnabled())
TypeScript::SetThis(cx, calleeScript, types::Type::ObjectType(res));
TypeScript::SetThis(cx, callee->getFunctionPrivate()->script(), types::Type::ObjectType(res));
return res;
}
@ -3802,6 +3801,10 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &
JS_ASSERT(!a->isDenseArray() && !b->isDenseArray());
JS_ASSERT(!a->isArrayBuffer() && !b->isArrayBuffer());
/* New types for a JSObject need to be stable when trading guts. */
TypeObject *newTypeA = a->newType;
TypeObject *newTypeB = b->newType;
/* Trade the guts of the objects. */
const size_t size = a->structSize();
if (size == b->structSize()) {
@ -3872,6 +3875,9 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &
reserved.newaslots = NULL;
reserved.newbslots = NULL;
}
a->newType = newTypeA;
b->newType = newTypeB;
}
/*
@ -4479,6 +4485,14 @@ JSObject::allocSlots(JSContext *cx, size_t newcap)
bool
JSObject::growSlots(JSContext *cx, size_t newcap)
{
/*
* Slots are only allocated for call objects when new properties are
* added to them, which can only happen while the call is still on the
* stack (and an eval, DEFFUN, etc. happens). We thus do not need to
* worry about updating any active outer function args/vars.
*/
JS_ASSERT_IF(isCall(), maybeCallObjStackFrame() != NULL);
/*
* When an object with CAPACITY_DOUBLING_MAX or fewer slots needs to
* grow, double its capacity, to add N elements in amortized O(N) time.
@ -4545,6 +4559,15 @@ JSObject::growSlots(JSContext *cx, size_t newcap)
void
JSObject::shrinkSlots(JSContext *cx, size_t newcap)
{
/*
* Refuse to shrink slots for call objects. This only happens in a very
* obscure situation (deleting names introduced by a direct 'eval') and
* allowing the slots pointer to change may require updating pointers in
* the function's active args/vars information.
*/
if (isCall())
return;
uint32 oldcap = numSlots();
JS_ASSERT(newcap <= oldcap);
JS_ASSERT(newcap >= slotSpan());

View File

@ -733,6 +733,7 @@ struct JSObject : js::gc::Cell {
private:
inline js::Value* fixedSlots() const;
inline bool hasContiguousSlots(size_t start, size_t count) const;
public:
/* Minimum size for dynamically allocated slots. */
@ -793,12 +794,22 @@ struct JSObject : js::gc::Cell {
void rollbackProperties(JSContext *cx, uint32 slotSpan);
js::Value& getSlotRef(uintN slot) {
JS_ASSERT(slot < capacity);
js::Value *getSlotAddress(uintN slot) {
/*
* This can be used to get the address of the end of the slots for the
* object, which may be necessary when fetching zero-length arrays of
* slots (e.g. for callObjVarArray).
*/
JS_ASSERT(slot <= capacity);
size_t fixed = numFixedSlots();
if (slot < fixed)
return fixedSlots()[slot];
return slots[slot - fixed];
return fixedSlots() + slot;
return slots + (slot - fixed);
}
js::Value &getSlotRef(uintN slot) {
JS_ASSERT(slot < capacity);
return *getSlotAddress(slot);
}
inline js::Value &nativeGetSlotRef(uintN slot);
@ -888,10 +899,10 @@ struct JSObject : js::gc::Cell {
inline void clearType();
inline void setType(js::types::TypeObject *newType);
inline js::types::TypeObject *getNewType(JSContext *cx, JSScript *script = NULL,
inline js::types::TypeObject *getNewType(JSContext *cx, JSFunction *fun = NULL,
bool markUnknown = false);
private:
void makeNewType(JSContext *cx, JSScript *script, bool markUnknown);
void makeNewType(JSContext *cx, JSFunction *fun, bool markUnknown);
public:
/* Set a new prototype for an object with a singleton type. */
@ -1098,6 +1109,14 @@ struct JSObject : js::gc::Cell {
inline const js::Value &callObjVar(uintN i) const;
inline void setCallObjVar(uintN i, const js::Value &v);
/*
* Get the actual arrays of arguments and variables. Only call if type
* inference is enabled, where we ensure that call object variables are in
* contiguous slots (see NewCallObject).
*/
inline js::Value *callObjArgArray();
inline js::Value *callObjVarArray();
/*
* Date-specific getters and setters.
*/

View File

@ -143,7 +143,7 @@ JSObject::getProperty(JSContext *cx, JSObject *receiver, jsid id, js::Value *vp)
} else {
if (!js_GetProperty(cx, this, receiver, id, vp))
return false;
JS_ASSERT_IF(!hasSingletonType(),
JS_ASSERT_IF(!hasSingletonType() && nativeContains(js_CheckForStringIndex(id)),
js::types::TypeHasProperty(cx, type(), id, *vp));
}
return true;
@ -404,6 +404,17 @@ JSObject::hasSlotsArray() const
return slots && slots != fixedSlots();
}
inline bool
JSObject::hasContiguousSlots(size_t start, size_t count) const
{
/*
* Check that the range [start, start+count) is either all inline or all
* out of line.
*/
JS_ASSERT(start + count <= numSlots());
return (start + count <= numFixedSlots()) || (start >= numFixedSlots());
}
inline size_t
JSObject::structSize() const
{
@ -600,6 +611,14 @@ JSObject::setCallObjArg(uintN i, const js::Value &v)
setSlot(JSObject::CALL_RESERVED_SLOTS + i, v);
}
inline js::Value *
JSObject::callObjArgArray()
{
js::DebugOnly<JSFunction*> fun = getCallObjCalleeFunction();
JS_ASSERT(hasContiguousSlots(JSObject::CALL_RESERVED_SLOTS, fun->nargs));
return getSlotAddress(JSObject::CALL_RESERVED_SLOTS);
}
inline const js::Value &
JSObject::callObjVar(uintN i) const
{
@ -618,6 +637,29 @@ JSObject::setCallObjVar(uintN i, const js::Value &v)
setSlot(JSObject::CALL_RESERVED_SLOTS + fun->nargs + i, v);
}
inline js::Value *
JSObject::callObjVarArray()
{
JSFunction *fun = getCallObjCalleeFunction();
JS_ASSERT(hasContiguousSlots(JSObject::CALL_RESERVED_SLOTS + fun->nargs,
fun->script()->bindings.countVars()));
return getSlotAddress(JSObject::CALL_RESERVED_SLOTS + fun->nargs);
}
namespace js {
/*
* Any name atom for a function which will be added as a DeclEnv object to the
* scope chain above call objects for fun.
*/
static inline JSAtom *
CallObjectLambdaName(JSFunction *fun)
{
return (fun->flags & JSFUN_LAMBDA) ? fun->atom : NULL;
}
} /* namespace js */
inline const js::Value &
JSObject::getDateUTCTime() const
{
@ -852,7 +894,7 @@ JSObject::getType(JSContext *cx)
}
inline js::types::TypeObject *
JSObject::getNewType(JSContext *cx, JSScript *script, bool markUnknown)
JSObject::getNewType(JSContext *cx, JSFunction *fun, bool markUnknown)
{
if (isDenseArray() && !makeDenseArraySlow(cx))
return NULL;
@ -868,12 +910,12 @@ JSObject::getNewType(JSContext *cx, JSScript *script, bool markUnknown)
* Object.create is called with a prototype object that is also the
* 'prototype' property of some scripted function.
*/
if (newType->newScript && newType->newScript->script != script)
if (newType->newScript && newType->newScript->fun != fun)
newType->clearNewScript(cx);
if (markUnknown && cx->typeInferenceEnabled() && !newType->unknownProperties())
newType->markUnknown(cx);
} else {
makeNewType(cx, script, markUnknown);
makeNewType(cx, fun, markUnknown);
}
return newType;
}

View File

@ -359,9 +359,35 @@ js_DumpScript(JSContext *cx, JSScript *script)
return ok;
}
static char *
QuoteString(Sprinter *sp, JSString *str, uint32 quote);
static bool
ToDisassemblySource(JSContext *cx, jsval v, JSAutoByteString *bytes)
{
if (JSVAL_IS_STRING(v)) {
Sprinter sprinter;
void *mark = JS_ARENA_MARK(&cx->tempPool);
INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
char *nbytes = QuoteString(&sprinter, JSVAL_TO_STRING(v), '"');
if (!nbytes)
return false;
nbytes = JS_sprintf_append(NULL, "%s", nbytes);
JS_ARENA_RELEASE(&cx->tempPool, mark);
if (!nbytes)
return false;
bytes->initBytes(nbytes);
return true;
}
if (cx->runtime->gcRunning || JS_THREAD_DATA(cx)->noGCOrAllocationCheck) {
char *source = JS_sprintf_append(NULL, "<value>");
if (!source)
return false;
bytes->initBytes(source);
return true;
}
if (!JSVAL_IS_PRIMITIVE(v)) {
JSObject *obj = JSVAL_TO_OBJECT(v);
Class *clasp = obj->getClass();

View File

@ -1126,9 +1126,6 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerF
bool
Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *script)
{
if (!globalScope.defs.length())
return true;
JSObject *globalObj = globalScope.globalObj;
/* Define and update global properties. */
@ -1181,18 +1178,29 @@ Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *scrip
* object.
*/
while (worklist.length()) {
JSScript *inner = worklist.back();
JSScript *outer = worklist.back();
worklist.popBack();
if (JSScript::isValidOffset(inner->objectsOffset)) {
JSObjectArray *arr = inner->objects();
for (size_t i = 0; i < arr->length; i++) {
if (JSScript::isValidOffset(outer->objectsOffset)) {
JSObjectArray *arr = outer->objects();
/*
* If this is an eval script, don't treat the saved caller function
* stored in the first object slot as an inner function.
*/
size_t start = outer->savedCallerFun ? 1 : 0;
for (size_t i = start; i < arr->length; i++) {
JSObject *obj = arr->vector[i];
if (!obj->isFunction())
continue;
JSFunction *fun = obj->getFunctionPrivate();
JS_ASSERT(fun->isInterpreted());
JSScript *inner = fun->script();
if (outer->isHeavyweightFunction) {
outer->isOuterFunction = true;
inner->isInnerFunction = true;
}
if (!JSScript::isValidOffset(inner->globalsOffset) &&
!JSScript::isValidOffset(inner->objectsOffset)) {
continue;
@ -1202,10 +1210,10 @@ Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *scrip
}
}
if (!JSScript::isValidOffset(inner->globalsOffset))
if (!JSScript::isValidOffset(outer->globalsOffset))
continue;
GlobalSlotArray *globalUses = inner->globals();
GlobalSlotArray *globalUses = outer->globals();
uint32 nGlobalUses = globalUses->length;
for (uint32 i = 0; i < nGlobalUses; i++) {
uint32 index = globalUses->vector[i].slot;

View File

@ -1212,15 +1212,6 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
cg->upvarMap.clear();
}
/* Set global for compileAndGo scripts. */
if (script->compileAndGo) {
GlobalScope *globalScope = cg->compiler()->globalScope;
if (globalScope->globalObj && globalScope->globalObj->isGlobal())
script->where.global = globalScope->globalObj->asGlobal();
else if (cx->globalObject->isGlobal())
script->where.global = cx->globalObject->asGlobal();
}
if (cg->globalUses.length()) {
memcpy(script->globals()->vector, &cg->globalUses[0],
cg->globalUses.length() * sizeof(GlobalSlotArray::Entry));

View File

@ -511,12 +511,18 @@ struct JSScript : public js::gc::Cell {
undefined properties in this
script */
bool hasSingletons:1; /* script has singleton objects */
bool hasFunction:1; /* function is active in 'where' union */
bool hasFunction:1; /* script has an associated function */
bool isHeavyweightFunction:1; /* function is heavyweight */
bool isOuterFunction:1; /* function is heavyweight, with inner functions */
bool isInnerFunction:1; /* function is directly nested in a heavyweight
* outer function */
bool isActiveEval:1; /* script came from eval(), and is still active */
bool isCachedEval:1; /* script came from eval(), and is in eval cache */
bool usedLazyArgs:1; /* script has used lazy arguments at some point */
bool createdArgs:1; /* script has had arguments objects created */
bool uninlineable:1; /* script is considered uninlineable by analysis */
bool reentrantOuterFunction:1; /* outer function marked reentrant */
bool typesPurged:1; /* TypeScript has been purged at some point */
#ifdef JS_METHODJIT
bool debugMode:1; /* script was compiled in debug mode */
bool failedBoundsCheck:1; /* script has had hoisted bounds checks fail */
@ -535,8 +541,10 @@ struct JSScript : public js::gc::Cell {
* the script with 4 bytes. We use them to store tiny scripts like empty
* scripts.
*/
#if JS_BITS_PER_WORD == 64
#define JS_SCRIPT_INLINE_DATA_LIMIT 4
uint8 inlineData[JS_SCRIPT_INLINE_DATA_LIMIT];
#endif
const char *filename; /* source filename or null */
JSAtom **atoms; /* maps immediate index to literal struct */
@ -572,19 +580,6 @@ struct JSScript : public js::gc::Cell {
/* array of execution counters for every JSOp in the script, by runmode */
JSPCCounters pcCounters;
union {
/* Function this script is the body for, if there is one. */
JSFunction *fun;
/* Global object for this script, if compileAndGo. */
js::GlobalObject *global;
} where;
inline JSFunction *function() const {
JS_ASSERT(hasFunction);
return where.fun;
}
#ifdef JS_CRASH_DIAGNOSTICS
JSObject *ownerObject;
@ -594,17 +589,6 @@ struct JSScript : public js::gc::Cell {
void setOwnerObject(JSObject *owner);
/*
* Associates this script with a specific function, constructing a new type
* object for the function.
*/
bool typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton = false);
inline bool hasGlobal() const;
inline js::GlobalObject *global() const;
inline bool hasClearedGlobal() const;
#ifdef DEBUG
/*
* Unique identifier within the compartment for this script, used for
@ -620,17 +604,41 @@ struct JSScript : public js::gc::Cell {
/* Persistent type information retained across GCs. */
js::types::TypeScript *types;
/* Ensure the script has types, bytecode and/or type inference results. */
inline bool ensureHasTypes(JSContext *cx);
inline bool ensureRanBytecode(JSContext *cx);
/* Ensure the script has a TypeScript. */
inline bool ensureHasTypes(JSContext *cx, JSFunction *fun = NULL);
/*
* Ensure the script has scope and bytecode analysis information.
* Performed when the script first runs, or first runs after a TypeScript
* GC purge. If fun/scope are NULL then the script must already have types
* with scope information.
*/
inline bool ensureRanAnalysis(JSContext *cx, JSFunction *fun = NULL, JSObject *scope = NULL);
/* Ensure the script has type inference analysis information. */
inline bool ensureRanInference(JSContext *cx);
/* Filled in by one of the above. */
inline bool hasAnalysis();
inline void clearAnalysis();
inline js::analyze::ScriptAnalysis *analysis();
/*
* Associates this script with a specific function, constructing a new type
* object for the function if necessary.
*/
bool typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton = false);
inline bool hasGlobal() const;
inline bool hasClearedGlobal() const;
inline JSFunction *function() const;
inline js::GlobalObject *global() const;
inline js::types::TypeScriptNesting *nesting() const;
inline void clearNesting();
private:
bool makeTypes(JSContext *cx);
bool makeTypes(JSContext *cx, JSFunction *fun);
bool makeAnalysis(JSContext *cx);
public:

View File

@ -177,9 +177,8 @@ JSScript::hasGlobal() const
* which have had their scopes cleared. compileAndGo code should not run
* anymore against such globals.
*/
if (!compileAndGo)
return false;
js::GlobalObject *obj = hasFunction ? function()->getGlobal() : where.global;
JS_ASSERT(types && types->hasScope());
js::GlobalObject *obj = types->global;
return obj && !obj->isCleared();
}
@ -187,16 +186,39 @@ inline js::GlobalObject *
JSScript::global() const
{
JS_ASSERT(hasGlobal());
return hasFunction ? function()->getGlobal() : where.global;
return types->global;
}
inline bool
JSScript::hasClearedGlobal() const
{
if (!compileAndGo)
return false;
js::GlobalObject *obj = hasFunction ? function()->getGlobal() : where.global;
JS_ASSERT(types && types->hasScope());
js::GlobalObject *obj = types->global;
return obj && obj->isCleared();
}
inline JSFunction *
JSScript::function() const
{
JS_ASSERT(hasFunction && types);
return types->function;
}
inline js::types::TypeScriptNesting *
JSScript::nesting() const
{
JS_ASSERT(hasFunction && types && types->hasScope());
return types->nesting;
}
inline void
JSScript::clearNesting()
{
js::types::TypeScriptNesting *nesting = this->nesting();
if (nesting) {
js::Foreground::delete_(nesting);
types->nesting = NULL;
}
}
#endif /* jsscriptinlines_h___ */

View File

@ -133,6 +133,7 @@ mjit::Compiler::Compiler(JSContext *cx, JSScript *outerScript, bool isConstructi
inlining_(false),
hasGlobalReallocation(false),
oomInVector(false),
gcNumber(cx->runtime->gcNumber),
applyTricks(NoApplyTricks),
pcLengths(NULL)
{
@ -188,7 +189,7 @@ mjit::Compiler::checkAnalysis(JSScript *script)
return Compile_Abort;
}
if (!script->ensureRanBytecode(cx))
if (!script->ensureRanAnalysis(cx))
return Compile_Error;
if (cx->typeInferenceEnabled() && !script->ensureRanInference(cx))
return Compile_Error;
@ -338,6 +339,11 @@ mjit::Compiler::scanInlineCalls(uint32 index, uint32 depth)
break;
}
if (!script->types || !script->types->hasScope()) {
okay = false;
break;
}
CompileStatus status = checkAnalysis(script);
if (status != Compile_Okay)
return status;
@ -742,27 +748,60 @@ mjit::Compiler::generatePrologue()
}
}
/* Create the call object. */
types::TypeScriptNesting *nesting = script->nesting();
/*
* Run the function prologue if necessary. This is always done in a
* stub for heavyweight functions (including nesting outer functions).
*/
JS_ASSERT_IF(nesting && nesting->children, script->function()->isHeavyweight());
if (script->function()->isHeavyweight()) {
prepareStubCall(Uses(0));
INLINE_STUBCALL(stubs::CreateFunCallObject, REJOIN_CREATE_CALL_OBJECT);
}
j.linkTo(masm.label(), &masm);
if (analysis->usesScopeChain() && !script->function()->isHeavyweight()) {
INLINE_STUBCALL(stubs::FunctionFramePrologue, REJOIN_FUNCTION_PROLOGUE);
} else {
/*
* Load the scope chain into the frame if necessary. The scope chain
* is always set for global and eval frames, and will have been set by
* Load the scope chain into the frame if it will be needed by NAME
* opcodes or by the nesting prologue below. The scope chain is
* always set for global and eval frames, and will have been set by
* CreateFunCallObject for heavyweight function frames.
*/
RegisterID t0 = Registers::ReturnReg;
Jump hasScope = masm.branchTest32(Assembler::NonZero,
FrameFlagsAddress(), Imm32(StackFrame::HAS_SCOPECHAIN));
masm.loadPayload(Address(JSFrameReg, StackFrame::offsetOfCallee(script->function())), t0);
masm.loadPtr(Address(t0, offsetof(JSObject, parent)), t0);
masm.storePtr(t0, Address(JSFrameReg, StackFrame::offsetOfScopeChain()));
hasScope.linkTo(masm.label(), &masm);
if (analysis->usesScopeChain() || nesting) {
RegisterID t0 = Registers::ReturnReg;
Jump hasScope = masm.branchTest32(Assembler::NonZero,
FrameFlagsAddress(), Imm32(StackFrame::HAS_SCOPECHAIN));
masm.loadPayload(Address(JSFrameReg, StackFrame::offsetOfCallee(script->function())), t0);
masm.loadPtr(Address(t0, offsetof(JSObject, parent)), t0);
masm.storePtr(t0, Address(JSFrameReg, StackFrame::offsetOfScopeChain()));
hasScope.linkTo(masm.label(), &masm);
}
if (nesting) {
/*
* Inline the common case for the nesting prologue: the
* function is a non-heavyweight inner function with no
* children of its own. We ensure during inference that the
* outer function does not add scope objects for 'let' or
* 'with', so that the frame's scope chain will be
* the parent's call object, and if it differs from the
* parent's current activation then the parent is reentrant.
*/
JSScript *parent = nesting->parent;
JS_ASSERT(parent);
JS_ASSERT_IF(parent->hasAnalysis() && parent->analysis()->ranBytecode(),
!parent->analysis()->addsScopeObjects());
RegisterID t0 = Registers::ReturnReg;
masm.move(ImmPtr(&parent->nesting()->activeCall), t0);
masm.loadPtr(Address(t0), t0);
Address scopeChain(JSFrameReg, StackFrame::offsetOfScopeChain());
Jump mismatch = masm.branchPtr(Assembler::NotEqual, t0, scopeChain);
masm.add32(Imm32(1), AbsoluteAddress(&nesting->activeFrames));
stubcc.linkExitDirect(mismatch, stubcc.masm.label());
OOL_STUBCALL(stubs::FunctionFramePrologue, REJOIN_FUNCTION_PROLOGUE);
stubcc.crossJump(stubcc.masm.jump(), masm.label());
}
}
if (outerScript->usesArguments && !script->function()->isHeavyweight()) {
@ -780,6 +819,8 @@ mjit::Compiler::generatePrologue()
Address(JSFrameReg, StackFrame::offsetOfArgs()));
hasArgs.linkTo(masm.label(), &masm);
}
j.linkTo(masm.label(), &masm);
}
if (cx->typeInferenceEnabled()) {
@ -846,6 +887,13 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
if (globalSlots && globalObj->getRawSlots() != globalSlots)
return Compile_Retry;
/*
* Watch for GCs which occurred during compilation. These may have
* renumbered shapes baked into the jitcode.
*/
if (cx->runtime->gcNumber != gcNumber)
return Compile_Retry;
for (size_t i = 0; i < branchPatches.length(); i++) {
Label label = labelOf(branchPatches[i].pc, branchPatches[i].inlineIndex);
branchPatches[i].jump.linkTo(label, &masm);
@ -2701,9 +2749,8 @@ mjit::Compiler::generateMethod()
END_CASE(JSOP_UNBRAND)
BEGIN_CASE(JSOP_UNBRANDTHIS)
jsop_this();
jsop_unbrand();
frame.pop();
prepareStubCall(Uses(1));
INLINE_STUBCALL(stubs::UnbrandThis, REJOIN_FALLTHROUGH);
END_CASE(JSOP_UNBRANDTHIS)
BEGIN_CASE(JSOP_GETGLOBAL)
@ -3112,22 +3159,31 @@ mjit::Compiler::emitReturn(FrameEntry *fe)
* even on the entry frame. To avoid double-putting, EnterMethodJIT clears
* out the entry frame's activation objects.
*/
if (script->hasFunction && script->function()->isHeavyweight()) {
/* There will always be a call object. */
prepareStubCall(Uses(fe ? 1 : 0));
INLINE_STUBCALL(stubs::PutActivationObjects, REJOIN_NONE);
} else {
/* if (hasCallObj() || hasArgsObj()) */
Jump putObjs = masm.branchTest32(Assembler::NonZero,
Address(JSFrameReg, StackFrame::offsetOfFlags()),
Imm32(StackFrame::HAS_CALL_OBJ | StackFrame::HAS_ARGS_OBJ));
stubcc.linkExit(putObjs, Uses(frame.frameSlots()));
if (script->hasFunction) {
types::TypeScriptNesting *nesting = script->nesting();
if (script->function()->isHeavyweight() || (nesting && nesting->children)) {
prepareStubCall(Uses(fe ? 1 : 0));
INLINE_STUBCALL(stubs::FunctionFrameEpilogue, REJOIN_NONE);
} else {
/* if (hasCallObj() || hasArgsObj()) */
Jump putObjs = masm.branchTest32(Assembler::NonZero,
Address(JSFrameReg, StackFrame::offsetOfFlags()),
Imm32(StackFrame::HAS_CALL_OBJ | StackFrame::HAS_ARGS_OBJ));
stubcc.linkExit(putObjs, Uses(frame.frameSlots()));
stubcc.leave();
OOL_STUBCALL(stubs::PutActivationObjects, REJOIN_NONE);
stubcc.leave();
OOL_STUBCALL(stubs::FunctionFrameEpilogue, REJOIN_NONE);
emitReturnValue(&stubcc.masm, fe);
emitFinalReturn(stubcc.masm);
emitReturnValue(&stubcc.masm, fe);
emitFinalReturn(stubcc.masm);
/*
* Do frame count balancing inline for inner functions in a nesting
* with no children of their own.
*/
if (nesting)
masm.sub32(Imm32(1), AbsoluteAddress(&nesting->activeFrames));
}
}
emitReturnValue(&masm, fe);
@ -5039,6 +5095,22 @@ mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache, bool popGuaranteed
return true;
}
/*
* If this is a SETNAME to a variable of a non-reentrant outer function,
* set the variable's slot directly for the active call object.
*/
if (cx->typeInferenceEnabled() && js_CodeSpec[*PC].format & JOF_NAME) {
ScriptAnalysis::NameAccess access =
analysis->resolveNameAccess(cx, ATOM_TO_JSID(atom), true);
if (access.nesting) {
Address address = frame.loadNameAddress(access);
frame.storeTo(rhs, address, popGuaranteed);
frame.shimmy(1);
frame.freeReg(address.base);
return true;
}
}
/*
* Set the property directly if we are accessing a known object which
* always has the property in a particular inline slot.
@ -5201,6 +5273,26 @@ mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache, bool popGuaranteed
void
mjit::Compiler::jsop_name(JSAtom *atom, JSValueType type, bool isCall)
{
/*
* If this is a NAME for a variable of a non-reentrant outer function, get
* the variable's slot directly for the active call object. We always need
* to check for undefined, however.
*/
if (cx->typeInferenceEnabled()) {
ScriptAnalysis::NameAccess access =
analysis->resolveNameAccess(cx, ATOM_TO_JSID(atom), true);
if (access.nesting) {
Address address = frame.loadNameAddress(access);
JSValueType type = knownPushedType(0);
BarrierState barrier = pushAddressMaybeBarrier(address, type, true,
/* testUndefined = */ true);
finishBarrier(barrier, REJOIN_GETTER, 0);
if (isCall)
jsop_callgname_epilogue();
return;
}
}
PICGenInfo pic(isCall ? ic::PICInfo::CALLNAME : ic::PICInfo::NAME, JSOp(*PC), true);
RESERVE_IC_SPACE(masm);
@ -5258,6 +5350,24 @@ mjit::Compiler::jsop_name(JSAtom *atom, JSValueType type, bool isCall)
bool
mjit::Compiler::jsop_xname(JSAtom *atom)
{
/*
* If this is a GETXPROP for a variable of a non-reentrant outer function,
* treat in the same way as a NAME.
*/
if (cx->typeInferenceEnabled()) {
ScriptAnalysis::NameAccess access =
analysis->resolveNameAccess(cx, ATOM_TO_JSID(atom), true);
if (access.nesting) {
frame.pop();
Address address = frame.loadNameAddress(access);
JSValueType type = knownPushedType(0);
BarrierState barrier = pushAddressMaybeBarrier(address, type, true,
/* testUndefined = */ true);
finishBarrier(barrier, REJOIN_GETTER, 0);
return true;
}
}
PICGenInfo pic(ic::PICInfo::XNAME, JSOp(*PC), true);
FrameEntry *fe = frame.peek(-1);
@ -5317,6 +5427,23 @@ mjit::Compiler::jsop_xname(JSAtom *atom)
void
mjit::Compiler::jsop_bindname(JSAtom *atom, bool usePropCache)
{
/*
* If this is a BINDNAME for a variable of a non-reentrant outer function,
* the object is definitely the outer function's active call object.
*/
if (cx->typeInferenceEnabled()) {
ScriptAnalysis::NameAccess access =
analysis->resolveNameAccess(cx, ATOM_TO_JSID(atom), true);
if (access.nesting) {
RegisterID reg = frame.allocReg();
JSObject **pobj = &access.nesting->activeCall;
masm.move(ImmPtr(pobj), reg);
masm.loadPtr(Address(reg), reg);
frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg);
return;
}
}
PICGenInfo pic(ic::PICInfo::BIND, JSOp(*PC), usePropCache);
// This code does not check the frame flags to see if scopeChain has been
@ -5458,6 +5585,16 @@ mjit::Compiler::jsop_this()
stubcc.rejoin(Changes(1));
}
/*
* Watch out for an obscure case where we don't know we are pushing
* an object: the script has not yet had a 'this' value assigned,
* so no pushed 'this' type has been inferred. Don't mark the type
* as known in this case, preserving the invariant that compiler
* types reflect inferred types.
*/
if (cx->typeInferenceEnabled() && knownPushedType(0) != JSVAL_TYPE_OBJECT)
return;
// Now we know that |this| is an object.
frame.pop();
frame.learnThisIsObject(type != JSVAL_TYPE_OBJECT);
@ -5908,7 +6045,7 @@ mjit::Compiler::jsop_callgname_epilogue()
/* Paths for known object callee. */
if (fval->isConstant()) {
JSObject *obj = &fval->getValue().toObject();
if (obj->getParent() == globalObj) {
if (obj->getGlobal() == globalObj) {
frame.push(UndefinedValue());
} else {
prepareStubCall(Uses(1));
@ -5918,6 +6055,20 @@ mjit::Compiler::jsop_callgname_epilogue()
return;
}
/*
* Fast path for functions whose global is statically known to be the
* current global. This is primarily for calls on inner functions within
* nestings, whose direct parent is a call object rather than the global
* and which will make a stub call in the path below.
*/
if (cx->typeInferenceEnabled()) {
types::TypeSet *types = analysis->pushedTypes(PC, 0);
if (types->hasGlobalObject(cx, globalObj)) {
frame.push(UndefinedValue());
return;
}
}
/*
* Optimized version. This inlines the common case, calling a
* (non-proxied) function that has the same global as the current
@ -6942,7 +7093,7 @@ mjit::Compiler::fixDoubleTypes(jsbytecode *target)
} else {
JS_ASSERT(vt.type == JSVAL_TYPE_DOUBLE);
}
} else if (fe->isType(JSVAL_TYPE_DOUBLE)) {
} else if (vt.type == JSVAL_TYPE_DOUBLE) {
fixedDoubleToAnyEntries.append(newv->slot);
frame.syncAndForgetFe(fe);
frame.forgetLoopReg(fe);

View File

@ -468,6 +468,7 @@ class Compiler : public BaseCompiler
bool inlining_;
bool hasGlobalReallocation;
bool oomInVector; // True if we have OOM'd appending to a vector.
uint32 gcNumber;
enum { NoApplyTricks, LazyArgsObj } applyTricks;
PCLengthEntry *pcLengths;

View File

@ -878,6 +878,19 @@ FrameState::syncAndForgetFe(FrameEntry *fe, bool markSynced)
fe->data.setMemory();
}
inline JSC::MacroAssembler::Address
FrameState::loadNameAddress(const analyze::ScriptAnalysis::NameAccess &access)
{
JS_ASSERT(access.script && access.nesting);
RegisterID reg = allocReg();
Value **pbase = access.arg ? &access.nesting->argArray : &access.nesting->varArray;
masm.move(ImmPtr(pbase), reg);
masm.loadPtr(Address(reg), reg);
return Address(reg, access.index * sizeof(Value));
}
inline void
FrameState::forgetLoopReg(FrameEntry *fe)
{

View File

@ -1949,9 +1949,6 @@ FrameState::pushCopyOf(FrameEntry *backing)
FrameEntry *
FrameState::walkTrackerForUncopy(FrameEntry *original)
{
/* Temporary entries are immutable and should never be uncopied. */
JS_ASSERT(!isTemporary(original));
uint32 firstCopy = InvalidIndex;
FrameEntry *bestFe = NULL;
uint32 ncopies = 0;
@ -1978,7 +1975,7 @@ FrameState::walkTrackerForUncopy(FrameEntry *original)
JS_ASSERT(firstCopy != InvalidIndex);
JS_ASSERT(bestFe);
JS_ASSERT(bestFe > original);
JS_ASSERT_IF(!isTemporary(original), bestFe > original);
/* Mark all extra copies as copies of the new backing index. */
bestFe->setCopyOf(NULL);
@ -2873,6 +2870,8 @@ FrameState::clearTemporaries()
for (FrameEntry *fe = temporaries; fe < temporariesTop; fe++) {
if (!fe->isTracked())
continue;
if (fe->isCopied())
uncopy(fe);
forgetAllRegs(fe);
fe->resetSynced();
}

View File

@ -953,6 +953,12 @@ class FrameState
inline void syncAndForgetFe(FrameEntry *fe, bool markSynced = false);
inline void forgetLoopReg(FrameEntry *fe);
/*
* Get an address for the specified name access in another script.
* The compiler owns the result's base register.
*/
inline Address loadNameAddress(const analyze::ScriptAnalysis::NameAccess &access);
private:
inline AnyRegisterID allocAndLoadReg(FrameEntry *fe, bool fp, RematInfo::RematType type);
inline void forgetReg(AnyRegisterID reg);

View File

@ -361,7 +361,7 @@ UncachedInlineCall(VMFrame &f, InitialFrameFlags initial,
PreserveRegsGuard regsGuard(cx, regs);
/* Scope with a call object parented by callee's parent. */
if (newfun->isHeavyweight() && !js::CreateFunCallObject(cx, regs.fp()))
if (!regs.fp()->functionPrologue(cx))
return false;
/*
@ -497,13 +497,6 @@ stubs::UncachedCallHelper(VMFrame &f, uint32 argc, bool lowered, UncachedCallRes
return;
}
void JS_FASTCALL
stubs::PutActivationObjects(VMFrame &f)
{
JS_ASSERT(f.fp()->hasCallObj() || f.fp()->hasArgsObj());
f.fp()->putActivationObjects();
}
static void
RemoveOrphanedNative(JSContext *cx, StackFrame *fp)
{
@ -634,7 +627,7 @@ js_InternalThrow(VMFrame &f)
*/
cx->compartment->jaegerCompartment()->setLastUnfinished(Jaeger_Unfinished);
if (!script->ensureRanBytecode(cx)) {
if (!script->ensureRanAnalysis(cx)) {
js_ReportOutOfMemory(cx);
return NULL;
}
@ -670,14 +663,6 @@ js_InternalThrow(VMFrame &f)
return script->nativeCodeForPC(fp->isConstructing(), pc);
}
void JS_FASTCALL
stubs::CreateFunCallObject(VMFrame &f)
{
JS_ASSERT(f.fp()->fun()->isHeavyweight());
if (!js::CreateFunCallObject(f.cx, f.fp()))
THROW();
}
void JS_FASTCALL
stubs::CreateThis(VMFrame &f, JSObject *proto)
{
@ -1251,7 +1236,7 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM
JSOp op = JSOp(*pc);
const JSCodeSpec *cs = &js_CodeSpec[op];
if (!script->ensureRanBytecode(cx)) {
if (!script->ensureRanAnalysis(cx)) {
js_ReportOutOfMemory(cx);
return js_InternalThrow(f);
}
@ -1395,7 +1380,7 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM
break;
}
case REJOIN_CHECK_ARGUMENTS: {
case REJOIN_CHECK_ARGUMENTS:
/*
* Do all the work needed in arity check JIT prologues after the
* arguments check occurs (FixupArity has been called if needed, but
@ -1406,22 +1391,17 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM
SetValueRangeToUndefined(fp->slots(), script->nfixed);
if (fp->fun()->isHeavyweight()) {
if (!js::CreateFunCallObject(cx, fp))
return js_InternalThrow(f);
}
if (!fp->functionPrologue(cx))
return js_InternalThrow(f);
/* FALLTHROUGH */
}
case REJOIN_CREATE_CALL_OBJECT: {
case REJOIN_FUNCTION_PROLOGUE:
fp->scopeChain();
/* Construct the 'this' object for the frame if necessary. */
if (!ScriptPrologueOrGeneratorResume(cx, fp, types::UseNewTypeAtEntry(cx, fp)))
return js_InternalThrow(f);
break;
}
case REJOIN_CALL_PROLOGUE:
case REJOIN_CALL_PROLOGUE_LOWERED_CALL:
@ -1493,6 +1473,7 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM
break;
case JSOP_CALLGNAME:
case JSOP_CALLNAME:
if (!ComputeImplicitThis(cx, &fp->scopeChain(), nextsp[-2], &nextsp[-1]))
return js_InternalThrow(f);
f.regs.pc = nextpc;

View File

@ -576,11 +576,16 @@ LoopState::hoistArrayLengthCheck(InvariantArrayKind arrayKind, const CrossSSAVal
if (indexSlot == UNASSIGNED) {
/* Hoist checks on x[n] accesses for constant n. */
if (indexConstant < 0) {
JaegerSpew(JSpew_Analysis, "Constant index is negative\n");
return false;
}
return addHoistedCheck(arrayKind, objSlot, UNASSIGNED, UNASSIGNED, indexConstant);
}
if (loopInvariantEntry(indexSlot)) {
/* Hoist checks on x[y] accesses when y is loop invariant. */
addNegativeCheck(indexSlot, indexConstant);
return addHoistedCheck(arrayKind, objSlot, indexSlot, UNASSIGNED, indexConstant);
}

View File

@ -925,7 +925,8 @@ mjit::EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimi
}
/* See comment in mjit::Compiler::emitReturn. */
fp->markActivationObjectsAsPut();
if (fp->isFunctionFrame())
fp->markFunctionEpilogueDone();
return ok ? Jaeger_Returned : Jaeger_Throwing;
}

View File

@ -285,12 +285,15 @@ enum RejoinState {
/*
* Type check on arguments failed during prologue, need stack check and
* call object creation before script can execute.
* the rest of the JIT prologue before the script can execute.
*/
REJOIN_CHECK_ARGUMENTS,
/* A GC while making a call object occurred, discarding the script's jitcode. */
REJOIN_CREATE_CALL_OBJECT,
/*
* The script's jitcode was discarded after marking an outer function as
* reentrant or due to a GC while creating a call object.
*/
REJOIN_FUNCTION_PROLOGUE,
/*
* State after calling a stub which returns a JIT code pointer for a call

View File

@ -691,9 +691,10 @@ class SetPropCompiler : public PICStubCompiler
* objects may differ due to eval(), DEFFUN, etc.).
*/
RecompilationMonitor monitor(cx);
JSScript *script = obj->getCallObjCalleeFunction()->script();
JSFunction *fun = obj->getCallObjCalleeFunction();
JSScript *script = fun->script();
uint16 slot = uint16(shape->shortid);
if (!script->ensureHasTypes(cx))
if (!script->ensureHasTypes(cx, fun))
return error();
{
types::AutoEnterTypeInference enter(cx);

View File

@ -2194,6 +2194,19 @@ stubs::Unbrand(VMFrame &f)
obj->unbrand(f.cx);
}
void JS_FASTCALL
stubs::UnbrandThis(VMFrame &f)
{
if (!ComputeThis(f.cx, f.fp()))
THROW();
Value &thisv = f.fp()->thisValue();
if (!thisv.isObject())
return;
JSObject *obj = &thisv.toObject();
if (obj->isNative())
obj->unbrand(f.cx);
}
void JS_FASTCALL
stubs::Pos(VMFrame &f)
{
@ -2520,6 +2533,27 @@ stubs::Exception(VMFrame &f)
f.cx->clearPendingException();
}
void JS_FASTCALL
stubs::FunctionFramePrologue(VMFrame &f)
{
if (!f.fp()->functionPrologue(f.cx))
THROW();
}
void JS_FASTCALL
stubs::FunctionFrameEpilogue(VMFrame &f)
{
f.fp()->functionEpilogue();
}
void JS_FASTCALL
stubs::AnyFrameEpilogue(VMFrame &f)
{
if (f.fp()->isNonEvalFunctionFrame())
f.fp()->functionEpilogue();
stubs::ScriptDebugEpilogue(f);
}
template <bool Clamped>
int32 JS_FASTCALL
stubs::ConvertToTypedInt(JSContext *cx, Value *vp)

View File

@ -113,8 +113,6 @@ void UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr);
void JS_FASTCALL CreateThis(VMFrame &f, JSObject *proto);
void JS_FASTCALL Throw(VMFrame &f);
void JS_FASTCALL PutActivationObjects(VMFrame &f);
void JS_FASTCALL CreateFunCallObject(VMFrame &f);
#if JS_MONOIC
void * JS_FASTCALL InvokeTracer(VMFrame &f, ic::TraceICInfo *tic);
#else
@ -202,6 +200,7 @@ JSBool JS_FASTCALL InstanceOf(VMFrame &f);
void JS_FASTCALL FastInstanceOf(VMFrame &f);
void JS_FASTCALL ArgCnt(VMFrame &f);
void JS_FASTCALL Unbrand(VMFrame &f);
void JS_FASTCALL UnbrandThis(VMFrame &f);
/*
* Helper for triggering recompilation should a name read miss a type barrier,
@ -227,6 +226,11 @@ void JS_FASTCALL ConvertToTypedFloat(JSContext *cx, Value *vp);
void JS_FASTCALL Exception(VMFrame &f);
void JS_FASTCALL FunctionFramePrologue(VMFrame &f);
void JS_FASTCALL FunctionFrameEpilogue(VMFrame &f);
void JS_FASTCALL AnyFrameEpilogue(VMFrame &f);
JSObject * JS_FASTCALL
NewDenseUnallocatedArray(VMFrame &f, uint32 length);

View File

@ -110,7 +110,8 @@ TrampolineCompiler::compileTrampoline(Trampolines::TrampolinePtr *where,
/*
* This is shamelessly copied from emitReturn, but with several changes:
* - There was always at least one inline call.
* - We don't know if there is a call object, so we always check.
* - We don't know if there are activation objects or a script with nesting
* state whose active frames need adjustment, so we always stub the epilogue.
* - We don't know where we came from, so we don't know frame depth or PC.
* - There is no stub buffer.
*/
@ -120,13 +121,8 @@ TrampolineCompiler::generateForceReturn(Assembler &masm)
/* The JSStackFrame register may have been clobbered while returning, reload it. */
masm.loadPtr(FrameAddress(VMFrame::offsetOfFp), JSFrameReg);
masm.fallibleVMCall(true, JS_FUNC_TO_DATA_PTR(void *, stubs::ScriptDebugEpilogue), NULL, NULL, 0);
/* if (hasArgsObj() || hasCallObj()) stubs::PutActivationObjects() */
Jump noActObjs = masm.branchTest32(Assembler::Zero, FrameFlagsAddress(),
Imm32(StackFrame::HAS_CALL_OBJ | StackFrame::HAS_ARGS_OBJ));
masm.fallibleVMCall(true, JS_FUNC_TO_DATA_PTR(void *, stubs::PutActivationObjects), NULL, NULL, 0);
noActObjs.linkTo(masm.label(), &masm);
/* Perform the frame epilogue. */
masm.fallibleVMCall(true, JS_FUNC_TO_DATA_PTR(void *, stubs::AnyFrameEpilogue), NULL, NULL, 0);
/* Store any known return value */
masm.loadValueAsComponents(UndefinedValue(), JSReturnReg_Type, JSReturnReg_Data);

View File

@ -225,7 +225,10 @@ BreakpointSite::clearTrap(JSContext *cx, BreakpointSiteMap::Enum *e,
trapClosure.setUndefined();
if (enabledCount == 0) {
*pc = realOpcode;
recompile(cx, true); /* ignore failure */
if (!cx->runtime->gcRunning) {
/* If the GC is running then the script is being destroyed. */
recompile(cx, true); /* ignore failure */
}
destroyIfEmpty(cx->runtime, e);
}
}

View File

@ -46,6 +46,7 @@
#include "Stack.h"
#include "jsscriptinlines.h"
#include "ArgumentsObject-inl.h"
#include "methodjit/MethodJIT.h"
@ -130,11 +131,6 @@ StackFrame::resetCallFrame(JSScript *script)
{
JS_ASSERT(script == this->script());
/* Undo changes to frame made during execution; see also initCallFrame */
putActivationObjects();
markActivationObjectsAsPut();
if (flags_ & UNDERFLOW_ARGS)
SetValueRangeToUndefined(formalArgs() + numActualArgs(), formalArgsEnd());
@ -365,9 +361,44 @@ StackFrame::callObj() const
return *pobj;
}
inline void
StackFrame::putActivationObjects()
inline bool
StackFrame::maintainNestingState() const
{
/*
* Whether to invoke the nesting epilogue/prologue to maintain active
* frame counts and check for reentrant outer functions.
*/
return isNonEvalFunctionFrame() && !isGeneratorFrame() && script()->nesting();
}
inline bool
StackFrame::functionPrologue(JSContext *cx)
{
JS_ASSERT(isNonEvalFunctionFrame());
JSFunction *fun = this->fun();
if (fun->isHeavyweight()) {
if (!CreateFunCallObject(cx, this))
return false;
} else {
/* Force instantiation of the scope chain, for JIT frames. */
scopeChain();
}
if (script()->nesting()) {
JS_ASSERT(maintainNestingState());
types::NestingPrologue(cx, this);
}
return true;
}
inline void
StackFrame::functionEpilogue(bool objectsOnly)
{
JS_ASSERT(isNonEvalFunctionFrame());
if (flags_ & (HAS_ARGS_OBJ | HAS_CALL_OBJ)) {
/* NB: there is an ordering dependency here. */
if (hasCallObj())
@ -375,10 +406,13 @@ StackFrame::putActivationObjects()
else if (hasArgsObj())
js_PutArgsObject(this);
}
if (!objectsOnly && maintainNestingState())
types::NestingEpilogue(this);
}
inline void
StackFrame::markActivationObjectsAsPut()
StackFrame::markFunctionEpilogueDone(bool activationOnly)
{
if (flags_ & (HAS_ARGS_OBJ | HAS_CALL_OBJ)) {
if (hasArgsObj() && !argsObj().getPrivate()) {
@ -399,6 +433,14 @@ StackFrame::markActivationObjectsAsPut()
flags_ &= ~HAS_CALL_OBJ;
}
}
/*
* For outer/inner function frames, undo the active frame balancing so that
* when we redo it in the epilogue we get the right final value. The other
* nesting epilogue changes (update active args/vars) are idempotent.
*/
if (!activationOnly && maintainNestingState())
script()->nesting()->activeFrames++;
}
/*****************************************************************************/
@ -547,7 +589,7 @@ ContextStack::popInlineFrame(FrameRegs &regs)
JS_ASSERT(&regs == &seg_->regs());
StackFrame *fp = regs.fp();
fp->putActivationObjects();
fp->functionEpilogue();
Value *newsp = fp->actualArgs() - 1;
JS_ASSERT(newsp >= fp->prev()->base());

View File

@ -779,7 +779,8 @@ ContextStack::popFrame(const FrameGuard &fg)
JS_ASSERT(space().firstUnused() == fg.regs_.sp);
JS_ASSERT(&fg.regs_ == &seg_->regs());
fg.regs_.fp()->putActivationObjects();
if (fg.regs_.fp()->isNonEvalFunctionFrame())
fg.regs_.fp()->functionEpilogue();
seg_->popRegs(fg.prevRegs_);
if (fg.pushedSeg_)

View File

@ -856,11 +856,28 @@ class StackFrame
inline void setScopeChainWithOwnCallObj(JSObject &obj);
/*
* NB: putActivationObjects does not mark activation objects as having been
* put (since the frame is about to be popped).
* Prologue for function frames: make a call object for heavyweight
* functions, and maintain type nesting invariants.
*/
inline void putActivationObjects();
inline void markActivationObjectsAsPut();
inline bool functionPrologue(JSContext *cx);
/*
* Epilogue for function frames: put any args or call object for the frame
* which may still be live, and maintain type nesting invariants. Only the
* args/call objects are put if activationOnly is set. Note: this does not
* mark the epilogue as having been completed, since the frame is about to
* be popped. Use markFunctionEpilogueDone for this.
*/
inline void functionEpilogue(bool activationOnly = false);
/*
* Mark any work needed in the function's epilogue as done. Only the args
* and call objects are reset if activationOnly is set. If activationOnly
* is *NOT* set, this call must be followed by a later functionEpilogue.
*/
inline void markFunctionEpilogueDone(bool activationOnly = false);
inline bool maintainNestingState() const;
/*
* Variables object

View File

@ -1366,7 +1366,10 @@ CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind,
case JSTRACE_SCRIPT:
{
JSScript *script = static_cast<JSScript *>(thing);
if (script->data != script->inlineData) {
#if JS_SCRIPT_INLINE_DATA_LIMIT
if (script->data != script->inlineData)
#endif
{
size_t usable = moz_malloc_usable_size(script->data);
curr->scriptData += usable ? usable : script->dataSize();
}

View File

@ -62,6 +62,11 @@ HTTP(..) == weightmapping-478.html weightmapping-478-ref.html
HTTP(..) == weightmapping-7.html weightmapping-7-ref.html
HTTP(..) == weightmapping-12579.html weightmapping-12579-ref.html
HTTP(..) == stretchmapping-all.html stretchmapping-all-ref.html
HTTP(..) == stretchmapping-reverse.html stretchmapping-reverse-ref.html
HTTP(..) == stretchmapping-35.html stretchmapping-35-ref.html
HTTP(..) == stretchmapping-137.html stretchmapping-137-ref.html
# test for font-stretch using @font-face
HTTP(..) == font-stretch-1.html font-stretch-1-ref.html

View File

@ -0,0 +1,348 @@
<!DOCTYPE HTML>
<html>
<head>
<title>font-stretch mapping tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
body {
margin: 50px;
font-family: Verdana, sans-serif;
}
h3, h4 { font-weight: normal; }
table {
border-collapse: collapse;
font-size: 28px;
}
td {
padding: 0; margin: 0;
font-family: test;
}
th {
font-weight: inherit;
}
p { width: 300px; }
.red { color: red; }
thead { font-weight: 400; font-size: 75%; }
/* make all the spans blocks to avoid influence of what's outside them
on line-height calculations */
span { display: block; }
@font-face {
font-family: test;
src: url(../fonts/mplus/mplus-1p-thin.ttf);
font-weight: 100;
font-stretch: ultra-condensed;
}
@font-face {
font-family: test;
src: url(../fonts/mplus/mplus-1p-regular.ttf);
font-weight: 400;
font-stretch: ultra-condensed;
}
@font-face {
font-family: test;
src: url(../fonts/mplus/mplus-1p-light.ttf);
font-weight: 200;
font-stretch: condensed;
}
@font-face {
font-family: test;
src: url(../fonts/mplus/mplus-1p-heavy.ttf);
font-weight: 800;
font-stretch: condensed;
}
@font-face {
font-family: test;
src: url(../fonts/mplus/mplus-1p-medium.ttf);
font-weight: 500;
font-stretch: expanded;
}
@font-face {
font-family: test;
src: url(../fonts/mplus/mplus-1p-black.ttf);
font-weight: 900;
font-stretch: expanded;
}
@font-face {
font-family: test100;
src: url(../fonts/mplus/mplus-1p-thin.ttf);
font-weight: 100;
}
@font-face {
font-family: test200;
src: url(../fonts/mplus/mplus-1p-light.ttf);
font-weight: 200;
}
@font-face {
font-family: test400;
src: url(../fonts/mplus/mplus-1p-regular.ttf);
font-weight: 400;
}
@font-face {
font-family: test500;
src: url(../fonts/mplus/mplus-1p-medium.ttf);
font-weight: 500;
}
@font-face {
font-family: test800;
src: url(../fonts/mplus/mplus-1p-heavy.ttf);
font-weight: 800;
}
@font-face {
font-family: test900;
src: url(../fonts/mplus/mplus-1p-black.ttf);
font-weight: 900;
}
.w1 { font-weight: 100; }
.w2 { font-weight: 200; }
.w3 { font-weight: 300; }
.w4 { font-weight: 400; }
.w5 { font-weight: 500; }
.w6 { font-weight: 600; }
.w7 { font-weight: 700; }
.w8 { font-weight: 800; }
.w9 { font-weight: 900; }
.w1 .fs1 { font-family: test100; }
.w2 .fs1 { font-family: test100; }
.w3 .fs1 { font-family: test100; }
.w4 .fs1 { font-family: test400; }
.w5 .fs1 { font-family: test400; }
.w6 .fs1 { font-family: test400; }
.w7 .fs1 { font-family: test400; }
.w8 .fs1 { font-family: test400; }
.w9 .fs1 { font-family: test400; }
.w1 .fs2 { font-family: test100; }
.w2 .fs2 { font-family: test100; }
.w3 .fs2 { font-family: test100; }
.w4 .fs2 { font-family: test400; }
.w5 .fs2 { font-family: test400; }
.w6 .fs2 { font-family: test400; }
.w7 .fs2 { font-family: test400; }
.w8 .fs2 { font-family: test400; }
.w9 .fs2 { font-family: test400; }
.w1 .fs3 { font-family: test200; }
.w2 .fs3 { font-family: test200; }
.w3 .fs3 { font-family: test200; }
.w4 .fs3 { font-family: test200; }
.w5 .fs3 { font-family: test200; }
.w6 .fs3 { font-family: test800; }
.w7 .fs3 { font-family: test800; }
.w8 .fs3 { font-family: test800; }
.w9 .fs3 { font-family: test800; }
.w1 .fs4 { font-family: test200; }
.w2 .fs4 { font-family: test200; }
.w3 .fs4 { font-family: test200; }
.w4 .fs4 { font-family: test200; }
.w5 .fs4 { font-family: test200; }
.w6 .fs4 { font-family: test800; }
.w7 .fs4 { font-family: test800; }
.w8 .fs4 { font-family: test800; }
.w9 .fs4 { font-family: test800; }
.w1 .fs5 { font-family: test200; }
.w2 .fs5 { font-family: test200; }
.w3 .fs5 { font-family: test200; }
.w4 .fs5 { font-family: test200; }
.w5 .fs5 { font-family: test200; }
.w6 .fs5 { font-family: test800; }
.w7 .fs5 { font-family: test800; }
.w8 .fs5 { font-family: test800; }
.w9 .fs5 { font-family: test800; }
.w1 .fs6 { font-family: test500; }
.w2 .fs6 { font-family: test500; }
.w3 .fs6 { font-family: test500; }
.w4 .fs6 { font-family: test500; }
.w5 .fs6 { font-family: test500; }
.w6 .fs6 { font-family: test900; }
.w7 .fs6 { font-family: test900; }
.w8 .fs6 { font-family: test900; }
.w9 .fs6 { font-family: test900; }
.w1 .fs7 { font-family: test500; }
.w2 .fs7 { font-family: test500; }
.w3 .fs7 { font-family: test500; }
.w4 .fs7 { font-family: test500; }
.w5 .fs7 { font-family: test500; }
.w6 .fs7 { font-family: test900; }
.w7 .fs7 { font-family: test900; }
.w8 .fs7 { font-family: test900; }
.w9 .fs7 { font-family: test900; }
.w1 .fs8 { font-family: test500; }
.w2 .fs8 { font-family: test500; }
.w3 .fs8 { font-family: test500; }
.w4 .fs8 { font-family: test500; }
.w5 .fs8 { font-family: test500; }
.w6 .fs8 { font-family: test900; }
.w7 .fs8 { font-family: test900; }
.w8 .fs8 { font-family: test900; }
.w9 .fs8 { font-family: test900; }
.w1 .fs9 { font-family: test500; }
.w2 .fs9 { font-family: test500; }
.w3 .fs9 { font-family: test500; }
.w4 .fs9 { font-family: test500; }
.w5 .fs9 { font-family: test500; }
.w6 .fs9 { font-family: test900; }
.w7 .fs9 { font-family: test900; }
.w8 .fs9 { font-family: test900; }
.w9 .fs9 { font-family: test900; }
</style>
</head>
<body>
<p>Font family with ultra-condensed 100, 400, condensed 200, 800 and expanded 500, 900</p>
<table>
<thead>
<th></th>
<th class="red">1</th>
<th>2</th>
<th class="red">3</th>
<th>4</th>
<th>5</th>
<th>6</th>
<th class="red">7</th>
<th>8</th>
<th>9</th>
</thead>
<tr class="w1">
<th>100</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w2">
<th>200</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w3">
<th>300</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w4">
<th>400</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w5">
<th>500</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w6">
<th>600</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w7">
<th>700</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w8">
<th>800</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w9">
<th>900</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
</table>
</body>
</html>

View File

@ -0,0 +1,268 @@
<!DOCTYPE HTML>
<html>
<head>
<title>font-stretch mapping tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
body {
margin: 50px;
font-family: Verdana, sans-serif;
}
h3, h4 { font-weight: normal; }
table {
border-collapse: collapse;
font-size: 28px;
}
td {
padding: 0; margin: 0;
font-family: test;
}
th {
font-weight: inherit;
}
p { width: 300px; }
.red { color: red; }
thead { font-weight: 400; font-size: 75%; }
/* make all the spans blocks to avoid influence of what's outside them
on line-height calculations */
span { display: block; }
@font-face {
font-family: test;
src: url(../fonts/mplus/mplus-1p-thin.ttf);
font-weight: 100;
font-stretch: ultra-condensed;
}
@font-face {
font-family: test;
src: url(../fonts/mplus/mplus-1p-regular.ttf);
font-weight: 400;
font-stretch: ultra-condensed;
}
@font-face {
font-family: test;
src: url(../fonts/mplus/mplus-1p-light.ttf);
font-weight: 200;
font-stretch: condensed;
}
@font-face {
font-family: test;
src: url(../fonts/mplus/mplus-1p-heavy.ttf);
font-weight: 800;
font-stretch: condensed;
}
@font-face {
font-family: test;
src: url(../fonts/mplus/mplus-1p-medium.ttf);
font-weight: 500;
font-stretch: expanded;
}
@font-face {
font-family: test;
src: url(../fonts/mplus/mplus-1p-black.ttf);
font-weight: 900;
font-stretch: expanded;
}
@font-face {
font-family: test100;
src: url(../fonts/mplus/mplus-1p-thin.ttf);
font-weight: 100;
}
@font-face {
font-family: test200;
src: url(../fonts/mplus/mplus-1p-light.ttf);
font-weight: 200;
}
@font-face {
font-family: test400;
src: url(../fonts/mplus/mplus-1p-regular.ttf);
font-weight: 400;
}
@font-face {
font-family: test500;
src: url(../fonts/mplus/mplus-1p-medium.ttf);
font-weight: 500;
}
@font-face {
font-family: test800;
src: url(../fonts/mplus/mplus-1p-heavy.ttf);
font-weight: 800;
}
@font-face {
font-family: test900;
src: url(../fonts/mplus/mplus-1p-black.ttf);
font-weight: 900;
}
.w1 { font-weight: 100; }
.w2 { font-weight: 200; }
.w3 { font-weight: 300; }
.w4 { font-weight: 400; }
.w5 { font-weight: 500; }
.w6 { font-weight: 600; }
.w7 { font-weight: 700; }
.w8 { font-weight: 800; }
.w9 { font-weight: 900; }
.fs1 { font-stretch: ultra-condensed; }
.fs2 { font-stretch: extra-condensed; }
.fs3 { font-stretch: condensed; }
.fs4 { font-stretch: semi-condensed; }
.fs5 { font-stretch: normal; }
.fs6 { font-stretch: semi-expanded; }
.fs7 { font-stretch: expanded; }
.fs8 { font-stretch: extra-expanded; }
.fs9 { font-stretch: ultra-expanded; }
</style>
</head>
<body>
<p>Font family with ultra-condensed 100, 400, condensed 200, 800 and expanded 500, 900</p>
<table>
<thead>
<th></th>
<th class="red">1</th>
<th>2</th>
<th class="red">3</th>
<th>4</th>
<th>5</th>
<th>6</th>
<th class="red">7</th>
<th>8</th>
<th>9</th>
</thead>
<tr class="w1">
<th>100</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w2">
<th>200</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w3">
<th>300</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w4">
<th>400</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w5">
<th>500</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w6">
<th>600</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w7">
<th>700</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w8">
<th>800</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w9">
<th>900</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
</table>
</body>
</html>

View File

@ -0,0 +1,320 @@
<!DOCTYPE HTML>
<html>
<head>
<title>font-stretch mapping tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
body {
margin: 50px;
font-family: Verdana, sans-serif;
}
h3, h4 { font-weight: normal; }
table {
border-collapse: collapse;
font-size: 28px;
}
td {
padding: 0; margin: 0;
font-family: test;
}
th {
font-weight: inherit;
}
p { width: 300px; }
.red { color: red; }
thead { font-weight: 400; font-size: 75%; }
/* make all the spans blocks to avoid influence of what's outside them
on line-height calculations */
span { display: block; }
@font-face {
font-family: test;
src: url(../fonts/mplus/mplus-1p-light.ttf);
font-weight: 200;
}
@font-face {
font-family: test;
src: url(../fonts/mplus/mplus-1p-medium.ttf);
font-weight: 500;
}
@font-face {
font-family: test;
src: url(../fonts/mplus/mplus-1p-thin.ttf);
font-weight: 100;
font-stretch: condensed;
}
@font-face {
font-family: test;
src: url(../fonts/mplus/mplus-1p-black.ttf);
font-weight: 900;
font-stretch: condensed;
}
@font-face {
font-family: test100;
src: url(../fonts/mplus/mplus-1p-thin.ttf);
font-weight: 100;
}
@font-face {
font-family: test200;
src: url(../fonts/mplus/mplus-1p-light.ttf);
font-weight: 200;
}
@font-face {
font-family: test500;
src: url(../fonts/mplus/mplus-1p-medium.ttf);
font-weight: 500;
}
@font-face {
font-family: test900;
src: url(../fonts/mplus/mplus-1p-black.ttf);
font-weight: 900;
}
.w1 { font-weight: 100; }
.w2 { font-weight: 200; }
.w3 { font-weight: 300; }
.w4 { font-weight: 400; }
.w5 { font-weight: 500; }
.w6 { font-weight: 600; }
.w7 { font-weight: 700; }
.w8 { font-weight: 800; }
.w9 { font-weight: 900; }
.w1 .fs1 { font-family: test100; }
.w2 .fs1 { font-family: test100; }
.w3 .fs1 { font-family: test100; }
.w4 .fs1 { font-family: test100; }
.w5 .fs1 { font-family: test100; }
.w6 .fs1 { font-family: test900; }
.w7 .fs1 { font-family: test900; }
.w8 .fs1 { font-family: test900; }
.w9 .fs1 { font-family: test900; }
.w1 .fs2 { font-family: test100; }
.w2 .fs2 { font-family: test100; }
.w3 .fs2 { font-family: test100; }
.w4 .fs2 { font-family: test100; }
.w5 .fs2 { font-family: test100; }
.w6 .fs2 { font-family: test900; }
.w7 .fs2 { font-family: test900; }
.w8 .fs2 { font-family: test900; }
.w9 .fs2 { font-family: test900; }
.w1 .fs3 { font-family: test100; }
.w2 .fs3 { font-family: test100; }
.w3 .fs3 { font-family: test100; }
.w4 .fs3 { font-family: test100; }
.w5 .fs3 { font-family: test100; }
.w6 .fs3 { font-family: test900; }
.w7 .fs3 { font-family: test900; }
.w8 .fs3 { font-family: test900; }
.w9 .fs3 { font-family: test900; }
.w1 .fs4 { font-family: test100; }
.w2 .fs4 { font-family: test100; }
.w3 .fs4 { font-family: test100; }
.w4 .fs4 { font-family: test100; }
.w5 .fs4 { font-family: test100; }
.w6 .fs4 { font-family: test900; }
.w7 .fs4 { font-family: test900; }
.w8 .fs4 { font-family: test900; }
.w9 .fs4 { font-family: test900; }
.w1 .fs5 { font-family: test200; }
.w2 .fs5 { font-family: test200; }
.w3 .fs5 { font-family: test200; }
.w4 .fs5 { font-family: test500; }
.w5 .fs5 { font-family: test500; }
.w6 .fs5 { font-family: test500; }
.w7 .fs5 { font-family: test500; }
.w8 .fs5 { font-family: test500; }
.w9 .fs5 { font-family: test500; }
.w1 .fs6 { font-family: test200; }
.w2 .fs6 { font-family: test200; }
.w3 .fs6 { font-family: test200; }
.w4 .fs6 { font-family: test500; }
.w5 .fs6 { font-family: test500; }
.w6 .fs6 { font-family: test500; }
.w7 .fs6 { font-family: test500; }
.w8 .fs6 { font-family: test500; }
.w9 .fs6 { font-family: test500; }
.w1 .fs7 { font-family: test200; }
.w2 .fs7 { font-family: test200; }
.w3 .fs7 { font-family: test200; }
.w4 .fs7 { font-family: test500; }
.w5 .fs7 { font-family: test500; }
.w6 .fs7 { font-family: test500; }
.w7 .fs7 { font-family: test500; }
.w8 .fs7 { font-family: test500; }
.w9 .fs7 { font-family: test500; }
.w1 .fs8 { font-family: test200; }
.w2 .fs8 { font-family: test200; }
.w3 .fs8 { font-family: test200; }
.w4 .fs8 { font-family: test500; }
.w5 .fs8 { font-family: test500; }
.w6 .fs8 { font-family: test500; }
.w7 .fs8 { font-family: test500; }
.w8 .fs8 { font-family: test500; }
.w9 .fs8 { font-family: test500; }
.w1 .fs9 { font-family: test200; }
.w2 .fs9 { font-family: test200; }
.w3 .fs9 { font-family: test200; }
.w4 .fs9 { font-family: test500; }
.w5 .fs9 { font-family: test500; }
.w6 .fs9 { font-family: test500; }
.w7 .fs9 { font-family: test500; }
.w8 .fs9 { font-family: test500; }
.w9 .fs9 { font-family: test500; }
</style>
</head>
<body>
<p>Font family with normal width 200, 500 and condensed 100, 900</p>
<table>
<thead>
<th></th>
<th>1</th>
<th>2</th>
<th class="red">3</th>
<th>4</th>
<th class="red">5</th>
<th>6</th>
<th>7</th>
<th>8</th>
<th>9</th>
</thead>
<tr class="w1">
<th>100</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w2">
<th>200</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w3">
<th>300</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w4">
<th>400</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w5">
<th>500</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w6">
<th>600</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w7">
<th>700</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w8">
<th>800</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w9">
<th>900</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
</table>
</body>
</html>

View File

@ -0,0 +1,240 @@
<!DOCTYPE HTML>
<html>
<head>
<title>font-stretch mapping tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
body {
margin: 50px;
font-family: Verdana, sans-serif;
}
h3, h4 { font-weight: normal; }
table {
border-collapse: collapse;
font-size: 28px;
}
td {
padding: 0; margin: 0;
font-family: test;
}
th {
font-weight: inherit;
}
p { width: 300px; }
.red { color: red; }
thead { font-weight: 400; font-size: 75%; }
/* make all the spans blocks to avoid influence of what's outside them
on line-height calculations */
span { display: block; }
@font-face {
font-family: test;
src: url(../fonts/mplus/mplus-1p-light.ttf);
font-weight: 200;
}
@font-face {
font-family: test;
src: url(../fonts/mplus/mplus-1p-medium.ttf);
font-weight: 500;
}
@font-face {
font-family: test;
src: url(../fonts/mplus/mplus-1p-thin.ttf);
font-weight: 100;
font-stretch: condensed;
}
@font-face {
font-family: test;
src: url(../fonts/mplus/mplus-1p-black.ttf);
font-weight: 900;
font-stretch: condensed;
}
@font-face {
font-family: test100;
src: url(../fonts/mplus/mplus-1p-thin.ttf);
font-weight: 100;
}
@font-face {
font-family: test200;
src: url(../fonts/mplus/mplus-1p-light.ttf);
font-weight: 200;
}
@font-face {
font-family: test500;
src: url(../fonts/mplus/mplus-1p-medium.ttf);
font-weight: 500;
}
@font-face {
font-family: test900;
src: url(../fonts/mplus/mplus-1p-black.ttf);
font-weight: 900;
}
.w1 { font-weight: 100; }
.w2 { font-weight: 200; }
.w3 { font-weight: 300; }
.w4 { font-weight: 400; }
.w5 { font-weight: 500; }
.w6 { font-weight: 600; }
.w7 { font-weight: 700; }
.w8 { font-weight: 800; }
.w9 { font-weight: 900; }
.fs1 { font-stretch: ultra-condensed; }
.fs2 { font-stretch: extra-condensed; }
.fs3 { font-stretch: condensed; }
.fs4 { font-stretch: semi-condensed; }
.fs5 { font-stretch: normal; }
.fs6 { font-stretch: semi-expanded; }
.fs7 { font-stretch: expanded; }
.fs8 { font-stretch: extra-expanded; }
.fs9 { font-stretch: ultra-expanded; }
</style>
</head>
<body>
<p>Font family with normal width 200, 500 and condensed 100, 900</p>
<table>
<thead>
<th></th>
<th>1</th>
<th>2</th>
<th class="red">3</th>
<th>4</th>
<th class="red">5</th>
<th>6</th>
<th>7</th>
<th>8</th>
<th>9</th>
</thead>
<tr class="w1">
<th>100</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w2">
<th>200</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w3">
<th>300</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w4">
<th>400</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w5">
<th>500</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w6">
<th>600</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w7">
<th>700</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w8">
<th>800</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
<tr class="w9">
<th>900</th>
<td class="fs1"><span></span></td>
<td class="fs2"><span></span></td>
<td class="fs3"><span></span></td>
<td class="fs4"><span></span></td>
<td class="fs5"><span></span></td>
<td class="fs6"><span></span></td>
<td class="fs7"><span></span></td>
<td class="fs8"><span></span></td>
<td class="fs9"><span></span></td>
</tr>
</table>
</body>
</html>

View File

@ -0,0 +1,366 @@
<!DOCTYPE HTML>
<html>
<head>
<title>font-stretch matching tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
body {
margin: 50px;
font-family: Calibri, Verdana, sans-serif;
}
p { margin: 0; padding: 0; }
table {
border-collapse: collapse;
}
th {
font-weight: normal;
background-color: #eee;
}
th, td { width: 3em; text-align: left; }
tr th { text-align: left; }
.fstest-full { font-family: fstest-full; }
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd1.ttf);
font-stretch: ultra-condensed;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd2.ttf);
font-stretch: extra-condensed;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd3.ttf);
font-stretch: condensed;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd4.ttf);
font-stretch: semi-condensed;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd5.ttf);
font-stretch: normal;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd6.ttf);
font-stretch: semi-expanded;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd7.ttf);
font-stretch: expanded;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd8.ttf);
font-stretch: extra-expanded;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd9.ttf);
font-stretch: ultra-expanded;
}
.fstest-1-3 { font-family: fstest-1-3; }
@font-face {
font-family: fstest-1-3;
src: url(../fonts/csstest-widths-wd1.ttf);
font-stretch: ultra-condensed;
}
@font-face {
font-family: fstest-1-3;
src: url(../fonts/csstest-widths-wd3.ttf);
font-stretch: condensed;
}
.fstest-1-4 { font-family: fstest-1-4; }
@font-face {
font-family: fstest-1-4;
src: url(../fonts/csstest-widths-wd1.ttf);
font-stretch: ultra-condensed;
}
@font-face {
font-family: fstest-1-4;
src: url(../fonts/csstest-widths-wd4.ttf);
font-stretch: semi-condensed;
}
.fstest-1-5 { font-family: fstest-1-5; }
@font-face {
font-family: fstest-1-5;
src: url(../fonts/csstest-widths-wd1.ttf);
font-stretch: ultra-condensed;
}
@font-face {
font-family: fstest-1-5;
src: url(../fonts/csstest-widths-wd5.ttf);
font-stretch: normal;
}
.fstest-2-6 { font-family: fstest-2-6; }
@font-face {
font-family: fstest-2-6;
src: url(../fonts/csstest-widths-wd2.ttf);
font-stretch: extra-condensed;
}
@font-face {
font-family: fstest-2-6;
src: url(../fonts/csstest-widths-wd6.ttf);
font-stretch: semi-expanded;
}
.fstest-4-6 { font-family: fstest-4-6; }
@font-face {
font-family: fstest-4-6;
src: url(../fonts/csstest-widths-wd4.ttf);
font-stretch: semi-condensed;
}
@font-face {
font-family: fstest-4-6;
src: url(../fonts/csstest-widths-wd6.ttf);
font-stretch: semi-expanded;
}
.fstest-4-7 { font-family: fstest-4-7; }
@font-face {
font-family: fstest-4-7;
src: url(../fonts/csstest-widths-wd4.ttf);
font-stretch: semi-condensed;
}
@font-face {
font-family: fstest-4-7;
src: url(../fonts/csstest-widths-wd7.ttf);
font-stretch: expanded;
}
.fstest-6-7 { font-family: fstest-6-7; }
@font-face {
font-family: fstest-6-7;
src: url(../fonts/csstest-widths-wd6.ttf);
font-stretch: semi-expanded;
}
@font-face {
font-family: fstest-6-7;
src: url(../fonts/csstest-widths-wd7.ttf);
font-stretch: expanded;
}
.fstest-7-9 { font-family: fstest-7-9; }
@font-face {
font-family: fstest-7-9;
src: url(../fonts/csstest-widths-wd7.ttf);
font-stretch: expanded;
}
@font-face {
font-family: fstest-7-9;
src: url(../fonts/csstest-widths-wd9.ttf);
font-stretch: ultra-expanded;
}
.fstest-8-9 { font-family: fstest-8-9; }
@font-face {
font-family: fstest-8-9;
src: url(../fonts/csstest-widths-wd8.ttf);
font-stretch: extra-expanded;
}
@font-face {
font-family: fstest-8-9;
src: url(../fonts/csstest-widths-wd9.ttf);
font-stretch: ultra-expanded;
}
.fs1 { font-stretch: ultra-condensed; }
.fs2 { font-stretch: extra-condensed; }
.fs3 { font-stretch: condensed; }
.fs4 { font-stretch: semi-condensed; }
.fs5 { font-stretch: normal; }
.fs6 { font-stretch: semi-expanded; }
.fs7 { font-stretch: expanded; }
.fs8 { font-stretch: extra-expanded; }
.fs9 { font-stretch: ultra-expanded; }
</style>
<script type="text/javascript">
</script>
</head>
<body>
<p>font-stretch mapping with different font family sets</p>
<p>(only numbers should appear in the body of the table)</p>
<table>
<thead>
<th>width</th>
<th>full</th>
<th>1-3</th>
<th>1-4</th>
<th>1-5</th>
<th>2-6</th>
<th>4-6</th>
<th>4-7</th>
<th>6-7</th>
<th>7-9</th>
<th>8-9</th>
</thead>
<tr class="fs1">
<th>1</th>
<td class="fstest-full">1</td>
<td class="fstest-1-3">1</td>
<td class="fstest-1-4">1</td>
<td class="fstest-1-5">1</td>
<td class="fstest-2-6">2</td>
<td class="fstest-4-6">4</td>
<td class="fstest-4-7">4</td>
<td class="fstest-6-7">6</td>
<td class="fstest-7-9">7</td>
<td class="fstest-8-9">8</td>
</tr>
<tr class="fs2">
<th>2</th>
<td class="fstest-full">2</td>
<td class="fstest-1-3">1</td>
<td class="fstest-1-4">1</td>
<td class="fstest-1-5">1</td>
<td class="fstest-2-6">2</td>
<td class="fstest-4-6">4</td>
<td class="fstest-4-7">4</td>
<td class="fstest-6-7">6</td>
<td class="fstest-7-9">7</td>
<td class="fstest-8-9">8</td>
</tr>
<tr class="fs3">
<th>3</th>
<td class="fstest-full">3</td>
<td class="fstest-1-3">3</td>
<td class="fstest-1-4">1</td>
<td class="fstest-1-5">1</td>
<td class="fstest-2-6">2</td>
<td class="fstest-4-6">4</td>
<td class="fstest-4-7">4</td>
<td class="fstest-6-7">6</td>
<td class="fstest-7-9">7</td>
<td class="fstest-8-9">8</td>
</tr>
<tr class="fs4">
<th>4</th>
<td class="fstest-full">4</td>
<td class="fstest-1-3">3</td>
<td class="fstest-1-4">4</td>
<td class="fstest-1-5">1</td>
<td class="fstest-2-6">2</td>
<td class="fstest-4-6">4</td>
<td class="fstest-4-7">4</td>
<td class="fstest-6-7">6</td>
<td class="fstest-7-9">7</td>
<td class="fstest-8-9">8</td>
</tr>
<tr class="fs5">
<th>5</th>
<td class="fstest-full">5</td>
<td class="fstest-1-3">3</td>
<td class="fstest-1-4">4</td>
<td class="fstest-1-5">5</td>
<td class="fstest-2-6">2</td>
<td class="fstest-4-6">4</td>
<td class="fstest-4-7">4</td>
<td class="fstest-6-7">6</td>
<td class="fstest-7-9">7</td>
<td class="fstest-8-9">8</td>
</tr>
<tr class="fs6">
<th>6</th>
<td class="fstest-full">6</td>
<td class="fstest-1-3">3</td>
<td class="fstest-1-4">4</td>
<td class="fstest-1-5">5</td>
<td class="fstest-2-6">6</td>
<td class="fstest-4-6">6</td>
<td class="fstest-4-7">7</td>
<td class="fstest-6-7">6</td>
<td class="fstest-7-9">7</td>
<td class="fstest-8-9">8</td>
</tr>
<tr class="fs7">
<th>7</th>
<td class="fstest-full">7</td>
<td class="fstest-1-3">3</td>
<td class="fstest-1-4">4</td>
<td class="fstest-1-5">5</td>
<td class="fstest-2-6">6</td>
<td class="fstest-4-6">6</td>
<td class="fstest-4-7">7</td>
<td class="fstest-6-7">7</td>
<td class="fstest-7-9">7</td>
<td class="fstest-8-9">8</td>
</tr>
<tr class="fs8">
<th>8</th>
<td class="fstest-full">8</td>
<td class="fstest-1-3">3</td>
<td class="fstest-1-4">4</td>
<td class="fstest-1-5">5</td>
<td class="fstest-2-6">6</td>
<td class="fstest-4-6">6</td>
<td class="fstest-4-7">7</td>
<td class="fstest-6-7">7</td>
<td class="fstest-7-9">9</td>
<td class="fstest-8-9">8</td>
</tr>
<tr class="fs9">
<th>9</th>
<td class="fstest-full">9</td>
<td class="fstest-1-3">3</td>
<td class="fstest-1-4">4</td>
<td class="fstest-1-5">5</td>
<td class="fstest-2-6">6</td>
<td class="fstest-4-6">6</td>
<td class="fstest-4-7">7</td>
<td class="fstest-6-7">7</td>
<td class="fstest-7-9">9</td>
<td class="fstest-8-9">9</td>
</tr>
</table>
</body>
</html>

View File

@ -0,0 +1,505 @@
<!DOCTYPE HTML>
<html>
<head>
<title>font-stretch matching tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
body {
margin: 50px;
font-family: Calibri, Verdana, sans-serif;
}
p { margin: 0; padding: 0; }
table {
border-collapse: collapse;
}
th {
font-weight: normal;
background-color: #eee;
}
th, td { width: 3em; text-align: left; }
tr th { text-align: left; }
.fstest-full { font-family: fstest-full; }
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd1.ttf);
font-stretch: ultra-condensed;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd2.ttf);
font-stretch: extra-condensed;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd3.ttf);
font-stretch: condensed;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd4.ttf);
font-stretch: semi-condensed;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd5.ttf);
font-stretch: normal;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd6.ttf);
font-stretch: semi-expanded;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd7.ttf);
font-stretch: expanded;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd8.ttf);
font-stretch: extra-expanded;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd9.ttf);
font-stretch: ultra-expanded;
}
.fstest-1-3 { font-family: fstest-1-3; }
@font-face {
font-family: fstest-1-3;
src: url(../fonts/csstest-widths-wd1.ttf);
font-stretch: ultra-condensed;
}
@font-face {
font-family: fstest-1-3;
src: url(../fonts/csstest-widths-wd3.ttf);
font-stretch: condensed;
}
.fstest-1-4 { font-family: fstest-1-4; }
@font-face {
font-family: fstest-1-4;
src: url(../fonts/csstest-widths-wd1.ttf);
font-stretch: ultra-condensed;
}
@font-face {
font-family: fstest-1-4;
src: url(../fonts/csstest-widths-wd4.ttf);
font-stretch: semi-condensed;
}
.fstest-1-5 { font-family: fstest-1-5; }
@font-face {
font-family: fstest-1-5;
src: url(../fonts/csstest-widths-wd1.ttf);
font-stretch: ultra-condensed;
}
@font-face {
font-family: fstest-1-5;
src: url(../fonts/csstest-widths-wd5.ttf);
font-stretch: normal;
}
.fstest-2-6 { font-family: fstest-2-6; }
@font-face {
font-family: fstest-2-6;
src: url(../fonts/csstest-widths-wd2.ttf);
font-stretch: extra-condensed;
}
@font-face {
font-family: fstest-2-6;
src: url(../fonts/csstest-widths-wd6.ttf);
font-stretch: semi-expanded;
}
.fstest-4-6 { font-family: fstest-4-6; }
@font-face {
font-family: fstest-4-6;
src: url(../fonts/csstest-widths-wd4.ttf);
font-stretch: semi-condensed;
}
@font-face {
font-family: fstest-4-6;
src: url(../fonts/csstest-widths-wd6.ttf);
font-stretch: semi-expanded;
}
.fstest-4-7 { font-family: fstest-4-7; }
@font-face {
font-family: fstest-4-7;
src: url(../fonts/csstest-widths-wd4.ttf);
font-stretch: semi-condensed;
}
@font-face {
font-family: fstest-4-7;
src: url(../fonts/csstest-widths-wd7.ttf);
font-stretch: expanded;
}
.fstest-6-7 { font-family: fstest-6-7; }
@font-face {
font-family: fstest-6-7;
src: url(../fonts/csstest-widths-wd6.ttf);
font-stretch: semi-expanded;
}
@font-face {
font-family: fstest-6-7;
src: url(../fonts/csstest-widths-wd7.ttf);
font-stretch: expanded;
}
.fstest-7-9 { font-family: fstest-7-9; }
@font-face {
font-family: fstest-7-9;
src: url(../fonts/csstest-widths-wd7.ttf);
font-stretch: expanded;
}
@font-face {
font-family: fstest-7-9;
src: url(../fonts/csstest-widths-wd9.ttf);
font-stretch: ultra-expanded;
}
.fstest-8-9 { font-family: fstest-8-9; }
@font-face {
font-family: fstest-8-9;
src: url(../fonts/csstest-widths-wd8.ttf);
font-stretch: extra-expanded;
}
@font-face {
font-family: fstest-8-9;
src: url(../fonts/csstest-widths-wd9.ttf);
font-stretch: ultra-expanded;
}
.fs1 { font-stretch: ultra-condensed; }
.fs2 { font-stretch: extra-condensed; }
.fs3 { font-stretch: condensed; }
.fs4 { font-stretch: semi-condensed; }
.fs5 { font-stretch: normal; }
.fs6 { font-stretch: semi-expanded; }
.fs7 { font-stretch: expanded; }
.fs8 { font-stretch: extra-expanded; }
.fs9 { font-stretch: ultra-expanded; }
</style>
<script type="text/javascript">
</script>
</head>
<body>
<p>font-stretch mapping with different font family sets</p>
<p>(only numbers should appear in the body of the table)</p>
<table>
<thead>
<th>width</th>
<th>full</th>
<th>1-3</th>
<th>1-4</th>
<th>1-5</th>
<th>2-6</th>
<th>4-6</th>
<th>4-7</th>
<th>6-7</th>
<th>7-9</th>
<th>8-9</th>
</thead>
<tr class="fs1">
<th>1</th>
<td class="fstest-full">F</td>
<td class="fstest-1-3">F</td>
<td class="fstest-1-4">F</td>
<td class="fstest-1-5">F</td>
<td class="fstest-2-6">F</td>
<td class="fstest-4-6">F</td>
<td class="fstest-4-7">F</td>
<td class="fstest-6-7">F</td>
<td class="fstest-7-9">F</td>
<td class="fstest-8-9">F</td>
</tr>
<tr class="fs2">
<th>2</th>
<td class="fstest-full">F</td>
<td class="fstest-1-3">F</td>
<td class="fstest-1-4">F</td>
<td class="fstest-1-5">F</td>
<td class="fstest-2-6">F</td>
<td class="fstest-4-6">F</td>
<td class="fstest-4-7">F</td>
<td class="fstest-6-7">F</td>
<td class="fstest-7-9">F</td>
<td class="fstest-8-9">F</td>
</tr>
<tr class="fs3">
<th>3</th>
<td class="fstest-full">F</td>
<td class="fstest-1-3">F</td>
<td class="fstest-1-4">F</td>
<td class="fstest-1-5">F</td>
<td class="fstest-2-6">F</td>
<td class="fstest-4-6">F</td>
<td class="fstest-4-7">F</td>
<td class="fstest-6-7">F</td>
<td class="fstest-7-9">F</td>
<td class="fstest-8-9">F</td>
</tr>
<tr class="fs4">
<th>4</th>
<td class="fstest-full">F</td>
<td class="fstest-1-3">F</td>
<td class="fstest-1-4">F</td>
<td class="fstest-1-5">F</td>
<td class="fstest-2-6">F</td>
<td class="fstest-4-6">F</td>
<td class="fstest-4-7">F</td>
<td class="fstest-6-7">F</td>
<td class="fstest-7-9">F</td>
<td class="fstest-8-9">F</td>
</tr>
<tr class="fs5">
<th>5</th>
<td class="fstest-full">F</td>
<td class="fstest-1-3">F</td>
<td class="fstest-1-4">F</td>
<td class="fstest-1-5">F</td>
<td class="fstest-2-6">F</td>
<td class="fstest-4-6">F</td>
<td class="fstest-4-7">F</td>
<td class="fstest-6-7">F</td>
<td class="fstest-7-9">F</td>
<td class="fstest-8-9">F</td>
</tr>
<tr class="fs6">
<th>6</th>
<td class="fstest-full">F</td>
<td class="fstest-1-3">F</td>
<td class="fstest-1-4">F</td>
<td class="fstest-1-5">F</td>
<td class="fstest-2-6">F</td>
<td class="fstest-4-6">F</td>
<td class="fstest-4-7">F</td>
<td class="fstest-6-7">F</td>
<td class="fstest-7-9">F</td>
<td class="fstest-8-9">F</td>
</tr>
<tr class="fs7">
<th>7</th>
<td class="fstest-full">F</td>
<td class="fstest-1-3">F</td>
<td class="fstest-1-4">F</td>
<td class="fstest-1-5">F</td>
<td class="fstest-2-6">F</td>
<td class="fstest-4-6">F</td>
<td class="fstest-4-7">F</td>
<td class="fstest-6-7">F</td>
<td class="fstest-7-9">F</td>
<td class="fstest-8-9">F</td>
</tr>
<tr class="fs8">
<th>8</th>
<td class="fstest-full">F</td>
<td class="fstest-1-3">F</td>
<td class="fstest-1-4">F</td>
<td class="fstest-1-5">F</td>
<td class="fstest-2-6">F</td>
<td class="fstest-4-6">F</td>
<td class="fstest-4-7">F</td>
<td class="fstest-6-7">F</td>
<td class="fstest-7-9">F</td>
<td class="fstest-8-9">F</td>
</tr>
<tr class="fs9">
<th>9</th>
<td class="fstest-full">F</td>
<td class="fstest-1-3">F</td>
<td class="fstest-1-4">F</td>
<td class="fstest-1-5">F</td>
<td class="fstest-2-6">F</td>
<td class="fstest-4-6">F</td>
<td class="fstest-4-7">F</td>
<td class="fstest-6-7">F</td>
<td class="fstest-7-9">F</td>
<td class="fstest-8-9">F</td>
</tr>
</table>
<!--
<p>Results based on spec logic:</p>
<table>
<thead>
<th>width</th>
<th>full</th>
<th>1-3</th>
<th>1-4</th>
<th>1-5</th>
<th>2-6</th>
<th>4-6</th>
<th>4-7</th>
<th>6-7</th>
<th>7-9</th>
<th>8-9</th>
</thead>
<tr class="fs1">
<th>1</th>
<td class="fstest-full">1</td>
<td class="fstest-1-3">1</td>
<td class="fstest-1-4">1</td>
<td class="fstest-1-5">1</td>
<td class="fstest-2-6">2</td>
<td class="fstest-4-6">4</td>
<td class="fstest-4-7">4</td>
<td class="fstest-6-7">6</td>
<td class="fstest-7-9">7</td>
<td class="fstest-8-9">8</td>
</tr>
<tr class="fs2">
<th>2</th>
<td class="fstest-full">2</td>
<td class="fstest-1-3">1</td>
<td class="fstest-1-4">1</td>
<td class="fstest-1-5">1</td>
<td class="fstest-2-6">2</td>
<td class="fstest-4-6">4</td>
<td class="fstest-4-7">4</td>
<td class="fstest-6-7">6</td>
<td class="fstest-7-9">7</td>
<td class="fstest-8-9">8</td>
</tr>
<tr class="fs3">
<th>3</th>
<td class="fstest-full">3</td>
<td class="fstest-1-3">3</td>
<td class="fstest-1-4">1</td>
<td class="fstest-1-5">1</td>
<td class="fstest-2-6">2</td>
<td class="fstest-4-6">4</td>
<td class="fstest-4-7">4</td>
<td class="fstest-6-7">6</td>
<td class="fstest-7-9">7</td>
<td class="fstest-8-9">8</td>
</tr>
<tr class="fs4">
<th>4</th>
<td class="fstest-full">4</td>
<td class="fstest-1-3">3</td>
<td class="fstest-1-4">4</td>
<td class="fstest-1-5">1</td>
<td class="fstest-2-6">2</td>
<td class="fstest-4-6">4</td>
<td class="fstest-4-7">4</td>
<td class="fstest-6-7">6</td>
<td class="fstest-7-9">7</td>
<td class="fstest-8-9">8</td>
</tr>
<tr class="fs5">
<th>5</th>
<td class="fstest-full">5</td>
<td class="fstest-1-3">3</td>
<td class="fstest-1-4">4</td>
<td class="fstest-1-5">5</td>
<td class="fstest-2-6">2</td>
<td class="fstest-4-6">4</td>
<td class="fstest-4-7">4</td>
<td class="fstest-6-7">6</td>
<td class="fstest-7-9">7</td>
<td class="fstest-8-9">8</td>
</tr>
<tr class="fs6">
<th>6</th>
<td class="fstest-full">6</td>
<td class="fstest-1-3">3</td>
<td class="fstest-1-4">4</td>
<td class="fstest-1-5">5</td>
<td class="fstest-2-6">6</td>
<td class="fstest-4-6">6</td>
<td class="fstest-4-7">7</td>
<td class="fstest-6-7">6</td>
<td class="fstest-7-9">7</td>
<td class="fstest-8-9">8</td>
</tr>
<tr class="fs7">
<th>7</th>
<td class="fstest-full">7</td>
<td class="fstest-1-3">3</td>
<td class="fstest-1-4">4</td>
<td class="fstest-1-5">5</td>
<td class="fstest-2-6">6</td>
<td class="fstest-4-6">6</td>
<td class="fstest-4-7">7</td>
<td class="fstest-6-7">7</td>
<td class="fstest-7-9">7</td>
<td class="fstest-8-9">8</td>
</tr>
<tr class="fs8">
<th>8</th>
<td class="fstest-full">8</td>
<td class="fstest-1-3">3</td>
<td class="fstest-1-4">4</td>
<td class="fstest-1-5">5</td>
<td class="fstest-2-6">6</td>
<td class="fstest-4-6">6</td>
<td class="fstest-4-7">7</td>
<td class="fstest-6-7">7</td>
<td class="fstest-7-9">9</td>
<td class="fstest-8-9">8</td>
</tr>
<tr class="fs9">
<th>9</th>
<td class="fstest-full">9</td>
<td class="fstest-1-3">3</td>
<td class="fstest-1-4">4</td>
<td class="fstest-1-5">5</td>
<td class="fstest-2-6">6</td>
<td class="fstest-4-6">6</td>
<td class="fstest-4-7">7</td>
<td class="fstest-6-7">7</td>
<td class="fstest-7-9">9</td>
<td class="fstest-8-9">9</td>
</tr>
</table>
-->
</body>
</html>

View File

@ -0,0 +1,54 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Assure OS/2 usWidthClass isn't referenced</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd5.ttf);
font-stretch: normal;
}
body {
margin: 50px;
}
p.test {
font-family: fstest-full;
font-size: 24px;
}
.fs9 { font-stretch: ultra-condensed; }
.fs8 { font-stretch: extra-condensed; }
.fs7 { font-stretch: condensed; }
.fs6 { font-stretch: semi-condensed; }
.fs5 { font-stretch: normal; }
.fs4 { font-stretch: semi-expanded; }
.fs3 { font-stretch: expanded; }
.fs2 { font-stretch: extra-expanded; }
.fs1 { font-stretch: ultra-expanded; }
</style>
</head>
<body>
<p>The numbers below should appear in ascending sequence:</p>
<p class="test">
<span class="fs1">1</span>
<span class="fs2">2</span>
<span class="fs3">3</span>
<span class="fs4">4</span>
<span class="fs5">5</span>
<span class="fs6">6</span>
<span class="fs7">7</span>
<span class="fs8">8</span>
<span class="fs9">9</span>
</p>
</body>
</html>

View File

@ -0,0 +1,102 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Assure OS/2 usWidthClass isn't referenced</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd9.ttf);
font-stretch: ultra-condensed;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd8.ttf);
font-stretch: extra-condensed;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd7.ttf);
font-stretch: condensed;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd6.ttf);
font-stretch: semi-condensed;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd5.ttf);
font-stretch: normal;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd4.ttf);
font-stretch: semi-expanded;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd3.ttf);
font-stretch: expanded;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd2.ttf);
font-stretch: extra-expanded;
}
@font-face {
font-family: fstest-full;
src: url(../fonts/csstest-widths-wd1.ttf);
font-stretch: ultra-expanded;
}
body {
margin: 50px;
}
p.test {
font-family: fstest-full;
font-size: 24px;
}
.fs9 { font-stretch: ultra-condensed; }
.fs8 { font-stretch: extra-condensed; }
.fs7 { font-stretch: condensed; }
.fs6 { font-stretch: semi-condensed; }
.fs5 { font-stretch: normal; }
.fs4 { font-stretch: semi-expanded; }
.fs3 { font-stretch: expanded; }
.fs2 { font-stretch: extra-expanded; }
.fs1 { font-stretch: ultra-expanded; }
</style>
</head>
<body>
<p>The numbers below should appear in ascending sequence:</p>
<p class="test">
<span class="fs1">F</span>
<span class="fs2">F</span>
<span class="fs3">F</span>
<span class="fs4">F</span>
<span class="fs5">F</span>
<span class="fs6">F</span>
<span class="fs7">F</span>
<span class="fs8">F</span>
<span class="fs9">F</span>
</p>
</body>
</html>

View File

@ -401,6 +401,12 @@ SpecialPowers.prototype = {
closeLogFile: function() {
this._mfl.close();
},
addCategoryEntry: function(category, entry, value, persists, replace) {
Cc["@mozilla.org/categorymanager;1"].
getService(Components.interfaces.nsICategoryManager).
addCategoryEntry(category, entry, value, persists, replace);
},
};
// Expose everything but internal APIs (starting with underscores) to