From eb1d707ea38bb4b2547599815bd85409456ae2f3 Mon Sep 17 00:00:00 2001 From: Leon Sha Date: Thu, 1 Sep 2011 08:24:41 +0800 Subject: [PATCH 01/23] Bug 683413 - [INFER] jit-test/tests/v8-v5/check-earley-boyer.js failed on sparc. r=bhackett. --- js/src/assembler/assembler/SparcAssembler.h | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/js/src/assembler/assembler/SparcAssembler.h b/js/src/assembler/assembler/SparcAssembler.h index 733b17b194d..17e19f4092d 100644 --- a/js/src/assembler/assembler/SparcAssembler.h +++ b/js/src/assembler/assembler/SparcAssembler.h @@ -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) From 3e0ad0cebaf940fc966be2d59beeef65d8acec2a Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Thu, 1 Sep 2011 12:20:30 -0700 Subject: [PATCH 02/23] [INFER] Improve performance for non-reentrant closures, bug 663138. --- js/src/jit-test/tests/jaeger/closure-01.js | 18 + js/src/jit-test/tests/jaeger/closure-02.js | 14 + js/src/jit-test/tests/jaeger/closure-03.js | 15 + js/src/jsanalyze.cpp | 33 +- js/src/jsanalyze.h | 51 +- js/src/jscompartment.cpp | 3 +- js/src/jsfun.cpp | 28 +- js/src/jsgcmark.cpp | 12 +- js/src/jsinfer.cpp | 638 ++++++++++++++++++--- js/src/jsinfer.h | 170 +++++- js/src/jsinferinlines.h | 88 ++- js/src/jsinterp.cpp | 20 +- js/src/jsinterpinlines.h | 35 +- js/src/jsobj.cpp | 22 +- js/src/jsobj.h | 31 +- js/src/jsobjinlines.h | 48 +- js/src/jsparse.cpp | 26 +- js/src/jsscript.cpp | 13 +- js/src/jsscript.h | 65 ++- js/src/jsscriptinlines.h | 36 +- js/src/methodjit/Compiler.cpp | 198 +++++-- js/src/methodjit/FrameState-inl.h | 13 + js/src/methodjit/FrameState.h | 6 + js/src/methodjit/InvokeHelpers.cpp | 35 +- js/src/methodjit/MethodJIT.cpp | 3 +- js/src/methodjit/MethodJIT.h | 9 +- js/src/methodjit/PolyIC.cpp | 5 +- js/src/methodjit/StubCalls.cpp | 21 + js/src/methodjit/StubCalls.h | 7 +- js/src/methodjit/TrampolineCompiler.cpp | 12 +- js/src/vm/Stack-inl.h | 60 +- js/src/vm/Stack.cpp | 3 +- js/src/vm/Stack.h | 25 +- 33 files changed, 1435 insertions(+), 328 deletions(-) create mode 100644 js/src/jit-test/tests/jaeger/closure-01.js create mode 100644 js/src/jit-test/tests/jaeger/closure-02.js create mode 100644 js/src/jit-test/tests/jaeger/closure-03.js diff --git a/js/src/jit-test/tests/jaeger/closure-01.js b/js/src/jit-test/tests/jaeger/closure-01.js new file mode 100644 index 00000000000..4c82b6fd350 --- /dev/null +++ b/js/src/jit-test/tests/jaeger/closure-01.js @@ -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); diff --git a/js/src/jit-test/tests/jaeger/closure-02.js b/js/src/jit-test/tests/jaeger/closure-02.js new file mode 100644 index 00000000000..902c8f5ae78 --- /dev/null +++ b/js/src/jit-test/tests/jaeger/closure-02.js @@ -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"); diff --git a/js/src/jit-test/tests/jaeger/closure-03.js b/js/src/jit-test/tests/jaeger/closure-03.js new file mode 100644 index 00000000000..e2d1ebe3f85 --- /dev/null +++ b/js/src/jit-test/tests/jaeger/closure-03.js @@ -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(); diff --git a/js/src/jsanalyze.cpp b/js/src/jsanalyze.cpp index e03a1c4615b..51b4d141630 100644 --- a/js/src/jsanalyze.cpp +++ b/js/src/jsanalyze.cpp @@ -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; diff --git a/js/src/jsanalyze.h b/js/src/jsanalyze.h index 679648c1ea6..df2671d0b22 100644 --- a/js/src/jsanalyze.h +++ b/js/src/jsanalyze.h @@ -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); diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index a2fe42b8329..ec7bc1e7b31 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -652,8 +652,7 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) { JSScript *script = reinterpret_cast(cursor); - if (script->types) - script->types->analysis = NULL; + script->clearAnalysis(); } /* Reset the analysis pool, releasing all analysis and intermediate type data. */ diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 8a0ae7c9607..53ac256668c 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -776,6 +776,17 @@ NewCallObject(JSContext *cx, JSScript *script, JSObject &scopeChain, JSObject *c size_t slots = JSObject::CALL_RESERVED_SLOTS + argsVars; gc::FinalizeKind 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; @@ -834,7 +845,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; @@ -1033,7 +1044,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; @@ -1100,7 +1115,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; @@ -2437,9 +2456,8 @@ js_InitFunctionClass(JSContext *cx, JSObject *obj) script->owner = NULL; #endif 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); diff --git a/js/src/jsgcmark.cpp b/js/src/jsgcmark.cpp index fa111830ac2..2f7d52b335d 100644 --- a/js/src/jsgcmark.cpp +++ b/js/src/jsgcmark.cpp @@ -222,8 +222,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) - js_TraceScript(trc, type->functionScript, NULL); + if (type->interpretedFunction) + MarkObject(trc, *type->interpretedFunction, "type_function"); } } @@ -838,7 +838,7 @@ ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type) PushMarkStack(gcmarker, type->proto); if (type->newScript) { - js_TraceScript(gcmarker, type->newScript->script, NULL); + PushMarkStack(gcmarker, type->newScript->fun); PushMarkStack(gcmarker, type->newScript->shape); } @@ -878,12 +878,12 @@ MarkChildren(JSTracer *trc, types::TypeObject *type) MarkObject(trc, *type->singleton, "type_singleton"); if (type->newScript) { - js_TraceScript(trc, type->newScript->script, NULL); + MarkObject(trc, *type->newScript->fun, "type_new_function"); MarkShape(trc, type->newScript->shape, "type_new_shape"); } - if (type->functionScript) - js_TraceScript(trc, type->functionScript, NULL); + if (type->interpretedFunction) + MarkObject(trc, *type->interpretedFunction, "type_function"); } #ifdef JS_HAS_XML_SUPPORT diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 39bae9745f8..52dc5ff9366 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -716,21 +716,32 @@ class TypeConstraintFilterPrimitive : public TypeConstraint { public: TypeSet *target; + TypeSet::FilterKind filter; - /* Primitive types other than null and undefined are passed through. */ - bool onlyNullVoid; - - TypeConstraintFilterPrimitive(TypeSet *target, bool onlyNullVoid) - : TypeConstraint("filter"), target(target), onlyNullVoid(onlyNullVoid) + TypeConstraintFilterPrimitive(TypeSet *target, TypeSet::FilterKind filter) + : TypeConstraint("filter"), target(target), filter(filter) {} void newType(JSContext *cx, TypeSet *source, Type type) { - if (onlyNullVoid) { + switch (filter) { + case TypeSet::FILTER_ALL_PRIMITIVES: + if (type.isPrimitive()) + return; + break; + + case TypeSet::FILTER_NULL_VOID: if (type.isPrimitive(JSVAL_TYPE_NULL) || type.isPrimitive(JSVAL_TYPE_UNDEFINED)) return; - } else if (type.isPrimitive()) { - return; + break; + + case TypeSet::FILTER_VOID: + if (type.isPrimitive(JSVAL_TYPE_UNDEFINED)) + return; + break; + + default: + JS_NOT_REACHED("Bad filter"); } target->addType(cx, type); @@ -738,10 +749,9 @@ public: }; void -TypeSet::addFilterPrimitives(JSContext *cx, TypeSet *target, bool onlyNullVoid) +TypeSet::addFilterPrimitives(JSContext *cx, TypeSet *target, FilterKind filter) { - add(cx, ArenaNew(cx->compartment->pool, - target, onlyNullVoid)); + add(cx, ArenaNew(cx->compartment->pool, target, filter)); } /* If id is a normal slotful 'own' property of an object, get its shape. */ @@ -1103,7 +1113,7 @@ TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type) return; } - JSScript *callee = NULL; + JSFunction *callee = NULL; if (type.isSingleObject()) { JSObject *obj = type.singleObject(); @@ -1159,9 +1169,9 @@ TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type) return; } - callee = obj->getFunctionPrivate()->script(); + callee = obj->getFunctionPrivate(); } else if (type.isTypeObject()) { - callee = type.typeObject()->functionScript; + callee = type.typeObject()->interpretedFunction; if (!callee) return; } else { @@ -1169,30 +1179,27 @@ TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type) return; } - unsigned nargs = callee->function()->nargs; - - if (!callee->ensureHasTypes(cx)) + if (!callee->script()->ensureHasTypes(cx, callee)) return; - /* Analyze the function if we have not already done so. */ - if (!callee->ensureRanInference(cx)) { - cx->compartment->types.setPendingNukeTypes(cx); - return; - } + unsigned nargs = callee->nargs; /* Add bindings for the arguments of the call. */ for (unsigned i = 0; i < callsite->argumentCount && i < nargs; i++) { TypeSet *argTypes = callsite->argumentTypes[i]; - TypeSet *types = TypeScript::ArgTypes(callee, i); + TypeSet *types = TypeScript::ArgTypes(callee->script(), i); argTypes->addSubsetBarrier(cx, script, pc, types); } /* Add void type for any formals in the callee not supplied at the call site. */ for (unsigned i = callsite->argumentCount; i < nargs; i++) { - TypeSet *types = TypeScript::ArgTypes(callee, i); + TypeSet *types = TypeScript::ArgTypes(callee->script(), i); types->addType(cx, Type::UndefinedType()); } + TypeSet *thisTypes = TypeScript::ThisTypes(callee->script()); + TypeSet *returnTypes = TypeScript::ReturnTypes(callee->script()); + if (callsite->isNew) { /* * If the script does not return a value then the pushed value is the @@ -1200,8 +1207,9 @@ TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type) * the new value, which is done dynamically; we don't keep track of the * possible 'new' types for a given prototype type object. */ - TypeScript::ThisTypes(callee)->addSubset(cx, callsite->returnTypes); - TypeScript::ReturnTypes(callee)->addFilterPrimitives(cx, callsite->returnTypes, false); + thisTypes->addSubset(cx, callsite->returnTypes); + returnTypes->addFilterPrimitives(cx, callsite->returnTypes, + TypeSet::FILTER_ALL_PRIMITIVES); } else { /* * Add a binding for the return value of the call. We don't add a @@ -1211,7 +1219,7 @@ TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type) * in the 'this' and 'callee' sets, which we want to maintain for * polymorphic JSOP_CALLPROP invocations. */ - TypeScript::ReturnTypes(callee)->addSubset(cx, callsite->returnTypes); + returnTypes->addSubset(cx, callsite->returnTypes); } } @@ -1230,27 +1238,27 @@ TypeConstraintPropagateThis::newType(JSContext *cx, TypeSet *source, Type type) } /* Ignore calls to natives, these will be handled by TypeConstraintCall. */ - JSScript *callee = NULL; + JSFunction *callee = NULL; if (type.isSingleObject()) { JSObject *object = type.singleObject(); if (!object->isFunction() || !object->getFunctionPrivate()->isInterpreted()) return; - callee = object->getFunctionPrivate()->script(); + callee = object->getFunctionPrivate(); } else if (type.isTypeObject()) { TypeObject *object = type.typeObject(); - if (!object->isFunction() || !object->functionScript) + if (!object->interpretedFunction) return; - callee = object->functionScript; + callee = object->interpretedFunction; } else { /* Ignore calls to primitives, these will go through a stub. */ return; } - if (!callee->ensureHasTypes(cx)) + if (!callee->script()->ensureHasTypes(cx, callee)) return; - TypeScript::ThisTypes(callee)->addType(cx, this->type); + TypeScript::ThisTypes(callee->script())->addType(cx, this->type); } void @@ -1623,7 +1631,7 @@ types::MarkArgumentsCreated(JSContext *cx, JSScript *script) mjit::ExpandInlineFrames(cx->compartment); #endif - if (!script->ensureRanBytecode(cx)) + if (!script->ensureRanAnalysis(cx)) return; ScriptAnalysis *analysis = script->analysis(); @@ -1712,7 +1720,7 @@ public: }; static void -CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script); +CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun); bool TypeSet::isOwnProperty(JSContext *cx, TypeObject *object, bool configurable) @@ -1725,7 +1733,7 @@ TypeSet::isOwnProperty(JSContext *cx, TypeObject *object, bool configurable) */ if (object->flags & OBJECT_FLAG_NEW_SCRIPT_REGENERATE) { if (object->newScript) { - CheckNewScriptProperties(cx, object, object->newScript->script); + CheckNewScriptProperties(cx, object, object->newScript->fun); } else { JS_ASSERT(object->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED); object->flags &= ~OBJECT_FLAG_NEW_SCRIPT_REGENERATE; @@ -1814,6 +1822,64 @@ TypeSet::getSingleton(JSContext *cx, bool freeze) return obj; } +static inline bool +TypeHasGlobal(Type type, JSObject *global) +{ + if (type.isUnknown() || type.isAnyObject()) + return false; + + if (type.isSingleObject()) + return type.singleObject()->getGlobal() == global; + + if (type.isTypeObject()) + return type.typeObject()->getGlobal() == global; + + JS_ASSERT(type.isPrimitive()); + return true; +} + +class TypeConstraintFreezeGlobal : public TypeConstraint +{ +public: + JSScript *script; + JSObject *global; + + TypeConstraintFreezeGlobal(JSScript *script, JSObject *global) + : TypeConstraint("freezeGlobal"), script(script), global(global) + { + JS_ASSERT(global); + } + + void newType(JSContext *cx, TypeSet *source, Type type) + { + if (!global || TypeHasGlobal(type, global)) + return; + + global = NULL; + cx->compartment->types.addPendingRecompile(cx, script); + } +}; + +bool +TypeSet::hasGlobalObject(JSContext *cx, JSObject *global) +{ + if (unknownObject()) + return false; + + unsigned count = getObjectCount(); + for (unsigned i = 0; i < count; i++) { + TypeObjectKey *object = getObject(i); + if (object && !TypeHasGlobal(Type::ObjectType(object), global)) + return false; + } + + add(cx, ArenaNew(cx->compartment->pool, + cx->compartment->types.compiledScript, + global), false); + + return true; +} + ///////////////////////////////////////////////////////////////////// // TypeCompartment ///////////////////////////////////////////////////////////////////// @@ -2860,9 +2926,14 @@ TypeObject::setFlags(JSContext *cx, TypeObjectFlags flags) if (singleton) { /* Make sure flags are consistent with persistent object state. */ JS_ASSERT_IF(flags & OBJECT_FLAG_CREATED_ARGUMENTS, - (flags & OBJECT_FLAG_UNINLINEABLE) && functionScript->createdArgs); - JS_ASSERT_IF(flags & OBJECT_FLAG_UNINLINEABLE, functionScript->uninlineable); - JS_ASSERT_IF(flags & OBJECT_FLAG_ITERATED, singleton->flags & JSObject::ITERATED); + (flags & OBJECT_FLAG_UNINLINEABLE) && + interpretedFunction->script()->createdArgs); + JS_ASSERT_IF(flags & OBJECT_FLAG_UNINLINEABLE, + interpretedFunction->script()->uninlineable); + JS_ASSERT_IF(flags & OBJECT_FLAG_REENTRANT_FUNCTION, + interpretedFunction->script()->reentrantOuterFunction); + JS_ASSERT_IF(flags & OBJECT_FLAG_ITERATED, + singleton->flags & JSObject::ITERATED); } this->flags |= flags; @@ -2953,7 +3024,7 @@ TypeObject::clearNewScript(JSContext *cx) for (FrameRegsIter iter(cx); !iter.done(); ++iter) { StackFrame *fp = iter.fp(); if (fp->isScriptFrame() && fp->isConstructing() && - fp->script() == newScript->script && fp->thisValue().isObject() && + fp->fun() == newScript->fun && fp->thisValue().isObject() && !fp->thisValue().toObject().hasLazyType() && fp->thisValue().toObject().type() == this) { JSObject *obj = &fp->thisValue().toObject(); @@ -3108,6 +3179,109 @@ GetInitializerType(JSContext *cx, JSScript *script, jsbytecode *pc) return TypeScript::InitObject(cx, script, pc, isArray ? JSProto_Array : JSProto_Object); } +/* + * Detach nesting state for script from its parent, removing it entirely if it + * has no children of its own. This happens when walking type information while + * initially resolving NAME accesses, thus will not invalidate any compiler + * dependencies. + */ +static void +DetachNestingParent(JSScript *script) +{ + TypeScriptNesting *nesting = script->nesting(); + + if (!nesting || !nesting->parent) + return; + + /* Remove from parent's list of children. */ + JSScript **pscript = &nesting->parent->nesting()->children; + while ((*pscript)->nesting() != nesting) + pscript = &(*pscript)->nesting()->next; + *pscript = nesting->next; + + nesting->parent = NULL; + + /* If this nesting can have no children of its own, destroy it. */ + if (!script->isOuterFunction) + script->clearNesting(); +} + +ScriptAnalysis::NameAccess +ScriptAnalysis::resolveNameAccess(JSContext *cx, jsid id, bool addDependency) +{ + JS_ASSERT(cx->typeInferenceEnabled()); + + NameAccess access; + PodZero(&access); + + if (!JSID_IS_ATOM(id)) + return access; + JSAtom *atom = JSID_TO_ATOM(id); + + JSScript *script = this->script, *prev = NULL; + while (script->hasFunction && script->nesting()) { + if (!script->ensureRanInference(cx)) + return access; + + /* + * Don't resolve names in scripts which use 'let' or 'with'. New names + * bound here can mask variables of the script itself. + * + * Also, don't resolve names in scripts which are generators. Frame + * balancing works differently for generators and we do not maintain + * active frame counts for such scripts. + */ + if (script->analysis()->addsScopeObjects() || + js_GetOpcode(cx, script, script->code) == JSOP_GENERATOR) { + if (prev) + DetachNestingParent(prev); + DetachNestingParent(script); + return access; + } + + /* Check if the script definitely binds the identifier. */ + uintN index; + BindingKind kind = script->bindings.lookup(cx, atom, &index); + if (kind == ARGUMENT || kind == VARIABLE) { + TypeObject *obj = script->function()->getType(cx); + + if (addDependency) { + /* + * Record the dependency which compiled code has on the outer + * function being non-reentrant. + */ + if (TypeSet::HasObjectFlags(cx, obj, OBJECT_FLAG_REENTRANT_FUNCTION)) + return access; + } + + access.script = script; + access.nesting = script->nesting(); + access.slot = (kind == ARGUMENT) ? ArgSlot(index) : LocalSlot(script, index); + access.arg = (kind == ARGUMENT); + access.index = index; + return access; + } else if (kind != NONE) { + return access; + } + + /* + * If the names bound by the script are extensible (e.g. DEFFUN), + * do not do any further checking in nested parents of the script. + */ + if (script->analysis()->extendsScope()) { + DetachNestingParent(script); + return access; + } + + if (!script->nesting()->parent) + return access; + prev = script; + script = script->nesting()->parent; + } + + return access; +} + /* Analyze type information for a single bytecode. */ bool ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, @@ -3340,7 +3514,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, else id = GetAtomId(cx, script, pc, 0); - TypeSet *seen = script->analysis()->bytecodeTypes(pc); + TypeSet *seen = bytecodeTypes(pc); seen->addSubset(cx, &pushed[0]); /* @@ -3370,13 +3544,19 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, case JSOP_NAME: case JSOP_CALLNAME: { - /* - * The first value pushed by NAME/CALLNAME must always be added to the - * bytecode types, we don't model these opcodes with inference. - */ - TypeSet *seen = script->analysis()->bytecodeTypes(pc); - addTypeBarrier(cx, pc, seen, Type::UnknownType()); + TypeSet *seen = bytecodeTypes(pc); seen->addSubset(cx, &pushed[0]); + + /* Try to resolve this name by walking the function's scope nesting. */ + jsid id = GetAtomId(cx, script, pc, 0); + NameAccess access = resolveNameAccess(cx, id); + if (access.script) { + TypeSet *types = TypeScript::SlotTypes(access.script, access.slot); + types->addSubsetBarrier(cx, script, pc, seen); + } else { + addTypeBarrier(cx, pc, seen, Type::UnknownType()); + } + if (op == JSOP_CALLNAME) { pushed[1].addType(cx, Type::UnknownType()); pushed[0].addPropagateThis(cx, script, pc, Type::UnknownType()); @@ -3396,7 +3576,19 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, break; } - case JSOP_SETNAME: + case JSOP_SETNAME: { + jsid id = GetAtomId(cx, script, pc, 0); + NameAccess access = resolveNameAccess(cx, id); + if (access.script) { + TypeSet *types = TypeScript::SlotTypes(access.script, access.slot); + poppedTypes(pc, 0)->addSubset(cx, types); + } else { + cx->compartment->types.monitorBytecode(cx, script, offset); + } + poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); + break; + } + case JSOP_SETCONST: cx->compartment->types.monitorBytecode(cx, script, offset); poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); @@ -3515,7 +3707,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, seen->addSubset(cx, &pushed[0]); if (op == JSOP_CALLPROP) - poppedTypes(pc, 0)->addFilterPrimitives(cx, &pushed[1], true); + poppedTypes(pc, 0)->addFilterPrimitives(cx, &pushed[1], TypeSet::FILTER_NULL_VOID); if (CheckNextTest(pc)) pushed[0].addType(cx, Type::UndefinedType()); break; @@ -3536,7 +3728,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, seen->addSubset(cx, &pushed[0]); if (op == JSOP_CALLELEM) - poppedTypes(pc, 1)->addFilterPrimitives(cx, &pushed[1], true); + poppedTypes(pc, 1)->addFilterPrimitives(cx, &pushed[1], TypeSet::FILTER_NULL_VOID); if (CheckNextTest(pc)) pushed[0].addType(cx, Type::UndefinedType()); break; @@ -3934,6 +4126,17 @@ ScriptAnalysis::analyzeTypes(JSContext *cx) for (unsigned i = 0; i < script->nfixed; i++) TypeScript::LocalTypes(script, i)->addType(cx, Type::UndefinedType()); + /* + * If a leaf function contains no free variables and no inner functions, + * we can immediately detach it from its parents (destroying its nesting), + * and do not need to maintain its active frame count or watch for + * reentrance in parents. Even if outdated activations of this function + * are live when the parent is called again, we do not need to consider + * this reentrance as no state in the parent's call object will be used. + */ + if (script->hasFunction && !usesScopeChain() && script->nesting()) + DetachNestingParent(script); + TypeInferenceState state(cx); unsigned offset = 0; @@ -4142,7 +4345,7 @@ public: }; static bool -AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script, JSObject **pbaseobj, +AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun, JSObject **pbaseobj, Vector *initializerList) { /* @@ -4163,7 +4366,9 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script, JS return false; } - if (!script->ensureRanInference(cx)) { + JSScript *script = fun->script(); + JS_ASSERT(!script->isInnerFunction); + if (!script->ensureRanAnalysis(cx, fun) || !script->ensureRanInference(cx)) { *pbaseobj = NULL; cx->compartment->types.setPendingNukeTypes(cx); return false; @@ -4345,7 +4550,8 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script, JS return false; } - JSScript *functionScript = scriptObj->getFunctionPrivate()->script(); + JSFunction *function = scriptObj->getFunctionPrivate(); + JS_ASSERT(!function->script()->isInnerFunction); /* * Generate constraints to clear definite properties from the type @@ -4363,7 +4569,7 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script, JS return false; } - if (!AnalyzeNewScriptProperties(cx, type, functionScript, + if (!AnalyzeNewScriptProperties(cx, type, function, pbaseobj, initializerList)) { return false; } @@ -4395,9 +4601,9 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script, JS * newScript on the type after they were cleared by a GC. */ static void -CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script) +CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun) { - if (type->unknownProperties()) + if (type->unknownProperties() || fun->script()->isInnerFunction) return; /* Strawman object to add properties to and watch for duplicates. */ @@ -4409,7 +4615,7 @@ CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script) } Vector initializerList(cx); - AnalyzeNewScriptProperties(cx, type, script, &baseobj, &initializerList); + AnalyzeNewScriptProperties(cx, type, fun, &baseobj, &initializerList); if (!baseobj || baseobj->slotSpan() == 0 || !!(type->flags & OBJECT_FLAG_NEW_SCRIPT_CLEARED)) { if (type->newScript) type->clearNewScript(cx); @@ -4456,7 +4662,7 @@ CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script) return; } - type->newScript->script = script; + type->newScript->fun = fun; type->newScript->finalizeKind = unsigned(kind); type->newScript->shape = baseobj->lastProperty(); @@ -4706,6 +4912,14 @@ IsAboutToBeFinalized(JSContext *cx, TypeObjectKey *key) return !reinterpret_cast((jsuword) key & ~1)->isMarked(); } +inline bool +ScriptIsAboutToBeFinalized(JSContext *cx, JSScript *script, JSFunction *fun) +{ + return script->isCachedEval || + (script->u.object && IsAboutToBeFinalized(cx, script->u.object)) || + (fun && IsAboutToBeFinalized(cx, fun)); +} + void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, Type type) { @@ -4716,7 +4930,7 @@ TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, Type type) /* Directly update associated type sets for applicable bytecodes. */ if (js_CodeSpec[*pc].format & JOF_TYPESET) { - if (!script->ensureRanBytecode(cx)) { + if (!script->ensureRanAnalysis(cx)) { cx->compartment->types.setPendingNukeTypes(cx); return; } @@ -4824,7 +5038,7 @@ TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Val AutoEnterTypeInference enter(cx); - if (!script->ensureRanBytecode(cx)) { + if (!script->ensureRanAnalysis(cx)) { cx->compartment->types.setPendingNukeTypes(cx); return; } @@ -4839,6 +5053,248 @@ TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Val types->addType(cx, type); } +bool +TypeScript::SetScope(JSContext *cx, JSScript *script, JSObject *scope) +{ + JS_ASSERT(script->types && !script->types->hasScope()); + + JSFunction *fun = script->types->function; + + JS_ASSERT(script->hasFunction == (fun != NULL)); + JS_ASSERT_IF(!fun, !script->isOuterFunction && !script->isInnerFunction); + JS_ASSERT_IF(!scope, fun && !script->isInnerFunction); + + /* + * The scope object must be the initial one for the script, before any call + * object has been created in the heavyweight case. + */ + JS_ASSERT_IF(scope && scope->isCall() && !scope->callIsForEval(), + scope->getCallObjCalleeFunction() != fun); + + if (!script->compileAndGo) { + script->types->global = NULL; + return true; + } + + JS_ASSERT_IF(fun && scope, fun->getGlobal() == scope->getGlobal()); + script->types->global = fun ? fun->getGlobal() : scope->getGlobal(); + + if (!cx->typeInferenceEnabled()) + return true; + + if (!script->isInnerFunction) { + /* + * Outermost functions need nesting information if there are inner + * functions directly nested in them. + */ + if (script->isOuterFunction) { + script->types->nesting = cx->new_(); + if (!script->types->nesting) + return false; + } + return true; + } + + /* + * Walk the scope chain to the next call object, which will be the function + * the script is nested inside. + */ + while (!scope->isCall()) + scope = scope->getParent(); + + /* The isInnerFunction test ensures there is no intervening strict eval call object. */ + JS_ASSERT(!scope->callIsForEval()); + + /* Don't track non-heavyweight parents, NAME ops won't reach into them. */ + JSFunction *parentFun = scope->getCallObjCalleeFunction(); + if (!parentFun || !parentFun->isHeavyweight()) + return true; + JSScript *parent = parentFun->script(); + JS_ASSERT(parent->isOuterFunction); + + /* + * We only need the nesting in the child if it has NAME accesses going + * into the parent. We won't know for sure whether this is the case until + * analyzing the script's types, which we don't want to do yet. The nesting + * info we make here may get pruned if/when we eventually do such analysis. + */ + + /* + * Scopes are set when scripts first execute, and the parent script must + * have executed first. It is still possible for the parent script to not + * have a scope, however, as we occasionally purge all TypeScripts from the + * compartment and there may be inner function objects parented to an + * activation of the outer function sticking around. In such cases, treat + * the parent's call object as the most recent one, so that it is not + * marked as reentrant. + */ + if (!parent->ensureHasTypes(cx, parentFun)) + return false; + if (!parent->types->hasScope()) { + if (!SetScope(cx, parent, scope->getParent())) + return false; + parent->nesting()->activeCall = scope; + parent->nesting()->argArray = scope->callObjArgArray(); + parent->nesting()->varArray = scope->callObjVarArray(); + } + + JS_ASSERT(!script->types->nesting); + + /* Construct and link nesting information for the two functions. */ + + script->types->nesting = cx->new_(); + if (!script->types->nesting) + return false; + + script->nesting()->parent = parent; + script->nesting()->next = parent->nesting()->children; + parent->nesting()->children = script; + + return true; +} + +TypeScriptNesting::~TypeScriptNesting() +{ + /* + * Unlink from any parent/child. Nesting info on a script does not keep + * either the parent or children live during GC. + */ + + if (parent) { + JSScript **pscript = &parent->nesting()->children; + while ((*pscript)->nesting() != this) + pscript = &(*pscript)->nesting()->next; + *pscript = next; + } + + while (children) { + TypeScriptNesting *child = children->nesting(); + children = child->next; + child->parent = NULL; + child->next = NULL; + } +} + +bool +ClearActiveNesting(JSScript *start) +{ + /* + * Clear active call information for script and any outer functions + * inner to it. Return false if an inner function has frames on the stack. + */ + + /* Traverse children, then parent, avoiding recursion. */ + JSScript *script = start; + bool traverseChildren = true; + while (true) { + TypeScriptNesting *nesting = script->nesting(); + if (nesting->children && traverseChildren) { + script = nesting->children; + continue; + } + if (nesting->activeFrames) + return false; + if (script->isOuterFunction) { + nesting->activeCall = NULL; + nesting->argArray = NULL; + nesting->varArray = NULL; + } + if (script == start) + break; + if (nesting->next) { + script = nesting->next; + traverseChildren = true; + } else { + script = nesting->parent; + traverseChildren = false; + } + } + + return true; +} + +/* + * For the specified scope and script with an outer function, check if the + * scope represents a reentrant activation on an inner function of the parent + * or any of its transitive parents. + */ +static void +CheckNestingParent(JSContext *cx, JSObject *scope, JSScript *script) +{ + restart: + JSScript *parent = script->nesting()->parent; + JS_ASSERT(parent); + + while (!scope->isCall() || scope->getCallObjCalleeFunction()->script() != parent) + scope = scope->getParent(); + + if (scope != parent->nesting()->activeCall) { + parent->reentrantOuterFunction = true; + MarkTypeObjectFlags(cx, parent->function(), OBJECT_FLAG_REENTRANT_FUNCTION); + + /* + * Continue checking parents to see if this is reentrant for them too. + * We don't need to check this in for non-reentrant calls on the outer + * function: when we entered any outer function to the immediate parent + * we cleared the active call for its transitive children, so a + * non-reentrant call on a child is also a non-reentrant call on the + * parent. + */ + if (parent->nesting()->parent) { + scope = scope->getParent(); + script = parent; + goto restart; + } + } +} + +void +NestingPrologue(JSContext *cx, StackFrame *fp) +{ + JSScript *script = fp->fun()->script(); + TypeScriptNesting *nesting = script->nesting(); + + if (nesting->parent) + CheckNestingParent(cx, &fp->scopeChain(), script); + + if (script->isOuterFunction) { + /* + * Check the stack has no frames for this activation, any of its inner + * functions or any of their transitive inner functions. + */ + if (!ClearActiveNesting(script)) { + script->reentrantOuterFunction = true; + MarkTypeObjectFlags(cx, fp->fun(), OBJECT_FLAG_REENTRANT_FUNCTION); + } + + nesting->activeCall = &fp->callObj(); + nesting->argArray = fp->formalArgs(); + nesting->varArray = fp->slots(); + } + + /* Maintain stack frame count for the function. */ + nesting->activeFrames++; +} + +void +NestingEpilogue(StackFrame *fp) +{ + JSScript *script = fp->fun()->script(); + TypeScriptNesting *nesting = script->nesting(); + + JS_ASSERT(nesting->activeFrames != 0); + nesting->activeFrames--; + + if (script->isOuterFunction) { + /* + * Now that the frame has finished, the call object has been put and + * holds the canonical slots for the call's arguments and variables. + */ + nesting->argArray = nesting->activeCall->callObjArgArray(); + nesting->varArray = nesting->activeCall->callObjVarArray(); + } +} + } } /* namespace js::types */ ///////////////////////////////////////////////////////////////////// @@ -4922,24 +5378,32 @@ IgnorePushed(const jsbytecode *pc, unsigned index) } bool -JSScript::makeTypes(JSContext *cx) +JSScript::makeTypes(JSContext *cx, JSFunction *fun) { JS_ASSERT(!types); + JS_ASSERT(hasFunction == (fun != NULL)); if (!cx->typeInferenceEnabled()) { types = (TypeScript *) cx->calloc_(sizeof(TypeScript)); - return types != NULL; + if (!types) + return false; + new(types) TypeScript(fun); + return true; } AutoEnterTypeInference enter(cx); - unsigned count = TypeScript::NumTypeSets(this); + /* Open code for NumTypeSets since the types are not filled in yet. */ + unsigned count = 2 + (fun ? fun->nargs : 0) + nfixed + nTypeSets; + types = (TypeScript *) cx->calloc_(sizeof(TypeScript) + (sizeof(TypeSet) * count)); if (!types) { cx->compartment->types.setPendingNukeTypes(cx); return false; } + new(types) TypeScript(fun); + #ifdef DEBUG TypeSet *typeArray = types->typeArray(); for (unsigned i = 0; i < nTypeSets; i++) @@ -4998,7 +5462,8 @@ bool JSScript::typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton) { hasFunction = true; - where.fun = fun; + if (fun->isHeavyweight()) + isHeavyweightFunction = true; if (!cx->typeInferenceEnabled()) return true; @@ -5014,7 +5479,7 @@ JSScript::typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton) AutoTypeRooter root(cx, type); fun->setType(type); - type->functionScript = this; + type->interpretedFunction = fun; } return true; @@ -5147,11 +5612,14 @@ JSObject::makeLazyType(JSContext *cx) type->singleton = this; if (isFunction() && getFunctionPrivate() && getFunctionPrivate()->isInterpreted()) { - type->functionScript = getFunctionPrivate()->script(); - if (type->functionScript->uninlineable) - type->flags |= OBJECT_FLAG_UNINLINEABLE; - if (type->functionScript->createdArgs) + type->interpretedFunction = getFunctionPrivate(); + JSScript *script = type->interpretedFunction->script(); + if (script->createdArgs) type->flags |= OBJECT_FLAG_CREATED_ARGUMENTS; + if (script->uninlineable) + type->flags |= OBJECT_FLAG_UNINLINEABLE; + if (script->reentrantOuterFunction) + type->flags |= OBJECT_FLAG_REENTRANT_FUNCTION; } if (flags & ITERATED) @@ -5185,7 +5653,7 @@ JSObject::makeLazyType(JSContext *cx) } void -JSObject::makeNewType(JSContext *cx, JSScript *newScript, bool unknown) +JSObject::makeNewType(JSContext *cx, JSFunction *fun, bool unknown) { JS_ASSERT(!newType); @@ -5210,8 +5678,8 @@ JSObject::makeNewType(JSContext *cx, JSScript *newScript, bool unknown) if (hasSpecialEquality()) type->flags |= OBJECT_FLAG_SPECIAL_EQUALITY; - if (newScript) - CheckNewScriptProperties(cx, type, newScript); + if (fun) + CheckNewScriptProperties(cx, type, fun); #if JS_HAS_XML_SUPPORT /* Special case for XML object equality, see makeLazyType(). */ @@ -5497,7 +5965,7 @@ TypeCompartment::sweep(JSContext *cx) const AllocationSiteKey &key = e.front().key; TypeObject *object = e.front().value; - if (key.script->isAboutToBeFinalized(cx) || !object->isMarked()) + if (ScriptIsAboutToBeFinalized(cx, key.script, key.fun) || !object->isMarked()) e.removeFront(); } } @@ -5537,15 +6005,9 @@ TypeScript::Sweep(JSContext *cx, JSScript *script) unsigned num = NumTypeSets(script); TypeSet *typeArray = script->types->typeArray(); - if (script->isAboutToBeFinalized(cx)) { - /* Release all memory associated with the persistent type sets. */ - for (unsigned i = 0; i < num; i++) - typeArray[i].clearObjects(); - } else { - /* Remove constraints and references to dead objects from the persistent type sets. */ - for (unsigned i = 0; i < num; i++) - typeArray[i].sweep(cx, compartment); - } + /* Remove constraints and references to dead objects from the persistent type sets. */ + for (unsigned i = 0; i < num; i++) + typeArray[i].sweep(cx, compartment); TypeResult **presult = &script->types->dynamicList; while (*presult) { @@ -5561,6 +6023,15 @@ TypeScript::Sweep(JSContext *cx, JSScript *script) } } + /* + * If the script has nesting state with a most recent activation, we do not + * need either to mark the call object or clear it if not live. Even with + * a dead pointer in the nesting, we can't get a spurious match while + * testing for reentrancy: if previous activations are still live, they + * cannot alias the most recent one, and future activations will overwrite + * activeCall on creation. + */ + /* * Method JIT code depends on the type inference data which is about to * be purged, so purge the jitcode as well. @@ -5579,6 +6050,9 @@ TypeScript::destroy() dynamicList = next; } + if (nesting) + Foreground::delete_(nesting); + Foreground::free_(this); } diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 99ed6309ea9..64f0d8d5419 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -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; /* Finalize kind to use for newly constructed objects. */ /* gc::FinalizeKind */ unsigned finalizeKind; @@ -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); @@ -826,6 +839,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); @@ -903,19 +922,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); @@ -967,6 +1098,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(); }; diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index 74e1dbbf813..5367fbafe16 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -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,10 @@ TypeScript::StandardType(JSContext *cx, JSScript *script, JSProtoKey key) struct AllocationSiteKey { JSScript *script; + + /* For determining whether the script is about to be destroyed :XXX: bug 674251 remove */ + JSFunction *fun; + uint32 offset : 24; JSProtoKey kind : 8; @@ -537,6 +548,7 @@ TypeScript::InitObject(JSContext *cx, JSScript *script, const jsbytecode *pc, JS AllocationSiteKey key; key.script = script; + key.fun = script->hasFunction ? script->function() : NULL; key.offset = offset; key.kind = kind; @@ -601,8 +613,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 +627,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 +642,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 +667,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 +689,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 +1113,7 @@ inline TypeObject::TypeObject(JSObject *proto, bool function, bool unknown) PodZero(this); this->proto = proto; + if (function) flags |= OBJECT_FLAG_FUNCTION; if (unknown) @@ -1216,6 +1245,16 @@ 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; +} + class AutoTypeRooter : private AutoGCRooter { public: AutoTypeRooter(JSContext *cx, TypeObject *type @@ -1236,28 +1275,20 @@ class AutoTypeRooter : private AutoGCRooter { } } /* namespace js::types */ inline bool -JSScript::isAboutToBeFinalized(JSContext *cx) +JSScript::ensureHasTypes(JSContext *cx, JSFunction *fun) { - return isCachedEval || - (u.object && IsAboutToBeFinalized(cx, u.object)) || - (hasFunction && IsAboutToBeFinalized(cx, function())); + return types || makeTypes(cx, fun); } inline bool -JSScript::ensureHasTypes(JSContext *cx) +JSScript::ensureRanAnalysis(JSContext *cx, JSFunction *fun, JSObject *scope) { - return types || makeTypes(cx); -} - -inline bool -JSScript::ensureRanBytecode(JSContext *cx) -{ - 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; } @@ -1265,7 +1296,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); @@ -1287,6 +1318,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) diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 6e08389b361..d75779873b6 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -695,7 +695,7 @@ InvokeKernel(JSContext *cx, const CallArgs &argsRef, MaybeConstruct construct) /* 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. */ @@ -742,7 +742,9 @@ InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &this if (fun->isNative()) break; script_ = fun->script(); - if (fun->isHeavyweight() || script_->isEmpty()) + if (!script_->ensureRanAnalysis(cx, fun, callee.getParent())) + return false; + if (FunctionNeedsPrologue(cx, fun) || script_->isEmpty()) break; /* @@ -935,6 +937,9 @@ ExecuteKernel(JSContext *cx, JSScript *script, JSObject &scopeChain, const Value Probes::startExecution(cx, script); + if (!script->ensureRanAnalysis(cx, NULL, &scopeChain)) + return false; + TypeScript::SetThis(cx, script, fp->thisValue()); AutoPreserveEnumerators preserve(cx); @@ -942,6 +947,9 @@ ExecuteKernel(JSContext *cx, JSScript *script, JSObject &scopeChain, const Value if (result && ok) *result = fp->returnValue(); + if (fp->isStrictEvalFrame()) + js_PutCallObject(fp); + Probes::stopExecution(cx, script); return !!ok; @@ -4217,15 +4225,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 @@ -4243,7 +4252,6 @@ BEGIN_CASE(JSOP_FUNAPPLY) } #endif - bool newType = cx->typeInferenceEnabled() && UseNewType(cx, script, regs.pc); if (!ScriptPrologue(cx, regs.fp(), newType)) goto error; diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index 2b2d507d9ce..85ea178a831 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -149,8 +149,18 @@ InvokeSessionGuard::invoke(JSContext *cx) return Invoke(cx, args_); #endif - /* Clear any garbage left from the last Invoke. */ 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; @@ -164,11 +174,20 @@ InvokeSessionGuard::invoke(JSContext *cx) #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; } @@ -331,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) { diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 73955aead30..a9965586fac 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -3012,11 +3012,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()); @@ -3026,7 +3025,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; } @@ -4414,6 +4413,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. @@ -4480,6 +4487,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()); diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 82f4cd0c292..5efda9ade57 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -700,6 +700,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. */ @@ -760,12 +761,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); @@ -855,10 +866,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. */ @@ -1065,6 +1076,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. */ diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 66b43fbc18c..f538555a0c6 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -416,6 +416,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 { @@ -612,6 +623,14 @@ JSObject::setCallObjArg(uintN i, const js::Value &v) setSlot(JSObject::CALL_RESERVED_SLOTS + i, v); } +inline js::Value * +JSObject::callObjArgArray() +{ + js::DebugOnly 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 { @@ -630,6 +649,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 { @@ -833,7 +875,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; @@ -849,12 +891,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; } diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index 2cd88825e09..c758537d3be 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -1138,9 +1138,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. */ @@ -1193,18 +1190,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; @@ -1214,10 +1222,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; diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 431278c8f95..92efbec2312 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -1228,15 +1228,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)); @@ -1517,8 +1508,8 @@ js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner) */ if (!script->isCachedEval && script->u.object) MarkObject(trc, *script->u.object, "object"); - if (script->hasFunction) - MarkObject(trc, *script->function(), "script_fun"); + if (script->types) + script->types->trace(trc); if (IS_GC_MARKING_TRACER(trc) && script->filename) js_MarkScriptFilename(script->filename); diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 73f80733a0f..011899b947b 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -514,12 +514,17 @@ struct JSScript { 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 */ #ifdef JS_METHODJIT bool debugMode:1; /* script was compiled in debug mode */ bool failedBoundsCheck:1; /* script has had hoisted bounds checks fail */ @@ -575,30 +580,6 @@ struct JSScript { public: - 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; - } - - /* - * 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 @@ -613,19 +594,41 @@ struct JSScript { /* 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(); - inline bool isAboutToBeFinalized(JSContext *cx); + /* + * 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: diff --git a/js/src/jsscriptinlines.h b/js/src/jsscriptinlines.h index d05dc38208c..54ee1a6128f 100644 --- a/js/src/jsscriptinlines.h +++ b/js/src/jsscriptinlines.h @@ -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___ */ diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 5011d94cc8f..4bc657a0f67 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -183,7 +183,7 @@ mjit::Compiler::compile() CompileStatus mjit::Compiler::checkAnalysis(JSScript *script) { - if (!script->ensureRanBytecode(cx)) + if (!script->ensureRanAnalysis(cx)) return Compile_Error; if (cx->typeInferenceEnabled() && !script->ensureRanInference(cx)) return Compile_Error; @@ -333,6 +333,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; @@ -737,27 +742,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()) { @@ -775,6 +813,8 @@ mjit::Compiler::generatePrologue() Address(JSFrameReg, StackFrame::offsetOfArgs())); hasArgs.linkTo(masm.label(), &masm); } + + j.linkTo(masm.label(), &masm); } if (cx->typeInferenceEnabled()) { @@ -3100,22 +3140,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); @@ -5027,6 +5076,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. @@ -5189,6 +5254,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); @@ -5246,6 +5331,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); @@ -5305,6 +5408,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 @@ -5896,7 +6016,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)); @@ -5906,6 +6026,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 diff --git a/js/src/methodjit/FrameState-inl.h b/js/src/methodjit/FrameState-inl.h index 2ecd238bb14..36945119bda 100644 --- a/js/src/methodjit/FrameState-inl.h +++ b/js/src/methodjit/FrameState-inl.h @@ -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) { diff --git a/js/src/methodjit/FrameState.h b/js/src/methodjit/FrameState.h index 2971903db29..dd4f19c44d7 100644 --- a/js/src/methodjit/FrameState.h +++ b/js/src/methodjit/FrameState.h @@ -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); diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index a32dd462a0f..93a0668f535 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -360,7 +360,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; /* @@ -496,13 +496,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) { @@ -633,7 +626,7 @@ js_InternalThrow(VMFrame &f) */ cx->compartment->jaegerCompartment()->setLastUnfinished(Jaeger_Unfinished); - if (!script->ensureRanBytecode(cx)) { + if (!script->ensureRanAnalysis(cx)) { js_ReportOutOfMemory(cx); return NULL; } @@ -669,14 +662,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) { @@ -1250,7 +1235,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); } @@ -1394,7 +1379,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 @@ -1405,22 +1390,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: @@ -1492,6 +1472,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; diff --git a/js/src/methodjit/MethodJIT.cpp b/js/src/methodjit/MethodJIT.cpp index 8e2af4e024d..97b9652f4d6 100644 --- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -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; } diff --git a/js/src/methodjit/MethodJIT.h b/js/src/methodjit/MethodJIT.h index 6e250ebf3d0..1dbce5d1ea3 100644 --- a/js/src/methodjit/MethodJIT.h +++ b/js/src/methodjit/MethodJIT.h @@ -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 diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index b26682bbd07..912f162614c 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -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); diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 3adf6878ea5..ab6dd56f6b1 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -2538,6 +2538,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 int32 JS_FASTCALL stubs::ConvertToTypedInt(JSContext *cx, Value *vp) diff --git a/js/src/methodjit/StubCalls.h b/js/src/methodjit/StubCalls.h index 3fdecfb3583..892013bf150 100644 --- a/js/src/methodjit/StubCalls.h +++ b/js/src/methodjit/StubCalls.h @@ -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 @@ -227,6 +225,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); diff --git a/js/src/methodjit/TrampolineCompiler.cpp b/js/src/methodjit/TrampolineCompiler.cpp index eb8a34929af..cc87c059ab7 100644 --- a/js/src/methodjit/TrampolineCompiler.cpp +++ b/js/src/methodjit/TrampolineCompiler.cpp @@ -109,7 +109,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. */ @@ -119,13 +120,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); diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index 70168761f78..78256f0eb2c 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -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 ®s) JS_ASSERT(®s == &seg_->regs()); StackFrame *fp = regs.fp(); - fp->putActivationObjects(); + fp->functionEpilogue(); Value *newsp = fp->actualArgs() - 1; JS_ASSERT(newsp >= fp->prev()->base()); diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index bf3a80e90ae..d6917044568 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -778,7 +778,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_) diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index e47ee2a339a..83f0b47ef56 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -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 From 736384af84e0024fc1f5c711e9e6eed33627390f Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Thu, 1 Sep 2011 12:36:42 -0700 Subject: [PATCH 03/23] [INFER] Use type barriers for NAME accesses on scripts whose types have been purged, bug 663138. --- js/src/jscompartment.cpp | 1 + js/src/jsinfer.cpp | 10 ++++++++-- js/src/jsscript.h | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index ec7bc1e7b31..8731851950a 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -643,6 +643,7 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) if (discardScripts) { script->types->destroy(); script->types = NULL; + script->typesPurged = true; } } } diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 52dc5ff9366..d069472b4c9 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -3547,10 +3547,16 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, TypeSet *seen = bytecodeTypes(pc); seen->addSubset(cx, &pushed[0]); - /* Try to resolve this name by walking the function's scope nesting. */ + /* + * Try to resolve this name by walking the function's scope nesting. + * If we succeed but the accessed script has had its TypeScript purged + * in the past, we still must use a type barrier: the name access can + * be on a call object which predated the purge, and whose types might + * not be reflected in the reconstructed information. + */ jsid id = GetAtomId(cx, script, pc, 0); NameAccess access = resolveNameAccess(cx, id); - if (access.script) { + if (access.script && !access.script->typesPurged) { TypeSet *types = TypeScript::SlotTypes(access.script, access.slot); types->addSubsetBarrier(cx, script, pc, seen); } else { diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 011899b947b..96b2cc271a3 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -525,6 +525,7 @@ struct JSScript { 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 */ From 47973553416b9472067e83f9f4390ff8e29b19dc Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Sat, 3 Sep 2011 11:04:45 -0700 Subject: [PATCH 04/23] [INFER] Don't use invalid activeCall when finishing an outer function frame, detect nesting in parents which add scope objects earlier, bug 663138. --- js/src/jit-test/tests/jaeger/closure-04.js | 23 ++++++++ js/src/jit-test/tests/jaeger/closure-05.js | 17 ++++++ js/src/jsfun.cpp | 10 ++++ js/src/jsinfer.cpp | 63 +++++++++++----------- 4 files changed, 82 insertions(+), 31 deletions(-) create mode 100644 js/src/jit-test/tests/jaeger/closure-04.js create mode 100644 js/src/jit-test/tests/jaeger/closure-05.js diff --git a/js/src/jit-test/tests/jaeger/closure-04.js b/js/src/jit-test/tests/jaeger/closure-04.js new file mode 100644 index 00000000000..72b188f03b2 --- /dev/null +++ b/js/src/jit-test/tests/jaeger/closure-04.js @@ -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(); +} diff --git a/js/src/jit-test/tests/jaeger/closure-05.js b/js/src/jit-test/tests/jaeger/closure-05.js new file mode 100644 index 00000000000..b2aeffa3650 --- /dev/null +++ b/js/src/jit-test/tests/jaeger/closure-05.js @@ -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(""); + } + } +} diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index a8b21db5554..c80a29586fa 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -961,6 +961,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 (js_Invoke). */ diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index a19777f893f..442029d1f9e 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -3214,9 +3214,6 @@ ScriptAnalysis::resolveNameAccess(JSContext *cx, jsid id, bool addDependency) */ if (script->analysis()->addsScopeObjects() || js_GetOpcode(cx, script, script->code) == JSOP_GENERATOR) { - if (prev) - DetachNestingParent(prev); - DetachNestingParent(script); return access; } @@ -3245,15 +3242,6 @@ ScriptAnalysis::resolveNameAccess(JSContext *cx, jsid id, bool addDependency) return access; } - /* - * If the names bound by the script are extensible (e.g. DEFFUN), - * do not do any further checking in nested parents of the script. - */ - if (script->analysis()->extendsScope()) { - DetachNestingParent(script); - return access; - } - if (!script->nesting()->parent) return access; prev = script; @@ -4113,16 +4101,38 @@ ScriptAnalysis::analyzeTypes(JSContext *cx) for (unsigned i = 0; i < script->nfixed; i++) TypeScript::LocalTypes(script, i)->addType(cx, Type::UndefinedType()); - /* - * If a leaf function contains no free variables and no inner functions, - * we can immediately detach it from its parents (destroying its nesting), - * and do not need to maintain its active frame count or watch for - * reentrance in parents. Even if outdated activations of this function - * are live when the parent is called again, we do not need to consider - * this reentrance as no state in the parent's call object will be used. - */ - if (script->hasFunction && !usesScopeChain() && script->nesting()) - DetachNestingParent(script); + TypeScriptNesting *nesting = script->hasFunction ? script->nesting() : NULL; + if (nesting && nesting->parent) { + /* + * Check whether NAME accesses can be resolved in parent scopes, and + * detach from the parent if so. Even if outdated activations of this + * function are live when the parent is called again, we do not need to + * consider this reentrance as no state in the parent will be used. + */ + if (!nesting->parent->ensureRanInference(cx)) + return; + + /* Don't track for leaf scripts which have no free variables. */ + if (!usesScopeChain() && !script->isOuterFunction) + DetachNestingParent(script); + + /* + * If the names bound by the script are extensible (DEFFUN, EVAL, ...), + * don't resolve NAME accesses into the parent. + */ + if (nesting->parent && extendsScope()) + DetachNestingParent(script); + + /* + * Don't track for parents which add call objects or are generators, + * don't resolve NAME accesses into the parent. + */ + if (nesting->parent && + (nesting->parent->analysis()->addsScopeObjects() || + js_GetOpcode(cx, nesting->parent, nesting->parent->code) == JSOP_GENERATOR)) { + DetachNestingParent(script); + } + } TypeInferenceState state(cx); @@ -5276,15 +5286,6 @@ NestingEpilogue(StackFrame *fp) JS_ASSERT(nesting->activeFrames != 0); nesting->activeFrames--; - - if (script->isOuterFunction) { - /* - * Now that the frame has finished, the call object has been put and - * holds the canonical slots for the call's arguments and variables. - */ - nesting->argArray = nesting->activeCall->callObjArgArray(); - nesting->varArray = nesting->activeCall->callObjVarArray(); - } } } } /* namespace js::types */ From c58767db1c19dbb56df6dbb461b34059e265b7f2 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Sun, 4 Sep 2011 13:17:50 -0700 Subject: [PATCH 05/23] [IFER] Fix red. --- js/src/jsscript.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 8e4a73d7599..1adbec2b00d 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -541,7 +541,11 @@ 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 == 32 +#define JS_SCRIPT_INLINE_DATA_LIMIT 0 +#else #define JS_SCRIPT_INLINE_DATA_LIMIT 4 +#endif uint8 inlineData[JS_SCRIPT_INLINE_DATA_LIMIT]; const char *filename; /* source filename or null */ From 59c6ee66945fd413ae78d13ed99c70ca932ec6cb Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Sun, 4 Sep 2011 13:32:13 -0700 Subject: [PATCH 06/23] [INFER] Don't swap newTypes for objects during TradeGuts, bug 684348. r=billm --- js/src/jit-test/tests/basic/bug684348.js | 4 ++++ js/src/jsobj.cpp | 7 +++++++ 2 files changed, 11 insertions(+) create mode 100644 js/src/jit-test/tests/basic/bug684348.js diff --git a/js/src/jit-test/tests/basic/bug684348.js b/js/src/jit-test/tests/basic/bug684348.js new file mode 100644 index 00000000000..58c064fc4a4 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug684348.js @@ -0,0 +1,4 @@ +var x = Proxy.create({ fix: function() { return []; } }); +Object.__proto__ = x; +Object.freeze(x); +quit(); diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index e022b2b1462..dfd09345759 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -3732,6 +3732,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()) { @@ -3802,6 +3806,9 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved & reserved.newaslots = NULL; reserved.newbslots = NULL; } + + a->newType = newTypeA; + b->newType = newTypeB; } /* From 4c2126dce844ec5d3edabac583fa6ecadb26ad74 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Sun, 4 Sep 2011 13:33:04 -0700 Subject: [PATCH 07/23] [INFER] Compiler types should reflect inferred types for JSOP_THIS in scripts which have not executed, bug 684084. --- js/src/jit-test/tests/jaeger/bug684084.js | 7 +++++++ js/src/methodjit/Compiler.cpp | 15 ++++++++++++--- js/src/methodjit/StubCalls.cpp | 13 +++++++++++++ js/src/methodjit/StubCalls.h | 1 + 4 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 js/src/jit-test/tests/jaeger/bug684084.js diff --git a/js/src/jit-test/tests/jaeger/bug684084.js b/js/src/jit-test/tests/jaeger/bug684084.js new file mode 100644 index 00000000000..ac2c11a3844 --- /dev/null +++ b/js/src/jit-test/tests/jaeger/bug684084.js @@ -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 ); diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 5f32cb6b688..e19b5326b3b 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -2740,9 +2740,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) @@ -5577,6 +5576,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); diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index b512720c3de..88ffec9bdcb 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -2195,6 +2195,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) { diff --git a/js/src/methodjit/StubCalls.h b/js/src/methodjit/StubCalls.h index 021f8806af0..374ed93d4dd 100644 --- a/js/src/methodjit/StubCalls.h +++ b/js/src/methodjit/StubCalls.h @@ -200,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, From 799c6dfea02e21e3906d50db8fb586e680bda9e8 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Sun, 4 Sep 2011 13:33:33 -0700 Subject: [PATCH 08/23] Don't try to recompile scripts when clearing traps during GC, bug 683966. --- js/src/vm/Debugger.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 7b3b0d179db..a308572ef02 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -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); } } From 27f0eb58806ccba57d648fe37bb58e4c4322b4c3 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Sun, 4 Sep 2011 13:34:08 -0700 Subject: [PATCH 09/23] [INFER] Always clear jitcode during GC with inference enabled, bug 683227. r=dvander --- js/src/jscompartment.cpp | 24 +++++++++++++++++++----- js/src/methodjit/Compiler.cpp | 8 ++++++++ js/src/methodjit/Compiler.h | 1 + 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 71d59f8b710..b4073fd2a98 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -599,9 +599,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(); + 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; @@ -613,9 +630,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(); if (script->types) { diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index e19b5326b3b..ff95454c67a 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -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) { @@ -886,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); diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index 0c689a64398..39e0e98c58d 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -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; From 2ac630826269c55fe13b310491f5fd36bc4ae774 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Sun, 4 Sep 2011 13:34:38 -0700 Subject: [PATCH 10/23] [INFER] Fix bogus assert, allow Disassemble() to be called during GC/arena traversal, bug 684281. --- js/src/jsinfer.cpp | 35 +++++++---------------------------- js/src/jsobjinlines.h | 2 +- js/src/jsopcode.cpp | 26 ++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 29 deletions(-) diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 442029d1f9e..ac3f08c11d4 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -2346,28 +2346,6 @@ ScriptAnalysis::addSingletonTypeBarrier(JSContext *cx, const jsbytecode *pc, Typ code.typeBarriers = barrier; } -static void -PrintScriptTypeCallback(JSContext *cx, void *data, void *thing, - JSGCTraceKind traceKind, size_t thingSize) -{ - JS_ASSERT(!data); - JS_ASSERT(traceKind == JSTRACE_SCRIPT); - JSScript *script = static_cast(thing); - if (script->hasAnalysis() && script->analysis()->ranInference()) - script->analysis()->printTypes(cx); -} - -#ifdef DEBUG -static void -PrintObjectCallback(JSContext *cx, void *data, void *thing, - JSGCTraceKind traceKind, size_t thingSize) -{ - JS_ASSERT(traceKind == JSTRACE_OBJECT); - TypeObject *object = (TypeObject *) thing; - object->print(cx); -} -#endif - void TypeCompartment::print(JSContext *cx, bool force) { @@ -2376,15 +2354,16 @@ TypeCompartment::print(JSContext *cx, bool force) if (!force && !InferSpewActive(ISpewResult)) return; - { - AutoUnlockGC unlock(cx->runtime); - IterateCells(cx, compartment, gc::FINALIZE_SCRIPT, cx, PrintScriptTypeCallback); + for (gc::CellIter i(cx, compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) { + JSScript *script = i.get(); + if (script->hasAnalysis() && script->analysis()->ranInference()) + script->analysis()->printTypes(cx); } #ifdef DEBUG - { - AutoUnlockGC unlock(cx->runtime); - IterateCells(cx, compartment, gc::FINALIZE_TYPE_OBJECT, NULL, PrintObjectCallback); + for (gc::CellIter i(cx, compartment, gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) { + TypeObject *object = i.get(); + object->print(cx); } #endif diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 2aef163e174..cabf1154e0b 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -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; diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 04480ebd528..a44a9ebb31f 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -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, ""); + if (!source) + return false; + bytes->initBytes(source); + return true; + } + if (!JSVAL_IS_PRIMITIVE(v)) { JSObject *obj = JSVAL_TO_OBJECT(v); Class *clasp = obj->getClass(); From d78ee3ef8ab82b7f41bae4546d6f3b049cf76a0f Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Mon, 5 Sep 2011 07:29:51 -0700 Subject: [PATCH 11/23] [INFER] Fix test for when double entries need to be forgotten after branching to a location their type is unknown, bug 684576. --- js/src/jit-test/tests/jaeger/bug684576.js | 10 ++++++++++ js/src/methodjit/Compiler.cpp | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 js/src/jit-test/tests/jaeger/bug684576.js diff --git a/js/src/jit-test/tests/jaeger/bug684576.js b/js/src/jit-test/tests/jaeger/bug684576.js new file mode 100644 index 00000000000..b845d2160d2 --- /dev/null +++ b/js/src/jit-test/tests/jaeger/bug684576.js @@ -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); diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index ff95454c67a..40deee57baa 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -7092,7 +7092,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); From fb7cef441c16f82b821dc796e1d8f1781f16356a Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Mon, 5 Sep 2011 07:31:30 -0700 Subject: [PATCH 12/23] [INFER] Fix bug 684594. --- js/src/methodjit/LoopState.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/js/src/methodjit/LoopState.cpp b/js/src/methodjit/LoopState.cpp index 68d019e9ef2..c14b92ef868 100644 --- a/js/src/methodjit/LoopState.cpp +++ b/js/src/methodjit/LoopState.cpp @@ -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); } From dc782a5ed8196d66890c41e6b4b8e47939b11b23 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Mon, 5 Sep 2011 07:33:06 -0700 Subject: [PATCH 13/23] [INFER] Allow uncopies of loop temporaries after backedges, bug 684621. --- js/src/jit-test/tests/jaeger/loops/bug684621.js | 15 +++++++++++++++ js/src/methodjit/FrameState.cpp | 7 +++---- 2 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 js/src/jit-test/tests/jaeger/loops/bug684621.js diff --git a/js/src/jit-test/tests/jaeger/loops/bug684621.js b/js/src/jit-test/tests/jaeger/loops/bug684621.js new file mode 100644 index 00000000000..9ca13bc7f4c --- /dev/null +++ b/js/src/jit-test/tests/jaeger/loops/bug684621.js @@ -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() diff --git a/js/src/methodjit/FrameState.cpp b/js/src/methodjit/FrameState.cpp index 21b2502ed52..347ba7d083d 100644 --- a/js/src/methodjit/FrameState.cpp +++ b/js/src/methodjit/FrameState.cpp @@ -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(); } From c80c38c904cb3d12ec63b7acd62604cc23e9bf0a Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Mon, 5 Sep 2011 07:34:27 -0700 Subject: [PATCH 14/23] [INFER] Fix bug 684623. --- js/src/jsgcmark.cpp | 2 +- js/src/jsgcmark.h | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/js/src/jsgcmark.cpp b/js/src/jsgcmark.cpp index 44155d4fc83..d88dab6d0f2 100644 --- a/js/src/jsgcmark.cpp +++ b/js/src/jsgcmark.cpp @@ -415,7 +415,7 @@ MarkKind(JSTracer *trc, void *thing, JSGCTraceKind kind) Mark(trc, reinterpret_cast(thing)); break; case JSTRACE_TYPE_OBJECT: - Mark(trc, reinterpret_cast(thing)); + MarkTypeObject(trc, reinterpret_cast(thing), "type_stack"); break; #if JS_HAS_XML_SUPPORT case JSTRACE_XML: diff --git a/js/src/jsgcmark.h b/js/src/jsgcmark.h index 513bb432d47..125a2409963 100644 --- a/js/src/jsgcmark.h +++ b/js/src/jsgcmark.h @@ -50,9 +50,6 @@ namespace js { namespace gc { -template -void Mark(JSTracer *trc, T *thing); - void MarkString(JSTracer *trc, JSString *str); From 85997e5de5ebe0cc751bb155eecb86a7651c29f1 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Mon, 5 Sep 2011 09:05:45 -0700 Subject: [PATCH 15/23] [INFER] Fix test failures. --- js/src/jscntxt.h | 2 +- js/src/jsinfer.cpp | 16 +++++++++++----- js/src/jsscript.h | 6 ++---- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index c2aa3a0e807..0cea834c5f0 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1496,7 +1496,7 @@ class AutoGCRooter { DESCRIPTOR = -13, /* js::AutoPropertyDescriptorRooter */ STRING = -14, /* js::AutoStringRooter */ IDVECTOR = -15, /* js::AutoIdVector */ - OBJVECTOR = -16, /* js::AutoObjectVector */ + OBJVECTOR = -16 /* js::AutoObjectVector */ }; private: diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index ac3f08c11d4..3b5ab0a365d 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -3178,7 +3178,7 @@ ScriptAnalysis::resolveNameAccess(JSContext *cx, jsid id, bool addDependency) return access; JSAtom *atom = JSID_TO_ATOM(id); - JSScript *script = this->script, *prev = NULL; + JSScript *script = this->script; while (script->hasFunction && script->nesting()) { if (!script->ensureRanInference(cx)) return access; @@ -3223,7 +3223,6 @@ ScriptAnalysis::resolveNameAccess(JSContext *cx, jsid id, bool addDependency) if (!script->nesting()->parent) return access; - prev = script; script = script->nesting()->parent; } @@ -4091,25 +4090,32 @@ ScriptAnalysis::analyzeTypes(JSContext *cx) if (!nesting->parent->ensureRanInference(cx)) return; + bool detached = false; + /* Don't track for leaf scripts which have no free variables. */ - if (!usesScopeChain() && !script->isOuterFunction) + if (!usesScopeChain() && !script->isOuterFunction) { DetachNestingParent(script); + detached = true; + } /* * If the names bound by the script are extensible (DEFFUN, EVAL, ...), * don't resolve NAME accesses into the parent. */ - if (nesting->parent && extendsScope()) + if (!detached && extendsScope()) { DetachNestingParent(script); + detached = true; + } /* * Don't track for parents which add call objects or are generators, * don't resolve NAME accesses into the parent. */ - if (nesting->parent && + if (!detached && (nesting->parent->analysis()->addsScopeObjects() || js_GetOpcode(cx, nesting->parent, nesting->parent->code) == JSOP_GENERATOR)) { DetachNestingParent(script); + detached = true; } } diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 1adbec2b00d..845bb62122a 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -541,12 +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 == 32 -#define JS_SCRIPT_INLINE_DATA_LIMIT 0 -#else +#if JS_BITS_PER_WORD == 64 #define JS_SCRIPT_INLINE_DATA_LIMIT 4 -#endif uint8 inlineData[JS_SCRIPT_INLINE_DATA_LIMIT]; +#endif const char *filename; /* source filename or null */ JSAtom **atoms; /* maps immediate index to literal struct */ From 4b4c3e28d42366ac02e7a70da9d7fc4d59fa9ab7 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Tue, 6 Sep 2011 03:08:29 -0700 Subject: [PATCH 16/23] [INFER] Fix red. --- js/src/xpconnect/src/xpcjsruntime.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index 3860dce81dd..c17dbd50671 100644 --- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -1366,7 +1366,10 @@ CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind, case JSTRACE_SCRIPT: { JSScript *script = static_cast(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(); } From 70458f0a710a704bcefc45e00f742e27d9cec419 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Tue, 6 Sep 2011 14:41:31 +0100 Subject: [PATCH 17/23] Backout a422b9ff0a9e (bug 591780 part 1) for causing 8% Ts regression on multiple platforms; a=khuey --- browser/base/content/nsContextMenu.js | 6 +- editor/composer/src/nsEditorSpellCheck.cpp | 14 +- editor/idl/nsIEditorSpellCheck.idl | 11 +- editor/libeditor/base/Makefile.in | 1 - editor/libeditor/base/nsEditor.cpp | 63 +----- editor/libeditor/base/nsEditor.h | 5 - editor/txtsvc/public/nsISpellChecker.h | 12 +- extensions/spellcheck/Makefile.in | 4 - .../spellcheck/hunspell/src/Makefile.in | 2 - .../spellcheck/hunspell/src/mozHunspell.cpp | 94 +------- .../spellcheck/hunspell/src/mozHunspell.h | 6 - .../idl/mozISpellCheckingEngine.idl | 33 +-- .../spellcheck/src/mozInlineSpellChecker.cpp | 12 +- .../spellcheck/src/mozInlineSpellChecker.h | 4 +- extensions/spellcheck/src/mozSpellChecker.cpp | 212 ++++++++---------- extensions/spellcheck/src/mozSpellChecker.h | 10 +- .../spellcheck/src/mozSpellCheckerFactory.cpp | 34 ++- extensions/spellcheck/tests/Makefile.in | 48 ---- .../spellcheck/tests/chrome/Makefile.in | 54 ----- .../spellcheck/tests/chrome/base/Makefile.in | 52 ----- .../spellcheck/tests/chrome/base/base_utf.aff | 198 ---------------- .../spellcheck/tests/chrome/base/base_utf.dic | 29 --- .../spellcheck/tests/chrome/map/Makefile.in | 52 ----- .../spellcheck/tests/chrome/map/maputf.aff | 11 - .../spellcheck/tests/chrome/map/maputf.dic | 4 - .../chrome/test_add_remove_dictionaries.xul | 110 --------- 26 files changed, 162 insertions(+), 919 deletions(-) delete mode 100644 extensions/spellcheck/tests/Makefile.in delete mode 100644 extensions/spellcheck/tests/chrome/Makefile.in delete mode 100644 extensions/spellcheck/tests/chrome/base/Makefile.in delete mode 100644 extensions/spellcheck/tests/chrome/base/base_utf.aff delete mode 100644 extensions/spellcheck/tests/chrome/base/base_utf.dic delete mode 100644 extensions/spellcheck/tests/chrome/map/Makefile.in delete mode 100644 extensions/spellcheck/tests/chrome/map/maputf.aff delete mode 100644 extensions/spellcheck/tests/chrome/map/maputf.dic delete mode 100644 extensions/spellcheck/tests/chrome/test_add_remove_dictionaries.xul diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js index 4ab61794d09..b4f9aacc043 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -321,8 +321,10 @@ nsContextMenu.prototype = { var onMisspelling = InlineSpellCheckerUI.overMisspelling; this.showItem("spell-check-enabled", canSpell); this.showItem("spell-separator", canSpell || this.onEditableArea); - document.getElementById("spell-check-enabled") - .setAttribute("checked", canSpell && InlineSpellCheckerUI.enabled); + if (canSpell) { + document.getElementById("spell-check-enabled") + .setAttribute("checked", InlineSpellCheckerUI.enabled); + } this.showItem("spell-add-to-dictionary", onMisspelling); diff --git a/editor/composer/src/nsEditorSpellCheck.cpp b/editor/composer/src/nsEditorSpellCheck.cpp index cd8961d6492..ecaa421472e 100644 --- a/editor/composer/src/nsEditorSpellCheck.cpp +++ b/editor/composer/src/nsEditorSpellCheck.cpp @@ -650,14 +650,6 @@ nsEditorSpellCheck::SetCurrentDictionary(const nsAString& aDictionary) return mSpellChecker->SetCurrentDictionary(aDictionary); } -NS_IMETHODIMP -nsEditorSpellCheck::GetSpellChecker(nsISpellChecker **aSpellChecker) -{ - *aSpellChecker = mSpellChecker; - NS_IF_ADDREF(*aSpellChecker); - return NS_OK; -} - NS_IMETHODIMP nsEditorSpellCheck::UninitSpellChecker() { @@ -826,7 +818,11 @@ nsEditorSpellCheck::UpdateCurrentDictionary() if (NS_FAILED(rv) || currentDictionary.IsEmpty()) { rv = SetCurrentDictionary(NS_LITERAL_STRING("en-US")); if (NS_FAILED(rv)) { - mSpellChecker->CheckCurrentDictionary(); + nsTArray dictList; + rv = mSpellChecker->GetDictionaryList(&dictList); + if (NS_SUCCEEDED(rv) && dictList.Length() > 0) { + SetCurrentDictionary(dictList[0]); + } } } } diff --git a/editor/idl/nsIEditorSpellCheck.idl b/editor/idl/nsIEditorSpellCheck.idl index 0ccb5fa4195..f06eabff715 100644 --- a/editor/idl/nsIEditorSpellCheck.idl +++ b/editor/idl/nsIEditorSpellCheck.idl @@ -40,20 +40,11 @@ interface nsIEditor; interface nsITextServicesFilter; -%{C++ -#include "nsISpellChecker.h" -%} -[ptr] native nsISpellChecker(nsISpellChecker); -[scriptable, uuid(334946c3-0e93-4aac-b662-e1b56f95d68b)] +[scriptable, uuid(af84da62-588f-409f-847d-feecc991bd93)] interface nsIEditorSpellCheck : nsISupports { - /** - * Get the spell checker used by this editor. - */ - [noscript] readonly attribute nsISpellChecker spellChecker; - /** * Returns true if we can enable spellchecking. If there are no available * dictionaries, this will return false. diff --git a/editor/libeditor/base/Makefile.in b/editor/libeditor/base/Makefile.in index 66b7463345a..acedb86f499 100644 --- a/editor/libeditor/base/Makefile.in +++ b/editor/libeditor/base/Makefile.in @@ -94,5 +94,4 @@ INCLUDES += \ -I$(topsrcdir)/content/base/src \ -I$(topsrcdir)/content/events/src \ -I$(topsrcdir)/layout/style \ - -I$(topsrcdir)/extensions/spellcheck/src \ $(NULL) diff --git a/editor/libeditor/base/nsEditor.cpp b/editor/libeditor/base/nsEditor.cpp index 21562c45454..d49f5acc50c 100644 --- a/editor/libeditor/base/nsEditor.cpp +++ b/editor/libeditor/base/nsEditor.cpp @@ -24,7 +24,6 @@ * Daniel Glazman * Masayuki Nakano * Mats Palmgren - * Jesper Kristensen * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -49,11 +48,6 @@ #include "nsFocusManager.h" #include "nsUnicharUtils.h" #include "nsReadableUtils.h" -#include "nsIObserverService.h" -#include "mozilla/Services.h" -#include "mozISpellCheckingEngine.h" -#include "nsIEditorSpellCheck.h" -#include "mozInlineSpellChecker.h" #include "nsIDOMText.h" #include "nsIDOMElement.h" @@ -213,7 +207,6 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsEditor) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY(nsIEditorIMESupport) NS_INTERFACE_MAP_ENTRY(nsIEditor) - NS_INTERFACE_MAP_ENTRY(nsIObserver) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditor) NS_INTERFACE_MAP_END @@ -307,13 +300,6 @@ nsEditor::PostCreate() // update the UI with our state NotifyDocumentListeners(eDocumentCreated); NotifyDocumentListeners(eDocumentStateChanged); - - nsCOMPtr obs = mozilla::services::GetObserverService(); - if (obs) { - obs->AddObserver(this, - SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION, - PR_FALSE); - } } // update nsTextStateManager and caret if we have focus @@ -426,12 +412,6 @@ nsEditor::PreDestroy(PRBool aDestroyingFrames) if (mDidPreDestroy) return NS_OK; - nsCOMPtr obs = mozilla::services::GetObserverService(); - if (obs) { - obs->RemoveObserver(this, - SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION); - } - // Let spellchecker clean up its observers etc. It is important not to // actually free the spellchecker here, since the spellchecker could have // caused flush notifications, which could have gotten here if a textbox @@ -1303,13 +1283,6 @@ NS_IMETHODIMP nsEditor::GetInlineSpellChecker(PRBool autoCreate, return autoCreate ? NS_ERROR_NOT_AVAILABLE : NS_OK; } - // We don't want to show the spell checking UI if there are no spell check dictionaries available. - PRBool canSpell = mozInlineSpellChecker::CanEnableInlineSpellChecking(); - if (!canSpell) { - *aInlineSpellChecker = nsnull; - return NS_ERROR_FAILURE; - } - nsresult rv; if (!mInlineSpellChecker && autoCreate) { mInlineSpellChecker = do_CreateInstance(MOZ_INLINESPELLCHECKER_CONTRACTID, &rv); @@ -1328,49 +1301,17 @@ NS_IMETHODIMP nsEditor::GetInlineSpellChecker(PRBool autoCreate, return NS_OK; } -NS_IMETHODIMP nsEditor::Observe(nsISupports* aSubj, const char *aTopic, - const PRUnichar *aData) -{ - NS_ASSERTION(!strcmp(aTopic, - SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION), - "Unexpected observer topic"); - - // When mozInlineSpellChecker::CanEnableInlineSpellChecking changes - SyncRealTimeSpell(); - - // When nsIEditorSpellCheck::GetCurrentDictionary changes - if (mInlineSpellChecker) { - // if the current dictionary is no longer available, find another one - nsCOMPtr editorSpellCheck; - mInlineSpellChecker->GetSpellChecker(getter_AddRefs(editorSpellCheck)); - if (editorSpellCheck) { - nsCOMPtr spellChecker; - editorSpellCheck->GetSpellChecker(getter_AddRefs(spellChecker)); - spellChecker->CheckCurrentDictionary(); - } - - // update the inline spell checker to reflect the new current dictionary - mInlineSpellChecker->SpellCheckRange(nsnull); // causes recheck - } - - return NS_OK; -} - NS_IMETHODIMP nsEditor::SyncRealTimeSpell() { NS_TIME_FUNCTION; PRBool enable = GetDesiredSpellCheckState(); - // Initializes mInlineSpellChecker nsCOMPtr spellChecker; GetInlineSpellChecker(enable, getter_AddRefs(spellChecker)); - if (mInlineSpellChecker) { - // We might have a mInlineSpellChecker even if there are no dictionaries - // available since we don't destroy the mInlineSpellChecker when the last - // dictionariy is removed, but in that case spellChecker is null - mInlineSpellChecker->SetEnableRealTimeSpell(enable && spellChecker); + if (spellChecker) { + spellChecker->SetEnableRealTimeSpell(enable); } return NS_OK; diff --git a/editor/libeditor/base/nsEditor.h b/editor/libeditor/base/nsEditor.h index e5fc4a8b2a4..1ef527abf67 100644 --- a/editor/libeditor/base/nsEditor.h +++ b/editor/libeditor/base/nsEditor.h @@ -66,7 +66,6 @@ #include "nsStubMutationObserver.h" #include "nsIViewManager.h" #include "nsCycleCollectionParticipant.h" -#include "nsIObserver.h" class nsIDOMCharacterData; class nsIDOMRange; @@ -101,7 +100,6 @@ class nsIDOMNSEvent; class nsEditor : public nsIEditor, public nsIEditorIMESupport, public nsSupportsWeakReference, - public nsIObserver, public nsIPhonetic { public: @@ -155,9 +153,6 @@ public: /* ------------ nsIEditorIMESupport methods -------------- */ NS_DECL_NSIEDITORIMESUPPORT - /* ------------ nsIObserver methods -------------- */ - NS_DECL_NSIOBSERVER - // nsIPhonetic NS_DECL_NSIPHONETIC diff --git a/editor/txtsvc/public/nsISpellChecker.h b/editor/txtsvc/public/nsISpellChecker.h index b3545d5dad4..d1644df77fe 100644 --- a/editor/txtsvc/public/nsISpellChecker.h +++ b/editor/txtsvc/public/nsISpellChecker.h @@ -44,9 +44,9 @@ #define NS_SPELLCHECKER_CONTRACTID "@mozilla.org/spellchecker;1" #define NS_ISPELLCHECKER_IID \ -{ /* 27bff957-b486-40ae-9f5d-af0cdd211868 */ \ -0x27bff957, 0xb486, 0x40ae, \ - { 0x9f, 0x5d, 0xaf, 0x0c, 0xdd, 0x21, 0x18, 0x68 } } +{ /* E75AC48C-E948-452E-8DB3-30FEE29FE3D2 */ \ +0xe75ac48c, 0xe948, 0x452e, \ + { 0x8d, 0xb3, 0x30, 0xfe, 0xe2, 0x9f, 0xe3, 0xd2 } } class nsITextServicesDocument; class nsString; @@ -146,12 +146,6 @@ public: * empty string, spellchecker will be disabled. */ NS_IMETHOD SetCurrentDictionary(const nsAString &aDictionary) = 0; - - /** - * Call this on any change in installed dictionaries to ensure that the spell - * checker is not using a current dictionary which is no longer available. - */ - NS_IMETHOD CheckCurrentDictionary() = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsISpellChecker, NS_ISPELLCHECKER_IID) diff --git a/extensions/spellcheck/Makefile.in b/extensions/spellcheck/Makefile.in index b270c3e9006..e7895eacba6 100644 --- a/extensions/spellcheck/Makefile.in +++ b/extensions/spellcheck/Makefile.in @@ -44,8 +44,4 @@ include $(DEPTH)/config/autoconf.mk MODULE = spellchecker DIRS = idl locales hunspell src -ifdef ENABLE_TESTS -DIRS += tests -endif - include $(topsrcdir)/config/rules.mk diff --git a/extensions/spellcheck/hunspell/src/Makefile.in b/extensions/spellcheck/hunspell/src/Makefile.in index c53c279bc10..7ca1316e308 100644 --- a/extensions/spellcheck/hunspell/src/Makefile.in +++ b/extensions/spellcheck/hunspell/src/Makefile.in @@ -71,8 +71,6 @@ endif include $(topsrcdir)/config/rules.mk -INCLUDES += -I$(topsrcdir)/extensions/spellcheck/src - ifdef MOZ_NATIVE_HUNSPELL CXXFLAGS += $(MOZ_HUNSPELL_CFLAGS) endif diff --git a/extensions/spellcheck/hunspell/src/mozHunspell.cpp b/extensions/spellcheck/hunspell/src/mozHunspell.cpp index af67fd350c8..89826d2dab6 100644 --- a/extensions/spellcheck/hunspell/src/mozHunspell.cpp +++ b/extensions/spellcheck/hunspell/src/mozHunspell.cpp @@ -41,7 +41,6 @@ * Harri Pitkanen * Andras Timar * Tor Lillqvist - * Jesper Kristensen (mail@jesperkristensen.dk) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -71,8 +70,6 @@ #include "nsUnicharUtilCIID.h" #include "nsUnicharUtils.h" #include "nsCRT.h" -#include "mozInlineSpellChecker.h" -#include "mozilla/Services.h" #include #include "nsIMemoryReporter.h" @@ -125,7 +122,8 @@ mozHunspell::Init() LoadDictionaryList(); - nsCOMPtr obs = mozilla::services::GetObserverService(); + nsCOMPtr obs = + do_GetService("@mozilla.org/observer-service;1"); if (obs) { obs->AddObserver(this, "profile-do-change", PR_TRUE); } @@ -149,6 +147,9 @@ NS_IMETHODIMP mozHunspell::GetDictionary(PRUnichar **aDictionary) { NS_ENSURE_ARG_POINTER(aDictionary); + if (mDictionary.IsEmpty()) + return NS_ERROR_NOT_INITIALIZED; + *aDictionary = ToNewUnicode(mDictionary); return *aDictionary ? NS_OK : NS_ERROR_OUT_OF_MEMORY; } @@ -160,6 +161,9 @@ NS_IMETHODIMP mozHunspell::SetDictionary(const PRUnichar *aDictionary) { NS_ENSURE_ARG_POINTER(aDictionary); + if (mDictionary.Equals(aDictionary)) + return NS_OK; + nsIFile* affFile = mDictionaries.GetWeak(nsDependentString(aDictionary)); if (!affFile) return NS_ERROR_FILE_NOT_FOUND; @@ -174,9 +178,6 @@ NS_IMETHODIMP mozHunspell::SetDictionary(const PRUnichar *aDictionary) nsresult rv = affFile->GetNativePath(affFileName); NS_ENSURE_SUCCESS(rv, rv); - if (mAffixFileName.Equals(affFileName.get())) - return NS_OK; - dictFileName = affFileName; PRInt32 dotPos = dictFileName.RFindChar('.'); if (dotPos == -1) @@ -190,7 +191,6 @@ NS_IMETHODIMP mozHunspell::SetDictionary(const PRUnichar *aDictionary) delete mHunspell; mDictionary = aDictionary; - mAffixFileName = affFileName; mHunspell = new Hunspell(affFileName.get(), dictFileName.get()); @@ -222,13 +222,6 @@ NS_IMETHODIMP mozHunspell::SetDictionary(const PRUnichar *aDictionary) else mLanguage = Substring(mDictionary, 0, pos); - nsCOMPtr obs = mozilla::services::GetObserverService(); - if (obs) { - obs->NotifyObservers(nsnull, - SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION, - nsnull); - } - return NS_OK; } @@ -340,14 +333,6 @@ NS_IMETHODIMP mozHunspell::GetDictionaryList(PRUnichar ***aDictionaries, return NS_OK; } -static PLDHashOperator -FindFirstString(const nsAString& aString, nsIFile* aFile, void* aClosure) -{ - nsAString *dic = (nsAString*) aClosure; - dic->Assign(aString); - return PL_DHASH_STOP; -} - void mozHunspell::LoadDictionaryList() { @@ -360,7 +345,6 @@ mozHunspell::LoadDictionaryList() if (!dirSvc) return; - // find built in dictionaries nsCOMPtr dictDir; rv = dirSvc->Get(DICTIONARY_SEARCH_DIRECTORY, NS_GET_IID(nsIFile), getter_AddRefs(dictDir)); @@ -388,7 +372,6 @@ mozHunspell::LoadDictionaryList() } } - // find dictionaries from extensions requiring restart nsCOMPtr dictDirs; rv = dirSvc->Get(DICTIONARY_SEARCH_DIRECTORY_LIST, NS_GET_IID(nsISimpleEnumerator), getter_AddRefs(dictDirs)); @@ -404,51 +387,6 @@ mozHunspell::LoadDictionaryList() if (dictDir) LoadDictionariesFromDir(dictDir); } - - // find dictionaries from restartless extensions - for (PRUint32 i = 0; i < mDynamicDirectories.Count(); i++) { - LoadDictionariesFromDir(mDynamicDirectories[i]); - } - - // Now we have finished updating the list of dictionaries, update the current - // dictionary and any editors which may use it. - mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking(); - - // If the current dictionary has gone, try to replace it with another - // dictionary of the same language - if (!mDictionary.IsEmpty()) { - rv = SetDictionary(mDictionary.get()); - if (NS_SUCCEEDED(rv)) - return; - } - - // If we didn't find a dictionary equal to the current dictionary or we had - // no current dictionary, just pick an arbitrary dictionary. - nsAutoString firstDictionary; - mDictionaries.EnumerateRead(FindFirstString, &firstDictionary); - if (!firstDictionary.IsEmpty()) { - rv = SetDictionary(firstDictionary.get()); - if (NS_SUCCEEDED(rv)) - return; - } - - // If there are no dictionaries, set no current dictionary - if (!mDictionary.IsEmpty()) { - delete mHunspell; - mHunspell = nsnull; - mDictionary.AssignLiteral(""); - mAffixFileName.AssignLiteral(""); - mLanguage.AssignLiteral(""); - mDecoder = nsnull; - mEncoder = nsnull; - - nsCOMPtr obs = mozilla::services::GetObserverService(); - if (obs) { - obs->NotifyObservers(nsnull, - SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION, - nsnull); - } - } } NS_IMETHODIMP @@ -604,19 +542,3 @@ mozHunspell::Observe(nsISupports* aSubj, const char *aTopic, return NS_OK; } - -/* void addDirectory(in nsIFile dir); */ -NS_IMETHODIMP mozHunspell::AddDirectory(nsIFile *aDir) -{ - mDynamicDirectories.AppendObject(aDir); - LoadDictionaryList(); - return NS_OK; -} - -/* void removeDirectory(in nsIFile dir); */ -NS_IMETHODIMP mozHunspell::RemoveDirectory(nsIFile *aDir) -{ - mDynamicDirectories.RemoveObject(aDir); - LoadDictionaryList(); - return NS_OK; -} diff --git a/extensions/spellcheck/hunspell/src/mozHunspell.h b/extensions/spellcheck/hunspell/src/mozHunspell.h index 4b81b06cddc..5b76d0c3601 100644 --- a/extensions/spellcheck/hunspell/src/mozHunspell.h +++ b/extensions/spellcheck/hunspell/src/mozHunspell.h @@ -41,7 +41,6 @@ * Harri Pitkanen * Andras Timar * Tor Lillqvist - * Jesper Kristensen (mail@jesperkristensen.dk) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -65,7 +64,6 @@ #include "mozIPersonalDictionary.h" #include "nsString.h" #include "nsCOMPtr.h" -#include "nsCOMArray.h" #include "nsIObserver.h" #include "nsIUnicodeEncoder.h" #include "nsIUnicodeDecoder.h" @@ -111,10 +109,6 @@ protected: nsInterfaceHashtable mDictionaries; nsString mDictionary; nsString mLanguage; - nsCString mAffixFileName; - - // dynamic dirs used to search for dictionaries - nsCOMArray mDynamicDirectories; Hunspell *mHunspell; diff --git a/extensions/spellcheck/idl/mozISpellCheckingEngine.idl b/extensions/spellcheck/idl/mozISpellCheckingEngine.idl index 3a9e7dd1919..e880d87353e 100644 --- a/extensions/spellcheck/idl/mozISpellCheckingEngine.idl +++ b/extensions/spellcheck/idl/mozISpellCheckingEngine.idl @@ -20,7 +20,6 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): - * Jesper Kristensen * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -41,7 +40,7 @@ interface nsIFile; interface mozIPersonalDictionary; -[scriptable, uuid(8ba643a4-7ddc-4662-b976-7ec123843f10)] +[scriptable, uuid(6eb307d6-3567-481a-971a-feb666b8ae72)] /** * This interface represents a SpellChecker. @@ -50,22 +49,11 @@ interface mozIPersonalDictionary; interface mozISpellCheckingEngine : nsISupports { /** * The name of the current dictionary - * - * Whenever getDictionaryList is not empty, this attribute contains a value - * from that list. Whenever getDictionaryList is empty, this attribute - * contains the empty string. Setting this attribute to a value not in - * getDictionaryList will throw NS_ERROR_FILE_NOT_FOUND. - * - * The spellcheck engine will send a notification with - * "spellcheck-dictionary-update" as topic when this changes. */ attribute wstring dictionary; /** * The language this spellchecker is using when checking - * - * The spellcheck engine will send a notification with - * "spellcheck-dictionary-update" as topic when this changes. */ readonly attribute wstring language; @@ -101,17 +89,11 @@ interface mozISpellCheckingEngine : nsISupports { /** * check a word - * - * The spellcheck engine will send a notification with - * "spellcheck-dictionary-update" as topic when this changes. */ boolean check(in wstring word); /** * get a list of suggestions for a misspelled word - * - * The spellcheck engine will send a notification with - * "spellcheck-dictionary-update" as topic when this changes. */ void suggest(in wstring word,[array, size_is(count)] out wstring suggestions, out PRUint32 count); @@ -119,22 +101,9 @@ interface mozISpellCheckingEngine : nsISupports { * Load dictionaries from the specified dir */ void loadDictionariesFromDir(in nsIFile dir); - - /** - * Add dictionaries from a directory to the spell checker - */ - void addDirectory(in nsIFile dir); - - /** - * Remove dictionaries from a directory from the spell checker - */ - void removeDirectory(in nsIFile dir); }; %{C++ #define DICTIONARY_SEARCH_DIRECTORY "DictD" #define DICTIONARY_SEARCH_DIRECTORY_LIST "DictDL" - -#define SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION \ - "spellcheck-dictionary-update" %} diff --git a/extensions/spellcheck/src/mozInlineSpellChecker.cpp b/extensions/spellcheck/src/mozInlineSpellChecker.cpp index 12d9b9bc3d4..e159d38e567 100644 --- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp +++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp @@ -600,9 +600,9 @@ nsresult mozInlineSpellChecker::Cleanup(PRBool aDestroyingFrames) // do that and caches the result so we don't have to keep allocating those // objects if there are no dictionaries or spellchecking. // -// Whenever dictionaries are added or removed at runtime, this value must be -// updated before an observer notification is sent out about the change, to -// avoid editors getting a wrong cached result. +// This caching will prevent adding dictionaries at runtime if we start out +// with no dictionaries! Installing dictionaries as extensions will require +// a restart anyway, so it shouldn't be a problem. PRBool // static mozInlineSpellChecker::CanEnableInlineSpellChecking() @@ -625,12 +625,6 @@ mozInlineSpellChecker::CanEnableInlineSpellChecking() return (gCanEnableSpellChecking == SpellCheck_Available); } -void // static -mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking() -{ - gCanEnableSpellChecking = SpellCheck_Uninitialized; -} - // mozInlineSpellChecker::RegisterEventListeners // // The inline spell checker listens to mouse events and keyboard navigation+ // events. diff --git a/extensions/spellcheck/src/mozInlineSpellChecker.h b/extensions/spellcheck/src/mozInlineSpellChecker.h index f371ed72f8e..183422fd02f 100644 --- a/extensions/spellcheck/src/mozInlineSpellChecker.h +++ b/extensions/spellcheck/src/mozInlineSpellChecker.h @@ -229,10 +229,8 @@ public: NS_DECL_NSIDOMEVENTLISTENER NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(mozInlineSpellChecker, nsIDOMEventListener) - // returns true if there are any spell checking dictionaries available + // returns true if it looks likely that we can enable real-time spell checking static PRBool CanEnableInlineSpellChecking(); - // update the cached value whenever the list of available dictionaries changes - static void UpdateCanEnableInlineSpellChecking(); nsresult Blur(nsIDOMEvent* aEvent); nsresult MouseClick(nsIDOMEvent* aMouseEvent); diff --git a/extensions/spellcheck/src/mozSpellChecker.cpp b/extensions/spellcheck/src/mozSpellChecker.cpp index b23927e02cf..96832848549 100644 --- a/extensions/spellcheck/src/mozSpellChecker.cpp +++ b/extensions/spellcheck/src/mozSpellChecker.cpp @@ -18,7 +18,6 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): David Einstein Deinst@world.std.com - * Jesper Kristensen * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -78,6 +77,9 @@ mozSpellChecker::Init() mPersonalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1"); mSpellCheckingEngine = nsnull; + mCurrentEngineContractId = nsnull; + mDictionariesMap.Init(); + InitSpellCheckDictionaryMap(); return NS_OK; } @@ -305,45 +307,35 @@ mozSpellChecker::GetPersonalDictionary(nsTArray *aWordList) return NS_OK; } +struct AppendNewStruct +{ + nsTArray *dictionaryList; + PRBool failed; +}; + +static PLDHashOperator +AppendNewString(const nsAString& aString, nsCString*, void* aClosure) +{ + AppendNewStruct *ans = (AppendNewStruct*) aClosure; + + if (!ans->dictionaryList->AppendElement(aString)) + { + ans->failed = PR_TRUE; + return PL_DHASH_STOP; + } + + return PL_DHASH_NEXT; +} + NS_IMETHODIMP mozSpellChecker::GetDictionaryList(nsTArray *aDictionaryList) { - nsresult rv; + AppendNewStruct ans = {aDictionaryList, PR_FALSE}; - // For catching duplicates - nsClassHashtable dictionaries; - dictionaries.Init(); + mDictionariesMap.EnumerateRead(AppendNewString, &ans); - nsCOMArray spellCheckingEngines; - rv = GetEngineList(&spellCheckingEngines); - NS_ENSURE_SUCCESS(rv, rv); - - for (PRUint32 i = 0; i < spellCheckingEngines.Count(); i++) { - nsCOMPtr engine = spellCheckingEngines[i]; - - PRUint32 count = 0; - PRUnichar **words = NULL; - engine->GetDictionaryList(&words, &count); - for (PRUint32 k = 0; k < count; k++) { - nsAutoString dictName; - - dictName.Assign(words[k]); - - // Skip duplicate dictionaries. Only take the first one - // for each name. - if (dictionaries.Get(dictName, NULL)) - continue; - - dictionaries.Put(dictName, NULL); - - if (!aDictionaryList->AppendElement(dictName)) { - NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, words); - return NS_ERROR_OUT_OF_MEMORY; - } - } - - NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, words); - } + if (ans.failed) + return NS_ERROR_OUT_OF_MEMORY; return NS_OK; } @@ -364,87 +356,44 @@ mozSpellChecker::GetCurrentDictionary(nsAString &aDictionary) NS_IMETHODIMP mozSpellChecker::SetCurrentDictionary(const nsAString &aDictionary) { - mSpellCheckingEngine = nsnull; + nsresult rv; + nsCString *contractId; if (aDictionary.IsEmpty()) { + mCurrentEngineContractId = nsnull; + mSpellCheckingEngine = nsnull; return NS_OK; } - nsresult rv; - nsCOMArray spellCheckingEngines; - rv = GetEngineList(&spellCheckingEngines); - NS_ENSURE_SUCCESS(rv, rv); - - for (PRUint32 i = 0; i < spellCheckingEngines.Count(); i++) { - nsCOMPtr engine = spellCheckingEngines[i]; - - rv = engine->SetDictionary(PromiseFlatString(aDictionary).get()); - if (NS_SUCCEEDED(rv)) { - mSpellCheckingEngine = engine; - - nsCOMPtr personalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1"); - mSpellCheckingEngine->SetPersonalDictionary(personalDictionary.get()); - - return NS_OK; - } + if (!mDictionariesMap.Get(aDictionary, &contractId)){ + NS_WARNING("Dictionary not found"); + return NS_ERROR_NOT_AVAILABLE; } - // We could not find any engine with the requested dictionary - return NS_ERROR_NOT_AVAILABLE; -} + if (!mCurrentEngineContractId || !mCurrentEngineContractId->Equals(*contractId)){ + mSpellCheckingEngine = do_GetService(contractId->get(), &rv); + if (NS_FAILED(rv)) + return rv; -NS_IMETHODIMP -mozSpellChecker::CheckCurrentDictionary() -{ - // Check if the current engine has any dictionaries available. If not, - // the last dictionary has just been uninstalled, and we need to stop using - // the engine. - if (mSpellCheckingEngine) { - nsXPIDLString dictname; - - mSpellCheckingEngine->GetDictionary(getter_Copies(dictname)); - - // We still have a dictionary, so keep using that. - if (!dictname.IsEmpty()) { - return NS_OK; - } - - // Our current dictionary has gone, so we cannot use the engine anymore. - mSpellCheckingEngine = nsnull; + mCurrentEngineContractId = contractId; } - // We have no current engine. Pick one. - nsresult rv; - nsCOMArray spellCheckingEngines; - rv = GetEngineList(&spellCheckingEngines); - NS_ENSURE_SUCCESS(rv, rv); - - for (PRUint32 i = 0; i < spellCheckingEngines.Count(); i++) { - nsCOMPtr engine = spellCheckingEngines[i]; - - nsXPIDLString dictname; - - engine->GetDictionary(getter_Copies(dictname)); - - if (!dictname.IsEmpty()) { - mSpellCheckingEngine = engine; - - nsCOMPtr personalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1"); - mSpellCheckingEngine->SetPersonalDictionary(personalDictionary.get()); - - nsXPIDLString language; - nsresult rv; - nsCOMPtr serv = do_GetService("@mozilla.org/spellchecker/i18nmanager;1", &rv); - if(serv && NS_SUCCEEDED(rv)) { - serv->GetUtil(language.get(), getter_AddRefs(mConverter)); - } - - return NS_OK; - } + nsresult res; + res = mSpellCheckingEngine->SetDictionary(PromiseFlatString(aDictionary).get()); + if(NS_FAILED(res)){ + NS_WARNING("Dictionary load failed"); + return res; } - // There are no dictionaries available - return NS_OK; + mSpellCheckingEngine->SetPersonalDictionary(mPersonalDictionary); + + nsXPIDLString language; + + nsCOMPtr serv(do_GetService("@mozilla.org/spellchecker/i18nmanager;1", &res)); + if(serv && NS_SUCCEEDED(res)){ + res = serv->GetUtil(language.get(),getter_AddRefs(mConverter)); + } + return res; } nsresult @@ -528,10 +477,11 @@ mozSpellChecker::GetCurrentBlockIndex(nsITextServicesDocument *aDoc, PRInt32 *ou } nsresult -mozSpellChecker::GetEngineList(nsCOMArray* aSpellCheckingEngines) +mozSpellChecker::InitSpellCheckDictionaryMap() { nsresult rv; PRBool hasMoreEngines; + nsTArray contractIds; nsCOMPtr catMgr = do_GetService(NS_CATEGORYMANAGER_CONTRACTID); if (!catMgr) @@ -558,24 +508,52 @@ mozSpellChecker::GetEngineList(nsCOMArray* aSpellChecki if (NS_FAILED(rv)) return rv; + contractIds.AppendElement(contractId); + } + + contractIds.AppendElement(NS_LITERAL_CSTRING(DEFAULT_SPELL_CHECKER)); + + // Retrieve dictionaries from all available spellcheckers and + // fill mDictionariesMap hash (only the first dictionary with the + // each name is used). + for (PRUint32 i=0;i < contractIds.Length();i++){ + PRUint32 count,k; + PRUnichar **words; + + const nsCString& contractId = contractIds[i]; + // Try to load spellchecker engine. Ignore errors silently // except for the last one (HunSpell). nsCOMPtr engine = do_GetService(contractId.get(), &rv); - if (NS_SUCCEEDED(rv)) { - aSpellCheckingEngines->AppendObject(engine); - } - } + if (NS_FAILED(rv)){ + // Fail if not succeeded to load HunSpell. Ignore errors + // for external spellcheck engines. + if (i==contractIds.Length()-1){ + return rv; + } - // Try to load HunSpell spellchecker engine. - nsCOMPtr engine = - do_GetService(DEFAULT_SPELL_CHECKER, &rv); - if (NS_FAILED(rv)) { - // Fail if not succeeded to load HunSpell. Ignore errors - // for external spellcheck engines. - return rv; + continue; + } + + engine->GetDictionaryList(&words,&count); + for(k=0;kAppendObject(engine); return NS_OK; } diff --git a/extensions/spellcheck/src/mozSpellChecker.h b/extensions/spellcheck/src/mozSpellChecker.h index 10f52f21a9a..142c0ee15f4 100644 --- a/extensions/spellcheck/src/mozSpellChecker.h +++ b/extensions/spellcheck/src/mozSpellChecker.h @@ -20,7 +20,6 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): David Einstein Deinst@world.std.com - * Jesper Kristensen * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -40,7 +39,6 @@ #define mozSpellChecker_h__ #include "nsCOMPtr.h" -#include "nsCOMArray.h" #include "nsISpellChecker.h" #include "nsString.h" #include "nsITextServicesDocument.h" @@ -77,13 +75,17 @@ public: NS_IMETHOD GetDictionaryList(nsTArray *aDictionaryList); NS_IMETHOD GetCurrentDictionary(nsAString &aDictionary); NS_IMETHOD SetCurrentDictionary(const nsAString &aDictionary); - NS_IMETHOD CheckCurrentDictionary(); protected: nsCOMPtr mConverter; nsCOMPtr mTsDoc; nsCOMPtr mPersonalDictionary; + // Hastable maps directory name to the spellchecker contract ID + nsClassHashtable mDictionariesMap; + + nsString mDictionaryName; + nsCString *mCurrentEngineContractId; nsCOMPtr mSpellCheckingEngine; PRBool mFromStart; @@ -91,6 +93,6 @@ protected: nsresult GetCurrentBlockIndex(nsITextServicesDocument *aDoc, PRInt32 *outBlockIndex); - nsresult GetEngineList(nsCOMArray *aDictionaryList); + nsresult InitSpellCheckDictionaryMap(); }; #endif // mozSpellChecker_h__ diff --git a/extensions/spellcheck/src/mozSpellCheckerFactory.cpp b/extensions/spellcheck/src/mozSpellCheckerFactory.cpp index e4f917a8307..8ab97debbca 100644 --- a/extensions/spellcheck/src/mozSpellCheckerFactory.cpp +++ b/extensions/spellcheck/src/mozSpellCheckerFactory.cpp @@ -59,7 +59,39 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(mozHunspellDirProvider) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(mozSpellChecker, Init) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(mozPersonalDictionary, Init) NS_GENERIC_FACTORY_CONSTRUCTOR(mozSpellI18NManager) -NS_GENERIC_FACTORY_CONSTRUCTOR(mozInlineSpellChecker) + +// This special constructor for the inline spell checker asks the inline +// spell checker if we can create spell checking objects at all (ie, if there +// are any dictionaries loaded) before trying to create one. The static +// CanEnableInlineSpellChecking caches the value so this will be faster (we +// have to run this code for every edit box we create, as well as for every +// right click in those edit boxes). +static nsresult +mozInlineSpellCheckerConstructor(nsISupports *aOuter, REFNSIID aIID, + void **aResult) +{ + if (! mozInlineSpellChecker::CanEnableInlineSpellChecking()) + return NS_ERROR_FAILURE; + + nsresult rv; + + *aResult = NULL; + if (NULL != aOuter) { + rv = NS_ERROR_NO_AGGREGATION; + return rv; + } + + mozInlineSpellChecker* inst = new mozInlineSpellChecker(); + if (NULL == inst) { + rv = NS_ERROR_OUT_OF_MEMORY; + return rv; + } + NS_ADDREF(inst); + rv = inst->QueryInterface(aIID, aResult); + NS_RELEASE(inst); + + return rv; +} NS_DEFINE_NAMED_CID(MOZ_HUNSPELL_CID); NS_DEFINE_NAMED_CID(HUNSPELLDIRPROVIDER_CID); diff --git a/extensions/spellcheck/tests/Makefile.in b/extensions/spellcheck/tests/Makefile.in deleted file mode 100644 index 686fe393fd4..00000000000 --- a/extensions/spellcheck/tests/Makefile.in +++ /dev/null @@ -1,48 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ -relativesrcdir = extensions/spellcheck/tests - -include $(DEPTH)/config/autoconf.mk - -DIRS = chrome - -include $(topsrcdir)/config/rules.mk diff --git a/extensions/spellcheck/tests/chrome/Makefile.in b/extensions/spellcheck/tests/chrome/Makefile.in deleted file mode 100644 index da60413beb7..00000000000 --- a/extensions/spellcheck/tests/chrome/Makefile.in +++ /dev/null @@ -1,54 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ -relativesrcdir = extensions/spellcheck/tests/chrome - -include $(DEPTH)/config/autoconf.mk - -DIRS = base map - -include $(topsrcdir)/config/rules.mk - -_TEST_FILES = test_add_remove_dictionaries.xul \ - $(NULL) - -libs:: $(_TEST_FILES) - $(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir) diff --git a/extensions/spellcheck/tests/chrome/base/Makefile.in b/extensions/spellcheck/tests/chrome/base/Makefile.in deleted file mode 100644 index 04159b2ca7a..00000000000 --- a/extensions/spellcheck/tests/chrome/base/Makefile.in +++ /dev/null @@ -1,52 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ -relativesrcdir = extensions/spellcheck/tests/chrome/base - -include $(DEPTH)/config/autoconf.mk -include $(topsrcdir)/config/rules.mk - -_TEST_FILES = base_utf.dic \ - base_utf.aff \ - $(NULL) - -libs:: $(_TEST_FILES) - $(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir) diff --git a/extensions/spellcheck/tests/chrome/base/base_utf.aff b/extensions/spellcheck/tests/chrome/base/base_utf.aff deleted file mode 100644 index 493157b3018..00000000000 --- a/extensions/spellcheck/tests/chrome/base/base_utf.aff +++ /dev/null @@ -1,198 +0,0 @@ -# OpenOffice.org’s en_US.aff file -# with Unicode apostrophe: ’ - -SET UTF-8 -TRY esianrtolcdugmphbyfvkwzESIANRTOLCDUGMPHBYFVKWZ' - -MAXNGRAMSUGS 1 -WORDCHARS .'’ - -PFX A Y 1 -PFX A 0 re . - -PFX I Y 1 -PFX I 0 in . - -PFX U Y 1 -PFX U 0 un . - -PFX C Y 1 -PFX C 0 de . - -PFX E Y 1 -PFX E 0 dis . - -PFX F Y 1 -PFX F 0 con . - -PFX K Y 1 -PFX K 0 pro . - -SFX V N 2 -SFX V e ive e -SFX V 0 ive [^e] - -SFX N Y 3 -SFX N e ion e -SFX N y ication y -SFX N 0 en [^ey] - -SFX X Y 3 -SFX X e ions e -SFX X y ications y -SFX X 0 ens [^ey] - -SFX H N 2 -SFX H y ieth y -SFX H 0 th [^y] - -SFX Y Y 1 -SFX Y 0 ly . - -SFX G Y 2 -SFX G e ing e -SFX G 0 ing [^e] - -SFX J Y 2 -SFX J e ings e -SFX J 0 ings [^e] - -SFX D Y 4 -SFX D 0 d e -SFX D y ied [^aeiou]y -SFX D 0 ed [^ey] -SFX D 0 ed [aeiou]y - -SFX T N 4 -SFX T 0 st e -SFX T y iest [^aeiou]y -SFX T 0 est [aeiou]y -SFX T 0 est [^ey] - -SFX R Y 4 -SFX R 0 r e -SFX R y ier [^aeiou]y -SFX R 0 er [aeiou]y -SFX R 0 er [^ey] - -SFX Z Y 4 -SFX Z 0 rs e -SFX Z y iers [^aeiou]y -SFX Z 0 ers [aeiou]y -SFX Z 0 ers [^ey] - -SFX S Y 4 -SFX S y ies [^aeiou]y -SFX S 0 s [aeiou]y -SFX S 0 es [sxzh] -SFX S 0 s [^sxzhy] - -SFX P Y 3 -SFX P y iness [^aeiou]y -SFX P 0 ness [aeiou]y -SFX P 0 ness [^y] - -SFX M Y 1 -SFX M 0 's . - -SFX B Y 3 -SFX B 0 able [^aeiou] -SFX B 0 able ee -SFX B e able [^aeiou]e - -SFX L Y 1 -SFX L 0 ment . - -REP 88 -REP a ei -REP ei a -REP a ey -REP ey a -REP ai ie -REP ie ai -REP are air -REP are ear -REP are eir -REP air are -REP air ere -REP ere air -REP ere ear -REP ere eir -REP ear are -REP ear air -REP ear ere -REP eir are -REP eir ere -REP ch te -REP te ch -REP ch ti -REP ti ch -REP ch tu -REP tu ch -REP ch s -REP s ch -REP ch k -REP k ch -REP f ph -REP ph f -REP gh f -REP f gh -REP i igh -REP igh i -REP i uy -REP uy i -REP i ee -REP ee i -REP j di -REP di j -REP j gg -REP gg j -REP j ge -REP ge j -REP s ti -REP ti s -REP s ci -REP ci s -REP k cc -REP cc k -REP k qu -REP qu k -REP kw qu -REP o eau -REP eau o -REP o ew -REP ew o -REP oo ew -REP ew oo -REP ew ui -REP ui ew -REP oo ui -REP ui oo -REP ew u -REP u ew -REP oo u -REP u oo -REP u oe -REP oe u -REP u ieu -REP ieu u -REP ue ew -REP ew ue -REP uff ough -REP oo ieu -REP ieu oo -REP ier ear -REP ear ier -REP ear air -REP air ear -REP w qu -REP qu w -REP z ss -REP ss z -REP shun tion -REP shun sion -REP shun cion -McDonalds’sá/w -McDonald’sszá/g3) st:McDonald’s po:noun_prs is:TRANS -McDonald’sszal/g4) st:McDonald’s po:noun_prs is:INSTR -McDonald’ssal/w diff --git a/extensions/spellcheck/tests/chrome/base/base_utf.dic b/extensions/spellcheck/tests/chrome/base/base_utf.dic deleted file mode 100644 index b2b536d2854..00000000000 --- a/extensions/spellcheck/tests/chrome/base/base_utf.dic +++ /dev/null @@ -1,29 +0,0 @@ -28 -created/U -create/XKVNGADS -imply/GNSDX -natural/PUY -like/USPBY -convey/BDGS -look/GZRDS -text -hello -said -sawyer -NASA -rotten -day -tomorrow -seven -FAQ/SM -can’t -doesn’t -etc -won’t -lip -text -horrifying -speech -suggest -uncreate/V -Hunspell diff --git a/extensions/spellcheck/tests/chrome/map/Makefile.in b/extensions/spellcheck/tests/chrome/map/Makefile.in deleted file mode 100644 index f9da7553bee..00000000000 --- a/extensions/spellcheck/tests/chrome/map/Makefile.in +++ /dev/null @@ -1,52 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ -relativesrcdir = extensions/spellcheck/tests/chrome/map - -include $(DEPTH)/config/autoconf.mk -include $(topsrcdir)/config/rules.mk - -_TEST_FILES = maputf.dic \ - maputf.aff \ - $(NULL) - -libs:: $(_TEST_FILES) - $(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir) diff --git a/extensions/spellcheck/tests/chrome/map/maputf.aff b/extensions/spellcheck/tests/chrome/map/maputf.aff deleted file mode 100644 index 30edb2a7850..00000000000 --- a/extensions/spellcheck/tests/chrome/map/maputf.aff +++ /dev/null @@ -1,11 +0,0 @@ -# With MAP suggestion, Hunspell can add missing accents to a word. - -SET UTF-8 - -# switch off ngram suggestion for testing -MAXNGRAMSUGS 0 - -MAP 3 -MAP uúü -MAP öóo -MAP ß(ss) diff --git a/extensions/spellcheck/tests/chrome/map/maputf.dic b/extensions/spellcheck/tests/chrome/map/maputf.dic deleted file mode 100644 index 1c6fa8d0589..00000000000 --- a/extensions/spellcheck/tests/chrome/map/maputf.dic +++ /dev/null @@ -1,4 +0,0 @@ -3 -Frühstück -tükörfúró -groß diff --git a/extensions/spellcheck/tests/chrome/test_add_remove_dictionaries.xul b/extensions/spellcheck/tests/chrome/test_add_remove_dictionaries.xul deleted file mode 100644 index 8299c2a9313..00000000000 --- a/extensions/spellcheck/tests/chrome/test_add_remove_dictionaries.xul +++ /dev/null @@ -1,110 +0,0 @@ - - - - - Add and remove dictionaries test - - - - From 09008f38ba07a135df377daed6a1d0f0d0be83b6 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Tue, 6 Sep 2011 13:02:39 -0700 Subject: [PATCH 18/23] Bug 684242 - Back out b532e0d93bc5 (bug 681980) because it breaks resume from sleep. --- embedding/android/AndroidManifest.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedding/android/AndroidManifest.xml.in b/embedding/android/AndroidManifest.xml.in index 3b7eb366196..510c007e34e 100644 --- a/embedding/android/AndroidManifest.xml.in +++ b/embedding/android/AndroidManifest.xml.in @@ -10,7 +10,7 @@ #endif > + android:targetSdkVersion="5"/> From 91a4d979e7d76e059a1849e79cd0c60e5a1047e9 Mon Sep 17 00:00:00 2001 From: John Daggett Date: Wed, 7 Sep 2011 13:00:15 +0900 Subject: [PATCH 19/23] Bug 3512. Reftests to test font-stretch with different sets of font families. r=jkew --- layout/reftests/font-matching/reftest.list | 5 + .../font-matching/stretchmapping-137-ref.html | 348 ++++++++++++ .../font-matching/stretchmapping-137.html | 268 ++++++++++ .../font-matching/stretchmapping-35-ref.html | 320 +++++++++++ .../font-matching/stretchmapping-35.html | 240 +++++++++ .../font-matching/stretchmapping-all-ref.html | 366 +++++++++++++ .../font-matching/stretchmapping-all.html | 505 ++++++++++++++++++ .../stretchmapping-reverse-ref.html | 54 ++ .../font-matching/stretchmapping-reverse.html | 102 ++++ 9 files changed, 2208 insertions(+) create mode 100644 layout/reftests/font-matching/stretchmapping-137-ref.html create mode 100644 layout/reftests/font-matching/stretchmapping-137.html create mode 100644 layout/reftests/font-matching/stretchmapping-35-ref.html create mode 100644 layout/reftests/font-matching/stretchmapping-35.html create mode 100644 layout/reftests/font-matching/stretchmapping-all-ref.html create mode 100644 layout/reftests/font-matching/stretchmapping-all.html create mode 100644 layout/reftests/font-matching/stretchmapping-reverse-ref.html create mode 100644 layout/reftests/font-matching/stretchmapping-reverse.html diff --git a/layout/reftests/font-matching/reftest.list b/layout/reftests/font-matching/reftest.list index c9cfadff5b4..1c0ac3103cf 100644 --- a/layout/reftests/font-matching/reftest.list +++ b/layout/reftests/font-matching/reftest.list @@ -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 diff --git a/layout/reftests/font-matching/stretchmapping-137-ref.html b/layout/reftests/font-matching/stretchmapping-137-ref.html new file mode 100644 index 00000000000..a42b81c8362 --- /dev/null +++ b/layout/reftests/font-matching/stretchmapping-137-ref.html @@ -0,0 +1,348 @@ + + + +font-stretch mapping tests + + + + + + +

Font family with ultra-condensed 100, 400, condensed 200, 800 and expanded 500, 900

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
123456789
100
200
300
400
500
600
700
800
900
+ + + diff --git a/layout/reftests/font-matching/stretchmapping-137.html b/layout/reftests/font-matching/stretchmapping-137.html new file mode 100644 index 00000000000..81f45f66dea --- /dev/null +++ b/layout/reftests/font-matching/stretchmapping-137.html @@ -0,0 +1,268 @@ + + + +font-stretch mapping tests + + + + + + +

Font family with ultra-condensed 100, 400, condensed 200, 800 and expanded 500, 900

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
123456789
100
200
300
400
500
600
700
800
900
+ + + diff --git a/layout/reftests/font-matching/stretchmapping-35-ref.html b/layout/reftests/font-matching/stretchmapping-35-ref.html new file mode 100644 index 00000000000..2124a687425 --- /dev/null +++ b/layout/reftests/font-matching/stretchmapping-35-ref.html @@ -0,0 +1,320 @@ + + + +font-stretch mapping tests + + + + + + +

Font family with normal width 200, 500 and condensed 100, 900

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
123456789
100
200
300
400
500
600
700
800
900
+ + + diff --git a/layout/reftests/font-matching/stretchmapping-35.html b/layout/reftests/font-matching/stretchmapping-35.html new file mode 100644 index 00000000000..a6c1245b393 --- /dev/null +++ b/layout/reftests/font-matching/stretchmapping-35.html @@ -0,0 +1,240 @@ + + + +font-stretch mapping tests + + + + + + +

Font family with normal width 200, 500 and condensed 100, 900

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
123456789
100
200
300
400
500
600
700
800
900
+ + + diff --git a/layout/reftests/font-matching/stretchmapping-all-ref.html b/layout/reftests/font-matching/stretchmapping-all-ref.html new file mode 100644 index 00000000000..267e50b4cae --- /dev/null +++ b/layout/reftests/font-matching/stretchmapping-all-ref.html @@ -0,0 +1,366 @@ + + + +font-stretch matching tests + + + + + + + + + +

font-stretch mapping with different font family sets

+

(only numbers should appear in the body of the table)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
widthfull1-31-41-52-64-64-76-77-98-9
11111244678
22111244678
33311244678
44341244678
55345244678
66345667678
77345667778
88345667798
99345667799
+ + + \ No newline at end of file diff --git a/layout/reftests/font-matching/stretchmapping-all.html b/layout/reftests/font-matching/stretchmapping-all.html new file mode 100644 index 00000000000..904fcd8f3b5 --- /dev/null +++ b/layout/reftests/font-matching/stretchmapping-all.html @@ -0,0 +1,505 @@ + + + +font-stretch matching tests + + + + + + + + + +

font-stretch mapping with different font family sets

+

(only numbers should appear in the body of the table)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
widthfull1-31-41-52-64-64-76-77-98-9
1FFFFFFFFFF
2FFFFFFFFFF
3FFFFFFFFFF
4FFFFFFFFFF
5FFFFFFFFFF
6FFFFFFFFFF
7FFFFFFFFFF
8FFFFFFFFFF
9FFFFFFFFFF
+ + + + + \ No newline at end of file diff --git a/layout/reftests/font-matching/stretchmapping-reverse-ref.html b/layout/reftests/font-matching/stretchmapping-reverse-ref.html new file mode 100644 index 00000000000..2a8fde16cbb --- /dev/null +++ b/layout/reftests/font-matching/stretchmapping-reverse-ref.html @@ -0,0 +1,54 @@ + + + +Assure OS/2 usWidthClass isn't referenced + + + + + + + +

The numbers below should appear in ascending sequence:

+ +

+1 +2 +3 +4 +5 +6 +7 +8 +9 +

+ + + \ No newline at end of file diff --git a/layout/reftests/font-matching/stretchmapping-reverse.html b/layout/reftests/font-matching/stretchmapping-reverse.html new file mode 100644 index 00000000000..ef3b0b1ed1c --- /dev/null +++ b/layout/reftests/font-matching/stretchmapping-reverse.html @@ -0,0 +1,102 @@ + + + +Assure OS/2 usWidthClass isn't referenced + + + + + + + +

The numbers below should appear in ascending sequence:

+ +

+F +F +F +F +F +F +F +F +F +

+ + + \ No newline at end of file From a5520ad4dfd92343e151f1a1d44ebb17763c0cb5 Mon Sep 17 00:00:00 2001 From: Josh Aas Date: Wed, 7 Sep 2011 00:51:38 -0400 Subject: [PATCH 20/23] Bug 669036: Fix bad path to child process binary that caused assertions. r=bsmedberg --- ipc/glue/GeckoChildProcessHost.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp index ef8f2b7b4a5..581e83869ce 100644 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -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 } From 5917328115f45a233b43e5df22565a316a3b30fa Mon Sep 17 00:00:00 2001 From: Josh Aas Date: Wed, 7 Sep 2011 00:54:21 -0400 Subject: [PATCH 21/23] Bug 682759: Use fixed size integer in NPRemoteWindow to store window reference. r=m_kato r=cjones --- dom/plugins/ipc/PluginInstanceChild.cpp | 4 ++-- dom/plugins/ipc/PluginInstanceParent.cpp | 6 +++--- dom/plugins/ipc/PluginMessageUtils.h | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dom/plugins/ipc/PluginInstanceChild.cpp b/dom/plugins/ipc/PluginInstanceChild.cpp index 67a2794e55c..5cbde80973b 100644 --- a/dom/plugins/ipc/PluginInstanceChild.cpp +++ b/dom/plugins/ipc/PluginInstanceChild.cpp @@ -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(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(aWindow.window)); SizePluginWindow(aWindow.width, aWindow.height); mWindow.window = (void*)mPluginWindowHWND; diff --git a/dom/plugins/ipc/PluginInstanceParent.cpp b/dom/plugins/ipc/PluginInstanceParent.cpp index 606a8746a10..3a289e22ae2 100644 --- a/dom/plugins/ipc/PluginInstanceParent.cpp +++ b/dom/plugins/ipc/PluginInstanceParent.cpp @@ -604,7 +604,7 @@ PluginInstanceParent::AsyncSetWindow(NPWindow* aWindow) { NPRemoteWindow window; mWindowType = aWindow->type; - window.window = reinterpret_cast(aWindow->window); + window.window = reinterpret_cast(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(aWindow->window)); - window.window = reinterpret_cast(aWindow->window); + window.window = reinterpret_cast(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(aWindow->window); + window.window = reinterpret_cast(aWindow->window); window.x = aWindow->x; window.y = aWindow->y; window.width = aWindow->width; diff --git a/dom/plugins/ipc/PluginMessageUtils.h b/dom/plugins/ipc/PluginMessageUtils.h index 9bc6196258d..12c3f27dfe9 100644 --- a/dom/plugins/ipc/PluginMessageUtils.h +++ b/dom/plugins/ipc/PluginMessageUtils.h @@ -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 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 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) && From 4c29f85d387aebfdfafc10fcd3746fdcd5ce4c13 Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Tue, 6 Sep 2011 15:01:01 -0400 Subject: [PATCH 22/23] Fixing bug 641552. Add mechanism for dynamically registering properties on the navigator object in all pages. r=peterv --- dom/base/nsDOMClassInfo.cpp | 92 +++++++++++++++++-- dom/base/nsDOMClassInfo.h | 3 + dom/base/nsIScriptNameSpaceManager.h | 3 + dom/base/nsScriptNameSpaceManager.cpp | 56 +++++++++-- dom/base/nsScriptNameSpaceManager.h | 12 ++- dom/tests/mochitest/bugs/Makefile.in | 1 + dom/tests/mochitest/bugs/test_bug597809.html | 7 +- dom/tests/mochitest/bugs/test_bug641552.html | 42 +++++++++ .../specialpowers/content/specialpowers.js | 6 ++ 9 files changed, 198 insertions(+), 24 deletions(-) create mode 100644 dom/tests/mochitest/bugs/test_bug641552.html diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 909145ef494..bf33af9b759 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -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 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); @@ -7020,6 +7027,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 native(do_CreateInstance(name_struct->mCID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + jsval prop_val = JSVAL_VOID; // Property value. + + nsCOMPtr gpi(do_QueryInterface(native)); + + if (gpi) { + JSObject *global = JS_GetGlobalForObject(cx, obj); + + nsISupports *globalNative = XPConnect()->GetNativeOfWrapper(cx, global); + nsCOMPtr 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 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) diff --git a/dom/base/nsDOMClassInfo.h b/dom/base/nsDOMClassInfo.h index 5efb38084cb..22a1bc11afe 100644 --- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -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) { diff --git a/dom/base/nsIScriptNameSpaceManager.h b/dom/base/nsIScriptNameSpaceManager.h index 0fea1ae9e45..e6f147646c5 100644 --- a/dom/base/nsIScriptNameSpaceManager.h +++ b/dom/base/nsIScriptNameSpaceManager.h @@ -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" diff --git a/dom/base/nsScriptNameSpaceManager.cpp b/dom/base/nsScriptNameSpaceManager.cpp index 5e6a952701e..1a09a204b85 100644 --- a/dom/base/nsScriptNameSpaceManager.cpp +++ b/dom/base/nsScriptNameSpaceManager.cpp @@ -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 - (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(); @@ -509,6 +519,25 @@ nsScriptNameSpaceManager::LookupName(const nsAString& aName, return NS_OK; } +nsresult +nsScriptNameSpaceManager::LookupNavigatorName(const nsAString& aName, + const nsGlobalNameStruct **aNameStruct) +{ + GlobalNameMapEntry *entry = + static_cast + (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 +549,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 +584,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 +604,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 +634,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 +686,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 +735,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 +753,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) { diff --git a/dom/base/nsScriptNameSpaceManager.h b/dom/base/nsScriptNameSpaceManager.h index 755c7aab5ac..5e5471f526a 100644 --- a/dom/base/nsScriptNameSpaceManager.h +++ b/dom/base/nsScriptNameSpaceManager.h @@ -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; }; diff --git a/dom/tests/mochitest/bugs/Makefile.in b/dom/tests/mochitest/bugs/Makefile.in index e9cfda6edce..a626f021533 100644 --- a/dom/tests/mochitest/bugs/Makefile.in +++ b/dom/tests/mochitest/bugs/Makefile.in @@ -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 \ diff --git a/dom/tests/mochitest/bugs/test_bug597809.html b/dom/tests/mochitest/bugs/test_bug597809.html index 42b743d81f0..ac2daa4211c 100644 --- a/dom/tests/mochitest/bugs/test_bug597809.html +++ b/dom/tests/mochitest/bugs/test_bug597809.html @@ -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(); }); diff --git a/dom/tests/mochitest/bugs/test_bug641552.html b/dom/tests/mochitest/bugs/test_bug641552.html new file mode 100644 index 00000000000..ca972d5f2a6 --- /dev/null +++ b/dom/tests/mochitest/bugs/test_bug641552.html @@ -0,0 +1,42 @@ + + + + + Test for Bug 641552 + + + + + +Mozilla Bug 641552 +

+
+
+
+ + diff --git a/testing/mochitest/specialpowers/content/specialpowers.js b/testing/mochitest/specialpowers/content/specialpowers.js index 4cead0ec5fb..2673a19201f 100644 --- a/testing/mochitest/specialpowers/content/specialpowers.js +++ b/testing/mochitest/specialpowers/content/specialpowers.js @@ -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 From a6f4cdfdca6da9a29c36dd2cfbdc5328f51acbf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabrice=20Desr=C3=A9?= Date: Tue, 6 Sep 2011 22:53:08 -0700 Subject: [PATCH 23/23] Followup fix for bug 641552. Make sure to enumerate new category on startup. r=jst --- dom/base/nsScriptNameSpaceManager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dom/base/nsScriptNameSpaceManager.cpp b/dom/base/nsScriptNameSpaceManager.cpp index 1a09a204b85..84574648e1c 100644 --- a/dom/base/nsScriptNameSpaceManager.cpp +++ b/dom/base/nsScriptNameSpaceManager.cpp @@ -442,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 serv =