diff options
Diffstat (limited to 'js/src')
291 files changed, 4500 insertions, 4589 deletions
diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 20678c68c..bc99e62b5 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -138,6 +138,17 @@ distclean:: CFLAGS += $(MOZ_ZLIB_CFLAGS) +ifeq ($(OS_ARCH),SunOS) +ifeq ($(TARGET_CPU),sparc) + +ifdef GNU_CC +CFLAGS += -mcpu=v9 +CXXFLAGS += -mcpu=v9 +endif #GNU_CC + +endif +endif + $(LIBRARY_NAME).pc: js.pc cp $^ $@ diff --git a/js/src/builtin/AsyncIteration.js b/js/src/builtin/AsyncIteration.js new file mode 100644 index 000000000..029c7c0f8 --- /dev/null +++ b/js/src/builtin/AsyncIteration.js @@ -0,0 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function AsyncIteratorIdentity() { + return this; +} diff --git a/js/src/builtin/IntlTimeZoneData.h b/js/src/builtin/IntlTimeZoneData.h index 8f963ffbc..1612f0f6b 100644 --- a/js/src/builtin/IntlTimeZoneData.h +++ b/js/src/builtin/IntlTimeZoneData.h @@ -1,5 +1,5 @@ // Generated by make_intl_data.py. DO NOT EDIT. -// tzdata version = 2019a +// tzdata version = 2019c #ifndef builtin_IntlTimeZoneData_h #define builtin_IntlTimeZoneData_h diff --git a/js/src/builtin/Iterator.js b/js/src/builtin/Iterator.js index 735eec7a0..e25b76156 100644 --- a/js/src/builtin/Iterator.js +++ b/js/src/builtin/Iterator.js @@ -84,44 +84,3 @@ function LegacyIteratorShim() { function LegacyGeneratorIteratorShim() { return NewLegacyIterator(ToObject(this), LegacyGeneratorIterator); } - -// 7.4.8 CreateListIterator() -function CreateListIterator(array) { - let iterator = NewListIterator(); - UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_TARGET, array); - UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_NEXT_INDEX, 0); - - // 7.4.8.1 ListIterator next() - // The spec requires that we use a new next function per iterator object. - let next = function() { - if (!IsObject(this) || !IsListIterator(this)) - return callFunction(CallListIteratorMethodIfWrapped, this, "ListIteratorNext"); - - if (ActiveFunction() !== UnsafeGetReservedSlot(this, ITERATOR_SLOT_NEXT_METHOD)) - ThrowTypeError(JSMSG_INCOMPATIBLE_METHOD, "next", "method", ToString(this)); - - let array = UnsafeGetObjectFromReservedSlot(this, ITERATOR_SLOT_TARGET); - let index = UnsafeGetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX); - - if (index >= ToLength(array.length)) { - UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, 1/0); - return { value: undefined, done: true }; - } - - UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, index + 1); - return { value: array[index], done: false }; - }; - - UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_NEXT_METHOD, next); - iterator.next = next; - - iterator[std_iterator] = ListIteratorIdentity; - return iterator; -} - -function ListIteratorIdentity() { - if (!IsObject(this) || !IsListIterator(this)) - return callFunction(CallListIteratorMethodIfWrapped, this, "ListIteratorIdentity"); - - return this; -} diff --git a/js/src/builtin/Module.js b/js/src/builtin/Module.js index 7b70a7fe8..5c3d5e147 100644 --- a/js/src/builtin/Module.js +++ b/js/src/builtin/Module.js @@ -65,12 +65,12 @@ function ModuleGetExportedNames(exportStarSet = []) return exportedNames; } -// 15.2.1.16.3 ResolveExport(exportName, resolveSet, exportStarSet) -function ModuleResolveExport(exportName, resolveSet = [], exportStarSet = []) +// 15.2.1.16.3 ResolveExport(exportName, resolveSet) +function ModuleResolveExport(exportName, resolveSet = []) { if (!IsObject(this) || !IsModule(this)) { return callFunction(CallModuleMethodIfWrapped, this, exportName, resolveSet, - exportStarSet, "ModuleResolveExport"); + "ModuleResolveExport"); } // Step 1 @@ -100,38 +100,29 @@ function ModuleResolveExport(exportName, resolveSet = [], exportStarSet = []) let e = indirectExportEntries[i]; if (exportName === e.exportName) { let importedModule = CallModuleResolveHook(module, e.moduleRequest, - MODULE_STATE_INSTANTIATED); - let indirectResolution = callFunction(importedModule.resolveExport, importedModule, - e.importName, resolveSet, exportStarSet); - if (indirectResolution !== null) - return indirectResolution; + MODULE_STATE_PARSED); + return callFunction(importedModule.resolveExport, importedModule, e.importName, + resolveSet); } } // Step 6 if (exportName === "default") { // A default export cannot be provided by an export *. - ThrowSyntaxError(JSMSG_BAD_DEFAULT_EXPORT); + return null; } // Step 7 - if (callFunction(ArrayIncludes, exportStarSet, module)) - return null; - - // Step 8 - _DefineDataProperty(exportStarSet, exportStarSet.length, module); - - // Step 9 let starResolution = null; - // Step 10 + // Step 8 let starExportEntries = module.starExportEntries; for (let i = 0; i < starExportEntries.length; i++) { let e = starExportEntries[i]; let importedModule = CallModuleResolveHook(module, e.moduleRequest, - MODULE_STATE_INSTANTIATED); + MODULE_STATE_PARSED); let resolution = callFunction(importedModule.resolveExport, importedModule, - exportName, resolveSet, exportStarSet); + exportName, resolveSet); if (resolution === "ambiguous") return resolution; @@ -148,6 +139,7 @@ function ModuleResolveExport(exportName, resolveSet = [], exportStarSet = []) } } + // Step 9 return starResolution; } @@ -213,8 +205,8 @@ function GetModuleEnvironment(module) function RecordInstantationFailure(module) { - // Set the module's environment slot to 'null' to indicate a failed module - // instantiation. + // Set the module's state to 'failed' to indicate a failed module + // instantiation and reset the environment slot to 'undefined'. assert(IsModule(module), "Non-module passed to RecordInstantationFailure"); SetModuleState(module, MODULE_STATE_FAILED); UnsafeSetReservedSlot(module, MODULE_OBJECT_ENVIRONMENT_SLOT, undefined); @@ -275,11 +267,13 @@ function ModuleDeclarationInstantiation() ThrowSyntaxError(JSMSG_MISSING_IMPORT, imp.importName); if (resolution === "ambiguous") ThrowSyntaxError(JSMSG_AMBIGUOUS_IMPORT, imp.importName); + if (resolution.module.state < MODULE_STATE_INSTANTIATED) + ThrowInternalError(JSMSG_BAD_MODULE_STATE); CreateImportBinding(env, imp.localName, resolution.module, resolution.bindingName); } } - // Step 16.iv + // Step 17.a.iii InstantiateModuleFunctionDeclarations(module); } catch (e) { RecordInstantationFailure(module); @@ -318,11 +312,3 @@ function ModuleEvaluation() return EvaluateModule(module); } _SetCanonicalName(ModuleEvaluation, "ModuleEvaluation"); - -function ModuleNamespaceEnumerate() -{ - if (!IsObject(this) || !IsModuleNamespace(this)) - return callFunction(CallModuleMethodIfWrapped, this, "ModuleNamespaceEnumerate"); - - return CreateListIterator(ModuleNamespaceExports(this)); -} diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp index 798ef46e1..575bab0b0 100644 --- a/js/src/builtin/ModuleObject.cpp +++ b/js/src/builtin/ModuleObject.cpp @@ -147,7 +147,7 @@ DEFINE_GETTER_FUNCTIONS(ExportEntryObject, moduleRequest, ModuleRequestSlot) DEFINE_GETTER_FUNCTIONS(ExportEntryObject, importName, ImportNameSlot) DEFINE_GETTER_FUNCTIONS(ExportEntryObject, localName, LocalNameSlot) -DEFINE_ATOM_ACCESSOR_METHOD(ExportEntryObject, exportName) +DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ExportEntryObject, exportName) DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ExportEntryObject, moduleRequest) DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ExportEntryObject, importName) DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ExportEntryObject, localName) @@ -289,14 +289,6 @@ ModuleNamespaceObject::create(JSContext* cx, HandleModuleObject module) if (!object) return nullptr; - RootedId funName(cx, INTERNED_STRING_TO_JSID(cx, cx->names().Symbol_iterator_fun)); - RootedFunction enumerateFun(cx); - enumerateFun = JS::GetSelfHostedFunction(cx, "ModuleNamespaceEnumerate", funName, 0); - if (!enumerateFun) - return nullptr; - - SetProxyExtra(object, ProxyHandler::EnumerateFunctionSlot, ObjectValue(*enumerateFun)); - return &object->as<ModuleNamespaceObject>(); } @@ -338,14 +330,9 @@ ModuleNamespaceObject::addBinding(JSContext* cx, HandleAtom exportedName, const char ModuleNamespaceObject::ProxyHandler::family = 0; ModuleNamespaceObject::ProxyHandler::ProxyHandler() - : BaseProxyHandler(&family, true) + : BaseProxyHandler(&family, false) {} -JS::Value ModuleNamespaceObject::ProxyHandler::getEnumerateFunction(HandleObject proxy) const -{ - return GetProxyExtra(proxy, EnumerateFunctionSlot); -} - bool ModuleNamespaceObject::ProxyHandler::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const @@ -358,6 +345,8 @@ bool ModuleNamespaceObject::ProxyHandler::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto, ObjectOpResult& result) const { + if (!proto) + return result.succeed(); return result.failCantSetProto(); } @@ -402,21 +391,12 @@ ModuleNamespaceObject::ProxyHandler::getOwnPropertyDescriptor(JSContext* cx, Han Rooted<ModuleNamespaceObject*> ns(cx, &proxy->as<ModuleNamespaceObject>()); if (JSID_IS_SYMBOL(id)) { Rooted<JS::Symbol*> symbol(cx, JSID_TO_SYMBOL(id)); - if (symbol == cx->wellKnownSymbols().iterator) { - RootedValue enumerateFun(cx, getEnumerateFunction(proxy)); - desc.object().set(proxy); - desc.setConfigurable(false); - desc.setEnumerable(false); - desc.setValue(enumerateFun); - return true; - } - if (symbol == cx->wellKnownSymbols().toStringTag) { RootedValue value(cx, StringValue(cx->names().Module)); desc.object().set(proxy); desc.setWritable(false); desc.setEnumerable(false); - desc.setConfigurable(true); + desc.setConfigurable(false); desc.setValue(value); return true; } @@ -458,8 +438,8 @@ ModuleNamespaceObject::ProxyHandler::has(JSContext* cx, HandleObject proxy, Hand Rooted<ModuleNamespaceObject*> ns(cx, &proxy->as<ModuleNamespaceObject>()); if (JSID_IS_SYMBOL(id)) { Rooted<JS::Symbol*> symbol(cx, JSID_TO_SYMBOL(id)); - return symbol == cx->wellKnownSymbols().iterator || - symbol == cx->wellKnownSymbols().toStringTag; + *bp = symbol == cx->wellKnownSymbols().toStringTag; + return true; } *bp = ns->bindings().has(id); @@ -473,23 +453,21 @@ ModuleNamespaceObject::ProxyHandler::get(JSContext* cx, HandleObject proxy, Hand Rooted<ModuleNamespaceObject*> ns(cx, &proxy->as<ModuleNamespaceObject>()); if (JSID_IS_SYMBOL(id)) { Rooted<JS::Symbol*> symbol(cx, JSID_TO_SYMBOL(id)); - if (symbol == cx->wellKnownSymbols().iterator) { - vp.set(getEnumerateFunction(proxy)); - return true; - } - if (symbol == cx->wellKnownSymbols().toStringTag) { vp.setString(cx->names().Module); return true; } - return false; + vp.setUndefined(); + return true; } ModuleEnvironmentObject* env; Shape* shape; - if (!ns->bindings().lookup(id, &env, &shape)) - return false; + if (!ns->bindings().lookup(id, &env, &shape)) { + vp.setUndefined(); + return true; + } RootedValue value(cx, env->getSlot(shape->slot())); if (value.isMagic(JS_UNINITIALIZED_LEXICAL)) { @@ -526,7 +504,7 @@ ModuleNamespaceObject::ProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject Rooted<ModuleNamespaceObject*> ns(cx, &proxy->as<ModuleNamespaceObject>()); RootedObject exports(cx, &ns->exports()); uint32_t count; - if (!GetLengthProperty(cx, exports, &count) || !props.reserve(props.length() + count)) + if (!GetLengthProperty(cx, exports, &count) || !props.reserve(props.length() + count + 1)) return false; Rooted<ValueVector> names(cx, ValueVector(cx)); @@ -536,6 +514,8 @@ ModuleNamespaceObject::ProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject for (uint32_t i = 0; i < count; i++) props.infallibleAppend(AtomToId(&names[i].toString()->asAtom())); + props.infallibleAppend(SYMBOL_TO_JSID(cx->wellKnownSymbols().toStringTag)); + return true; } @@ -1014,7 +994,7 @@ GlobalObject::initModuleProto(JSContext* cx, Handle<GlobalObject*> global) static const JSFunctionSpec protoFunctions[] = { JS_SELF_HOSTED_FN("getExportedNames", "ModuleGetExportedNames", 1, 0), - JS_SELF_HOSTED_FN("resolveExport", "ModuleResolveExport", 3, 0), + JS_SELF_HOSTED_FN("resolveExport", "ModuleResolveExport", 2, 0), JS_SELF_HOSTED_FN("declarationInstantiation", "ModuleDeclarationInstantiation", 0, 0), JS_SELF_HOSTED_FN("evaluation", "ModuleEvaluation", 0, 0), JS_FS_END @@ -1164,6 +1144,13 @@ ModuleBuilder::processExport(frontend::ParseNode* pn) bool isDefault = pn->getKind() == PNK_EXPORT_DEFAULT; ParseNode* kid = isDefault ? pn->pn_left : pn->pn_kid; + if (isDefault && pn->pn_right) { + // This is an export default containing an expression. + RootedAtom localName(cx_, cx_->names().starDefaultStar); + RootedAtom exportName(cx_, cx_->names().default_); + return appendExportEntry(exportName, localName); + } + switch (kid->getKind()) { case PNK_EXPORT_SPEC_LIST: MOZ_ASSERT(!isDefault); @@ -1177,53 +1164,46 @@ ModuleBuilder::processExport(frontend::ParseNode* pn) break; case PNK_CLASS: { - const ClassNode& cls = kid->as<ClassNode>(); - MOZ_ASSERT(cls.names()); - RootedAtom localName(cx_, cls.names()->innerBinding()->pn_atom); - RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get()); - if (!appendExportEntry(exportName, localName)) - return false; - break; + const ClassNode& cls = kid->as<ClassNode>(); + MOZ_ASSERT(cls.names()); + RootedAtom localName(cx_, cls.names()->innerBinding()->pn_atom); + RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get()); + if (!appendExportEntry(exportName, localName)) + return false; + break; } case PNK_VAR: case PNK_CONST: case PNK_LET: { - MOZ_ASSERT(kid->isArity(PN_LIST)); - for (ParseNode* var = kid->pn_head; var; var = var->pn_next) { - if (var->isKind(PNK_ASSIGN)) - var = var->pn_left; - MOZ_ASSERT(var->isKind(PNK_NAME)); - RootedAtom localName(cx_, var->pn_atom); - RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get()); - if (!appendExportEntry(exportName, localName)) - return false; - } - break; + MOZ_ASSERT(kid->isArity(PN_LIST)); + for (ParseNode* var = kid->pn_head; var; var = var->pn_next) { + if (var->isKind(PNK_ASSIGN)) + var = var->pn_left; + MOZ_ASSERT(var->isKind(PNK_NAME)); + RootedAtom localName(cx_, var->pn_atom); + RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get()); + if (!appendExportEntry(exportName, localName)) + return false; + } + break; } case PNK_FUNCTION: { - RootedFunction func(cx_, kid->pn_funbox->function()); - if (!func->isArrow()) { - RootedAtom localName(cx_, func->explicitName()); - RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get()); - MOZ_ASSERT_IF(isDefault, localName); - if (!appendExportEntry(exportName, localName)) - return false; - break; - } - } - - MOZ_FALLTHROUGH; // Arrow functions are handled below. - - default: - MOZ_ASSERT(isDefault); - RootedAtom localName(cx_, cx_->names().starDefaultStar); - RootedAtom exportName(cx_, cx_->names().default_); + RootedFunction func(cx_, kid->pn_funbox->function()); + MOZ_ASSERT(!func->isArrow()); + RootedAtom localName(cx_, func->explicitName()); + RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get()); + MOZ_ASSERT_IF(isDefault, localName); if (!appendExportEntry(exportName, localName)) return false; break; + } + + default: + MOZ_CRASH("Unexpected parse node"); } + return true; } diff --git a/js/src/builtin/ModuleObject.h b/js/src/builtin/ModuleObject.h index e83520ebe..22db762ac 100644 --- a/js/src/builtin/ModuleObject.h +++ b/js/src/builtin/ModuleObject.h @@ -142,15 +142,8 @@ class ModuleNamespaceObject : public ProxyObject private: struct ProxyHandler : public BaseProxyHandler { - enum - { - EnumerateFunctionSlot = 0 - }; - ProxyHandler(); - JS::Value getEnumerateFunction(HandleObject proxy) const; - bool getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, MutableHandle<PropertyDescriptor> desc) const override; bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id, diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index 56c77f304..bfcc8d20e 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -568,97 +568,6 @@ obj_setPrototypeOf(JSContext* cx, unsigned argc, Value* vp) return true; } -#if JS_HAS_OBJ_WATCHPOINT - -bool -js::WatchHandler(JSContext* cx, JSObject* obj_, jsid id_, const JS::Value& old, - JS::Value* nvp, void* closure) -{ - RootedObject obj(cx, obj_); - RootedId id(cx, id_); - - /* Avoid recursion on (obj, id) already being watched on cx. */ - AutoResolving resolving(cx, obj, id, AutoResolving::WATCH); - if (resolving.alreadyStarted()) - return true; - - FixedInvokeArgs<3> args(cx); - - args[0].set(IdToValue(id)); - args[1].set(old); - args[2].set(*nvp); - - RootedValue callable(cx, ObjectValue(*static_cast<JSObject*>(closure))); - RootedValue thisv(cx, ObjectValue(*obj)); - RootedValue rv(cx); - if (!Call(cx, callable, thisv, args, &rv)) - return false; - - *nvp = rv; - return true; -} - -static bool -obj_watch(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - RootedObject obj(cx, ToObject(cx, args.thisv())); - if (!obj) - return false; - - if (!GlobalObject::warnOnceAboutWatch(cx, obj)) - return false; - - if (args.length() <= 1) { - ReportMissingArg(cx, args.calleev(), 1); - return false; - } - - RootedObject callable(cx, ValueToCallable(cx, args[1], args.length() - 2)); - if (!callable) - return false; - - RootedId propid(cx); - if (!ValueToId<CanGC>(cx, args[0], &propid)) - return false; - - if (!WatchProperty(cx, obj, propid, callable)) - return false; - - args.rval().setUndefined(); - return true; -} - -static bool -obj_unwatch(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - RootedObject obj(cx, ToObject(cx, args.thisv())); - if (!obj) - return false; - - if (!GlobalObject::warnOnceAboutWatch(cx, obj)) - return false; - - RootedId id(cx); - if (args.length() != 0) { - if (!ValueToId<CanGC>(cx, args[0], &id)) - return false; - } else { - id = JSID_VOID; - } - - if (!UnwatchProperty(cx, obj, id)) - return false; - - args.rval().setUndefined(); - return true; -} - -#endif /* JS_HAS_OBJ_WATCHPOINT */ - /* ECMA 15.2.4.5. */ bool js::obj_hasOwnProperty(JSContext* cx, unsigned argc, Value* vp) @@ -1290,10 +1199,6 @@ static const JSFunctionSpec object_methods[] = { JS_FN(js_toString_str, obj_toString, 0,0), JS_SELF_HOSTED_FN(js_toLocaleString_str, "Object_toLocaleString", 0, 0), JS_SELF_HOSTED_FN(js_valueOf_str, "Object_valueOf", 0,0), -#if JS_HAS_OBJ_WATCHPOINT - JS_FN(js_watch_str, obj_watch, 2,0), - JS_FN(js_unwatch_str, obj_unwatch, 1,0), -#endif JS_FN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0), JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0), JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0), diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp index ec7845e89..4900a91cb 100644 --- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -11,11 +11,13 @@ #include "mozilla/TimeStamp.h" #include "jscntxt.h" +#include "jsexn.h" +#include "jsiter.h" #include "gc/Heap.h" #include "js/Debug.h" #include "vm/AsyncFunction.h" -#include "vm/SelfHosting.h" +#include "vm/AsyncIteration.h" #include "jsobjinlines.h" @@ -31,10 +33,34 @@ MillisecondsSinceStartup() return (now - mozilla::TimeStamp::ProcessCreation(ignored)).ToMilliseconds(); } -#define PROMISE_HANDLER_IDENTITY 0 -#define PROMISE_HANDLER_THROWER 1 -#define PROMISE_HANDLER_AWAIT_FULFILLED 2 -#define PROMISE_HANDLER_AWAIT_REJECTED 3 +enum PromiseHandler { + PromiseHandlerIdentity = 0, + PromiseHandlerThrower, + + // ES 2018 draft 25.5.5.4-5. + PromiseHandlerAsyncFunctionAwaitedFulfilled, + PromiseHandlerAsyncFunctionAwaitedRejected, + + // Async Iteration proposal 4.1. + PromiseHandlerAsyncGeneratorAwaitedFulfilled, + PromiseHandlerAsyncGeneratorAwaitedRejected, + + // Async Iteration proposal 11.4.3.5.1-2. + PromiseHandlerAsyncGeneratorResumeNextReturnFulfilled, + PromiseHandlerAsyncGeneratorResumeNextReturnRejected, + + // Async Iteration proposal 11.4.3.7 steps 8.c-e. + PromiseHandlerAsyncGeneratorYieldReturnAwaitedFulfilled, + PromiseHandlerAsyncGeneratorYieldReturnAwaitedRejected, + + // Async Iteration proposal 11.1.3.2.5. + // Async-from-Sync iterator handlers take the resolved value and create new + // iterator objects. To do so it needs to forward whether the iterator is + // done. In spec, this is achieved via the [[Done]] internal slot. We + // enumerate both true and false cases here. + PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone, + PromiseHandlerAsyncFromSyncIteratorValueUnwrapNotDone, +}; enum ResolutionMode { ResolveMode, @@ -47,9 +73,8 @@ enum ResolveFunctionSlots { }; enum RejectFunctionSlots { - RejectFunctionSlot_Promise = ResolveFunctionSlot_Promise, + RejectFunctionSlot_Promise = 0, RejectFunctionSlot_ResolveFunction, - RejectFunctionSlot_PromiseAllData = RejectFunctionSlot_ResolveFunction, }; enum PromiseAllResolveElementFunctionSlots { @@ -86,7 +111,7 @@ class PromiseAllDataHolder : public NativeObject static const Class class_; JSObject* promiseObj() { return &getFixedSlot(PromiseAllDataHolderSlot_Promise).toObject(); } JSObject* resolveObj() { - return getFixedSlot(PromiseAllDataHolderSlot_ResolveFunction).toObjectOrNull(); + return &getFixedSlot(PromiseAllDataHolderSlot_ResolveFunction).toObject(); } Value valuesArray() { return getFixedSlot(PromiseAllDataHolderSlot_ValuesArray); } int32_t remainingCount() { @@ -126,7 +151,7 @@ NewPromiseAllDataHolder(JSContext* cx, HandleObject resultPromise, HandleValue v dataHolder->setFixedSlot(PromiseAllDataHolderSlot_Promise, ObjectValue(*resultPromise)); dataHolder->setFixedSlot(PromiseAllDataHolderSlot_RemainingElements, Int32Value(1)); dataHolder->setFixedSlot(PromiseAllDataHolderSlot_ValuesArray, valuesArray); - dataHolder->setFixedSlot(PromiseAllDataHolderSlot_ResolveFunction, ObjectOrNullValue(resolve)); + dataHolder->setFixedSlot(PromiseAllDataHolderSlot_ResolveFunction, ObjectValue(*resolve)); return dataHolder; } @@ -177,13 +202,15 @@ enum ReactionRecordSlots { ReactionRecordSlot_IncumbentGlobalObject, ReactionRecordSlot_Flags, ReactionRecordSlot_HandlerArg, + ReactionRecordSlot_Generator, ReactionRecordSlots, }; #define REACTION_FLAG_RESOLVED 0x1 #define REACTION_FLAG_FULFILLED 0x2 #define REACTION_FLAG_IGNORE_DEFAULT_RESOLUTION 0x4 -#define REACTION_FLAG_AWAIT 0x8 +#define REACTION_FLAG_ASYNC_FUNCTION 0x8 +#define REACTION_FLAG_ASYNC_GENERATOR 0x10 // ES2016, 25.4.1.2. class PromiseReactionRecord : public NativeObject @@ -210,14 +237,30 @@ class PromiseReactionRecord : public NativeObject flags |= REACTION_FLAG_FULFILLED; setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags)); } - void setIsAwait() { + void setIsAsyncFunction() { int32_t flags = this->flags(); - flags |= REACTION_FLAG_AWAIT; + flags |= REACTION_FLAG_ASYNC_FUNCTION; setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags)); } - bool isAwait() { + bool isAsyncFunction() { + int32_t flags = this->flags(); + return flags & REACTION_FLAG_ASYNC_FUNCTION; + } + void setIsAsyncGenerator(Handle<AsyncGeneratorObject*> asyncGenObj) { int32_t flags = this->flags(); - return flags & REACTION_FLAG_AWAIT; + flags |= REACTION_FLAG_ASYNC_GENERATOR; + setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags)); + + setFixedSlot(ReactionRecordSlot_Generator, ObjectValue(*asyncGenObj)); + } + bool isAsyncGenerator() { + int32_t flags = this->flags(); + return flags & REACTION_FLAG_ASYNC_GENERATOR; + } + AsyncGeneratorObject* asyncGenerator() { + MOZ_ASSERT(isAsyncGenerator()); + return &getFixedSlot(ReactionRecordSlot_Generator).toObject() + .as<AsyncGeneratorObject>(); } Value handler() { MOZ_ASSERT(targetState() != JS::PromiseState::Pending); @@ -277,12 +320,6 @@ CreateResolvingFunctions(JSContext* cx, HandleValue promise, if (!reject) return false; - // TODO: remove this once all self-hosted promise code is gone. - // The resolving functions are trusted, so self-hosted code should be able - // to call them using callFunction instead of callContentFunction. - resolve->setFlags(resolve->flags() | JSFunction::SELF_HOSTED); - reject->setFlags(reject->flags() | JSFunction::SELF_HOSTED); - resolve->setExtendedSlot(ResolveFunctionSlot_Promise, promise); resolve->setExtendedSlot(ResolveFunctionSlot_RejectFunction, ObjectValue(*reject)); @@ -319,28 +356,27 @@ RejectPromiseFunction(JSContext* cx, unsigned argc, Value* vp) return true; } + // Step 5. + // Here, we only remove the Promise reference from the resolution + // functions. Actually marking it as fulfilled/rejected happens later. + ClearResolutionFunctionSlots(reject); + RootedObject promise(cx, &promiseVal.toObject()); // In some cases the Promise reference on the resolution function won't // have been removed during resolution, so we need to check that here, // too. if (promise->is<PromiseObject>() && - PromiseHasAnyFlag(promise->as<PromiseObject>(), PROMISE_FLAG_RESOLVED)) + promise->as<PromiseObject>().state() != JS::PromiseState::Pending) { - args.rval().setUndefined(); return true; } - // Step 5. - // Here, we only remove the Promise reference from the resolution - // functions. Actually marking it as fulfilled/rejected happens later. - ClearResolutionFunctionSlots(reject); - // Step 6. - bool result = RejectMaybeWrappedPromise(cx, promise, reasonVal); - if (result) - args.rval().setUndefined(); - return result; + if (!RejectMaybeWrappedPromise(cx, promise, reasonVal)) + return false; + args.rval().setUndefined(); + return true; } static MOZ_MUST_USE bool FulfillMaybeWrappedPromise(JSContext *cx, HandleObject promiseObj, @@ -351,16 +387,28 @@ static MOZ_MUST_USE bool EnqueuePromiseResolveThenableJob(JSContext* cx, HandleValue thenable, HandleValue thenVal); -// ES2016, 25.4.1.3.2, steps 7-13. +// ES2016, 25.4.1.3.2, steps 6-13. static MOZ_MUST_USE bool ResolvePromiseInternal(JSContext* cx, HandleObject promise, HandleValue resolutionVal) { - // Step 7. + // Step 7 (reordered). if (!resolutionVal.isObject()) return FulfillMaybeWrappedPromise(cx, promise, resolutionVal); RootedObject resolution(cx, &resolutionVal.toObject()); + // Step 6. + if (resolution == promise) { + // Step 6.a. + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_CANNOT_RESOLVE_PROMISE_WITH_ITSELF); + RootedValue selfResolutionError(cx); + MOZ_ALWAYS_TRUE(GetAndClearException(cx, &selfResolutionError)); + + // Step 6.b. + return RejectMaybeWrappedPromise(cx, promise, selfResolutionError); + } + // Step 8. RootedValue thenVal(cx); bool status = GetProperty(cx, resolution, resolution, cx->names().then, &thenVal); @@ -398,10 +446,7 @@ ResolvePromiseFunction(JSContext* cx, unsigned argc, Value* vp) RootedFunction resolve(cx, &args.callee().as<JSFunction>()); RootedValue resolutionVal(cx, args.get(0)); - // Steps 1-2. - RootedValue promiseVal(cx, resolve->getExtendedSlot(ResolveFunctionSlot_Promise)); - - // Steps 3-4. + // Steps 3-4 (reordered). // We use the reference to the reject function as a signal for whether // the resolve or reject function was already called, at which point // the references on each of the functions are cleared. @@ -410,43 +455,28 @@ ResolvePromiseFunction(JSContext* cx, unsigned argc, Value* vp) return true; } - RootedObject promise(cx, &promiseVal.toObject()); + // Steps 1-2 (reordered). + RootedObject promise(cx, &resolve->getExtendedSlot(ResolveFunctionSlot_Promise).toObject()); + + // Step 5. + // Here, we only remove the Promise reference from the resolution + // functions. Actually marking it as fulfilled/rejected happens later. + ClearResolutionFunctionSlots(resolve); // In some cases the Promise reference on the resolution function won't // have been removed during resolution, so we need to check that here, // too. if (promise->is<PromiseObject>() && - PromiseHasAnyFlag(promise->as<PromiseObject>(), PROMISE_FLAG_RESOLVED)) + promise->as<PromiseObject>().state() != JS::PromiseState::Pending) { - args.rval().setUndefined(); return true; } - // Step 5. - // Here, we only remove the Promise reference from the resolution - // functions. Actually marking it as fulfilled/rejected happens later. - ClearResolutionFunctionSlots(resolve); - - // Step 6. - if (resolutionVal == promiseVal) { - // Step 6.a. - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_CANNOT_RESOLVE_PROMISE_WITH_ITSELF); - RootedValue selfResolutionError(cx); - bool status = GetAndClearException(cx, &selfResolutionError); - MOZ_ASSERT(status); - - // Step 6.b. - status = RejectMaybeWrappedPromise(cx, promise, selfResolutionError); - if (status) - args.rval().setUndefined(); - return status; - } - - bool status = ResolvePromiseInternal(cx, promise, resolutionVal); - if (status) - args.rval().setUndefined(); - return status; + // Steps 6-13. + if (!ResolvePromiseInternal(cx, promise, resolutionVal)) + return false; + args.rval().setUndefined(); + return true; } static bool PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp); @@ -652,7 +682,7 @@ enum GetCapabilitiesExecutorSlots { }; static MOZ_MUST_USE PromiseObject* -CreatePromiseObjectWithDefaultResolution(JSContext* cx) +CreatePromiseObjectWithoutResolutionFunctions(JSContext* cx) { Rooted<PromiseObject*> promise(cx, CreatePromiseObjectInternal(cx)); if (!promise) @@ -687,7 +717,7 @@ NewPromiseCapability(JSContext* cx, HandleObject C, MutableHandleObject promise, // in the list passed to all/race, which (potentially) means exposing them // to content. if (canOmitResolutionFunctions && IsNativeFunction(cVal, PromiseConstructor)) { - promise.set(CreatePromiseObjectWithDefaultResolution(cx)); + promise.set(CreatePromiseObjectWithoutResolutionFunctions(cx)); if (!promise) return false; return true; @@ -795,9 +825,7 @@ RejectMaybeWrappedPromise(JSContext *cx, HandleObject promiseObj, HandleValue re // interpreter frame active right now. If a thenable job with a // throwing `then` function got us here, that'll not be the case, // so we add one by throwing the error from self-hosted code. - FixedInvokeArgs<1> getErrorArgs(cx); - getErrorArgs[0].set(Int32Value(JSMSG_PROMISE_ERROR_IN_WRAPPED_REJECTION_REASON)); - if (!CallSelfHostedFunction(cx, "GetInternalError", reason, getErrorArgs, &reason)) + if (!GetInternalError(cx, JSMSG_PROMISE_ERROR_IN_WRAPPED_REJECTION_REASON, &reason)) return false; } } @@ -832,10 +860,10 @@ TriggerPromiseReactions(JSContext* cx, HandleValue reactionsVal, JS::PromiseStat } static MOZ_MUST_USE bool -AwaitPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*> reaction, - MutableHandleValue rval) +AsyncFunctionPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*> reaction, + MutableHandleValue rval) { - MOZ_ASSERT(reaction->isAwait()); + MOZ_ASSERT(reaction->isAsyncFunction()); RootedValue handlerVal(cx, reaction->handler()); RootedValue argument(cx, reaction->handlerArg()); @@ -843,15 +871,14 @@ AwaitPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*> reaction, RootedValue generatorVal(cx, resultPromise->getFixedSlot(PromiseSlot_AwaitGenerator)); int32_t handlerNum = int32_t(handlerVal.toNumber()); - MOZ_ASSERT(handlerNum == PROMISE_HANDLER_AWAIT_FULFILLED - || handlerNum == PROMISE_HANDLER_AWAIT_REJECTED); // Await's handlers don't return a value, nor throw exception. // They fail only on OOM. - if (handlerNum == PROMISE_HANDLER_AWAIT_FULFILLED) { + if (handlerNum == PromiseHandlerAsyncFunctionAwaitedFulfilled) { if (!AsyncFunctionAwaitedFulfilled(cx, resultPromise, generatorVal, argument)) return false; } else { + MOZ_ASSERT(handlerNum == PromiseHandlerAsyncFunctionAwaitedRejected); if (!AsyncFunctionAwaitedRejected(cx, resultPromise, generatorVal, argument)) return false; } @@ -860,6 +887,55 @@ AwaitPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*> reaction, return true; } +static MOZ_MUST_USE bool +AsyncGeneratorPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*> reaction, + MutableHandleValue rval) +{ + MOZ_ASSERT(reaction->isAsyncGenerator()); + + RootedValue handlerVal(cx, reaction->handler()); + RootedValue argument(cx, reaction->handlerArg()); + Rooted<AsyncGeneratorObject*> asyncGenObj(cx, reaction->asyncGenerator()); + + int32_t handlerNum = int32_t(handlerVal.toNumber()); + + // Await's handlers don't return a value, nor throw exception. + // They fail only on OOM. + if (handlerNum == PromiseHandlerAsyncGeneratorAwaitedFulfilled) { + // 4.1.1. + if (!AsyncGeneratorAwaitedFulfilled(cx, asyncGenObj, argument)) + return false; + } else if (handlerNum == PromiseHandlerAsyncGeneratorAwaitedRejected) { + // 4.1.2. + if (!AsyncGeneratorAwaitedRejected(cx, asyncGenObj, argument)) + return false; + } else if (handlerNum == PromiseHandlerAsyncGeneratorResumeNextReturnFulfilled) { + asyncGenObj->setCompleted(); + // 11.4.3.5.1 step 1. + if (!AsyncGeneratorResolve(cx, asyncGenObj, argument, true)) + return false; + } else if (handlerNum == PromiseHandlerAsyncGeneratorResumeNextReturnRejected) { + asyncGenObj->setCompleted(); + // 11.4.3.5.2 step 1. + if (!AsyncGeneratorReject(cx, asyncGenObj, argument)) + return false; + } else if (handlerNum == PromiseHandlerAsyncGeneratorYieldReturnAwaitedFulfilled) { + asyncGenObj->setExecuting(); + // 11.4.3.7 steps 8.d-e. + if (!AsyncGeneratorYieldReturnAwaitedFulfilled(cx, asyncGenObj, argument)) + return false; + } else { + MOZ_ASSERT(handlerNum == PromiseHandlerAsyncGeneratorYieldReturnAwaitedRejected); + asyncGenObj->setExecuting(); + // 11.4.3.7 step 8.c. + if (!AsyncGeneratorYieldReturnAwaitedRejected(cx, asyncGenObj, argument)) + return false; + } + + rval.setUndefined(); + return true; +} + // ES2016, 25.4.2.1. /** * Callback triggering the fulfill/reject reaction for a resolved Promise, @@ -903,8 +979,10 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp) // Steps 1-2. Rooted<PromiseReactionRecord*> reaction(cx, &reactionObj->as<PromiseReactionRecord>()); - if (reaction->isAwait()) - return AwaitPromiseReactionJob(cx, reaction, args.rval()); + if (reaction->isAsyncFunction()) + return AsyncFunctionPromiseReactionJob(cx, reaction, args.rval()); + if (reaction->isAsyncGenerator()) + return AsyncGeneratorPromiseReactionJob(cx, reaction, args.rval()); // Step 3. RootedValue handlerVal(cx, reaction->handler()); @@ -919,13 +997,23 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp) int32_t handlerNum = int32_t(handlerVal.toNumber()); // Step 4. - if (handlerNum == PROMISE_HANDLER_IDENTITY) { + if (handlerNum == PromiseHandlerIdentity) { handlerResult = argument; - } else { + } else if (handlerNum == PromiseHandlerThrower) { // Step 5. - MOZ_ASSERT(handlerNum == PROMISE_HANDLER_THROWER); resolutionMode = RejectMode; handlerResult = argument; + } else { + MOZ_ASSERT(handlerNum == PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone || + handlerNum == PromiseHandlerAsyncFromSyncIteratorValueUnwrapNotDone); + + bool done = handlerNum == PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone; + // Async Iteration proposal 11.1.3.2.5 step 1. + RootedObject resultObj(cx, CreateIterResultObject(cx, argument, done)); + if (!resultObj) + return false; + + handlerResult = ObjectValue(*resultObj); } } else { // Step 6. @@ -964,8 +1052,8 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp) * * Usage of the function's extended slots is as follows: * ThenableJobSlot_Handler: The handler to use as the Promise reaction. - * This can be PROMISE_HANDLER_IDENTITY, - * PROMISE_HANDLER_THROWER, or a callable. In the + * This can be PromiseHandlerIdentity, + * PromiseHandlerThrower, or a callable. In the * latter case, it's guaranteed to be an object * from the same compartment as the * PromiseReactionJob. @@ -1100,11 +1188,17 @@ GetResolveFunctionFromReject(JSFunction* reject) { MOZ_ASSERT(reject->maybeNative() == RejectPromiseFunction); Value resolveFunVal = reject->getExtendedSlot(RejectFunctionSlot_ResolveFunction); - if (IsNativeFunction(resolveFunVal, ResolvePromiseFunction)) - return &resolveFunVal.toObject().as<JSFunction>(); + MOZ_ASSERT(IsNativeFunction(resolveFunVal, ResolvePromiseFunction)); + return &resolveFunVal.toObject().as<JSFunction>(); +} - PromiseAllDataHolder* resolveFunObj = &resolveFunVal.toObject().as<PromiseAllDataHolder>(); - return &resolveFunObj->resolveObj()->as<JSFunction>(); +static JSFunction* +GetRejectFunctionFromResolve(JSFunction* resolve) +{ + MOZ_ASSERT(resolve->maybeNative() == ResolvePromiseFunction); + Value rejectFunVal = resolve->getExtendedSlot(ResolveFunctionSlot_RejectFunction); + MOZ_ASSERT(IsNativeFunction(rejectFunVal, RejectPromiseFunction)); + return &rejectFunVal.toObject().as<JSFunction>(); } static JSFunction* @@ -1140,8 +1234,7 @@ ClearResolutionFunctionSlots(JSFunction* resolutionFun) JSFunction* reject; if (resolutionFun->maybeNative() == ResolvePromiseFunction) { resolve = resolutionFun; - reject = &resolutionFun->getExtendedSlot(ResolveFunctionSlot_RejectFunction) - .toObject().as<JSFunction>(); + reject = GetRejectFunctionFromResolve(resolutionFun); } else { resolve = GetResolveFunctionFromReject(resolutionFun); reject = resolutionFun; @@ -1367,6 +1460,14 @@ PromiseObject::create(JSContext* cx, HandleObject executor, HandleObject proto / return promise; } +// ES2016, 25.4.3.1. skipping creation of resolution functions and executor +// function invocation. +/* static */ PromiseObject* +PromiseObject::createSkippingExecutor(JSContext* cx) +{ + return CreatePromiseObjectWithoutResolutionFunctions(cx); +} + static MOZ_MUST_USE bool PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C, HandleObject promiseObj, HandleObject resolve, HandleObject reject, @@ -1585,7 +1686,6 @@ RunResolutionFunction(JSContext *cx, HandleObject resolutionFun, HandleValue res if (promise->state() != JS::PromiseState::Pending) return true; - if (mode == ResolveMode) { if (!PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_RESOLVE_FUNCTION)) return true; @@ -1595,7 +1695,6 @@ RunResolutionFunction(JSContext *cx, HandleObject resolutionFun, HandleValue res if (!PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_REJECT_FUNCTION)) return true; return RejectMaybeWrappedPromise(cx, promiseObj, result); - } // ES2016, 25.4.4.1.1. @@ -1669,7 +1768,7 @@ PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C, // Step 6. RootedValue nextValue(cx); RootedId indexId(cx); - RootedValue rejectFunVal(cx, ObjectOrNullValue(reject)); + RootedValue rejectFunVal(cx, ObjectValue(*reject)); while (true) { // Steps a-c, e-g. @@ -1690,11 +1789,8 @@ PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C, // Steps d.iii-iv. if (remainingCount == 0) { - if (resolve) { - return RunResolutionFunction(cx, resolve, valuesArrayVal, ResolveMode, - promiseObj); - } - return ResolvePromiseInternal(cx, promiseObj, valuesArrayVal); + return RunResolutionFunction(cx, resolve, valuesArrayVal, ResolveMode, + promiseObj); } // We're all set for now! @@ -1817,13 +1913,8 @@ PromiseAllResolveElementFunction(JSContext* cx, unsigned argc, Value* vp) // Step 6 (Adapted to work with PromiseAllDataHolder's layout). RootedObject resolveAllFun(cx, data->resolveObj()); RootedObject promiseObj(cx, data->promiseObj()); - if (!resolveAllFun) { - if (!FulfillMaybeWrappedPromise(cx, promiseObj, valuesVal)) - return false; - } else { - if (!RunResolutionFunction(cx, resolveAllFun, valuesVal, ResolveMode, promiseObj)) - return false; - } + if (!RunResolutionFunction(cx, resolveAllFun, valuesVal, ResolveMode, promiseObj)) + return false; } // Step 11. @@ -1904,8 +1995,8 @@ PerformPromiseRace(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C, RootedValue CVal(cx, ObjectValue(*C)); RootedValue nextValue(cx); - RootedValue resolveFunVal(cx, ObjectOrNullValue(resolve)); - RootedValue rejectFunVal(cx, ObjectOrNullValue(reject)); + RootedValue resolveFunVal(cx, ObjectValue(*resolve)); + RootedValue rejectFunVal(cx, ObjectValue(*reject)); while (true) { // Steps a-c, e-g. @@ -2006,6 +2097,13 @@ CommonStaticResolveRejectImpl(JSContext* cx, HandleValue thisVal, HandleValue ar return promise; } +MOZ_MUST_USE JSObject* +js::PromiseResolve(JSContext* cx, HandleObject constructor, HandleValue value) +{ + RootedValue C(cx, ObjectValue(*constructor)); + return CommonStaticResolveRejectImpl(cx, C, value, ResolveMode); +} + /** * ES2016, 25.4.4.4, Promise.reject. */ @@ -2148,12 +2246,12 @@ static MOZ_MUST_USE bool PerformPromiseThenWithReaction(JSContext* cx, // Some async/await functions are implemented here instead of // js/src/builtin/AsyncFunction.cpp, to call Promise internal functions. -// Async Functions proposal 1.1.8 and 1.2.14 step 1. +// ES 2018 draft 14.6.11 and 14.7.14 step 1. MOZ_MUST_USE PromiseObject* js::CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal) { // Step 1. - Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithDefaultResolution(cx)); + Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); if (!promise) return nullptr; @@ -2162,7 +2260,7 @@ js::CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal) return promise; } -// Async Functions proposal 2.2 steps 3.f, 3.g. +// ES 2018 draft 25.5.5.2 steps 3.f, 3.g. MOZ_MUST_USE bool js::AsyncFunctionThrown(JSContext* cx, Handle<PromiseObject*> resultPromise) { @@ -2178,7 +2276,7 @@ js::AsyncFunctionThrown(JSContext* cx, Handle<PromiseObject*> resultPromise) return true; } -// Async Functions proposal 2.2 steps 3.d-e, 3.g. +// ES 2018 draft 25.5.5.2 steps 3.d-e, 3.g. MOZ_MUST_USE bool js::AsyncFunctionReturned(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value) { @@ -2190,28 +2288,30 @@ js::AsyncFunctionReturned(JSContext* cx, Handle<PromiseObject*> resultPromise, H return true; } -// Async Functions proposal 2.3 steps 2-8. -MOZ_MUST_USE bool -js::AsyncFunctionAwait(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value) +// Helper function that performs the equivalent steps as +// Async Iteration proposal 4.1 Await steps 2-3, 6-9 or similar. +template <typename T> +static MOZ_MUST_USE bool +InternalAwait(JSContext* cx, HandleValue value, HandleObject resultPromise, + HandleValue onFulfilled, HandleValue onRejected, T extraStep) { + MOZ_ASSERT(onFulfilled.isNumber() || onFulfilled.isObject()); + MOZ_ASSERT(onRejected.isNumber() || onRejected.isObject()); + // Step 2. - Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithDefaultResolution(cx)); + Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); if (!promise) return false; - // Steps 3. + // Step 3. if (!ResolvePromiseInternal(cx, promise, value)) return false; - // Steps 4-5. - RootedValue onFulfilled(cx, Int32Value(PROMISE_HANDLER_AWAIT_FULFILLED)); - RootedValue onRejected(cx, Int32Value(PROMISE_HANDLER_AWAIT_REJECTED)); - RootedObject incumbentGlobal(cx); if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal)) return false; - // Steps 6-7. + // Steps 7-8. Rooted<PromiseReactionRecord*> reaction(cx, NewReactionRecord(cx, resultPromise, onFulfilled, onRejected, nullptr, nullptr, @@ -2219,12 +2319,403 @@ js::AsyncFunctionAwait(JSContext* cx, Handle<PromiseObject*> resultPromise, Hand if (!reaction) return false; - reaction->setIsAwait(); + // Step 6. + extraStep(reaction); - // Step 8. + // Step 9. return PerformPromiseThenWithReaction(cx, promise, reaction); } +// ES 2018 draft 25.5.5.3 steps 2-10. +MOZ_MUST_USE bool +js::AsyncFunctionAwait(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value) +{ + // Steps 4-5. + RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncFunctionAwaitedFulfilled)); + RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncFunctionAwaitedRejected)); + + // Steps 2-3, 6-10. + auto extra = [](Handle<PromiseReactionRecord*> reaction) { + reaction->setIsAsyncFunction(); + }; + return InternalAwait(cx, value, resultPromise, onFulfilled, onRejected, extra); +} + +// Async Iteration proposal 4.1 Await steps 2-9. +MOZ_MUST_USE bool +js::AsyncGeneratorAwait(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue value) +{ + // Steps 4-5. + RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncGeneratorAwaitedFulfilled)); + RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncGeneratorAwaitedRejected)); + + // Steps 2-3, 6-9. + auto extra = [&](Handle<PromiseReactionRecord*> reaction) { + reaction->setIsAsyncGenerator(asyncGenObj); + }; + return InternalAwait(cx, value, nullptr, onFulfilled, onRejected, extra); +} + +// Async Iteration proposal 11.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next +// Async Iteration proposal 11.1.3.2.2 %AsyncFromSyncIteratorPrototype%.return +// Async Iteration proposal 11.1.3.2.3 %AsyncFromSyncIteratorPrototype%.throw +bool +js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind completionKind) +{ + // Step 1. + RootedValue thisVal(cx, args.thisv()); + + // Step 2. + RootedObject resultPromise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); + if (!resultPromise) + return false; + + // Step 3. + if (!thisVal.isObject() || !thisVal.toObject().is<AsyncFromSyncIteratorObject>()) { + // Step 3.a. + RootedValue badGeneratorError(cx); + if (!GetTypeError(cx, JSMSG_NOT_AN_ASYNC_ITERATOR, &badGeneratorError)) + return false; + + // Step 3.b. + if (!RejectMaybeWrappedPromise(cx, resultPromise, badGeneratorError)) + return false; + + // Step 3.c. + args.rval().setObject(*resultPromise); + return true; + } + + Rooted<AsyncFromSyncIteratorObject*> asyncIter( + cx, &thisVal.toObject().as<AsyncFromSyncIteratorObject>()); + + // Step 4. + RootedObject iter(cx, asyncIter->iterator()); + + RootedValue resultVal(cx); + RootedValue func(cx); + if (completionKind == CompletionKind::Normal) { + // 11.1.3.2.1 steps 5-6 (partially). + if (!GetProperty(cx, iter, iter, cx->names().next, &func)) + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + } else if (completionKind == CompletionKind::Return) { + // 11.1.3.2.2 steps 5-6. + if (!GetProperty(cx, iter, iter, cx->names().return_, &func)) + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + + // Step 7. + if (func.isNullOrUndefined()) { + // Step 7.a. + RootedObject resultObj(cx, CreateIterResultObject(cx, args.get(0), true)); + if (!resultObj) + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + + RootedValue resultVal(cx, ObjectValue(*resultObj)); + + // Step 7.b. + if (!ResolvePromiseInternal(cx, resultPromise, resultVal)) + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + + // Step 7.c. + args.rval().setObject(*resultPromise); + return true; + } + } else { + // 11.1.3.2.3 steps 5-6. + MOZ_ASSERT(completionKind == CompletionKind::Throw); + if (!GetProperty(cx, iter, iter, cx->names().throw_, &func)) + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + + // Step 7. + if (func.isNullOrUndefined()) { + // Step 7.a. + if (!RejectMaybeWrappedPromise(cx, resultPromise, args.get(0))) + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + + // Step 7.b. + args.rval().setObject(*resultPromise); + return true; + } + } + + // 11.1.3.2.1 steps 5-6 (partially). + // 11.1.3.2.2, 11.1.3.2.3 steps 8-9. + RootedValue iterVal(cx, ObjectValue(*iter)); + FixedInvokeArgs<1> args2(cx); + args2[0].set(args.get(0)); + if (!js::Call(cx, func, iterVal, args2, &resultVal)) + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + + // 11.1.3.2.1 steps 5-6 (partially). + // 11.1.3.2.2, 11.1.3.2.3 steps 10. + if (!resultVal.isObject()) { + CheckIsObjectKind kind; + switch (completionKind) { + case CompletionKind::Normal: + kind = CheckIsObjectKind::IteratorNext; + break; + case CompletionKind::Throw: + kind = CheckIsObjectKind::IteratorThrow; + break; + case CompletionKind::Return: + kind = CheckIsObjectKind::IteratorReturn; + break; + } + MOZ_ALWAYS_FALSE(ThrowCheckIsObject(cx, kind)); + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + } + + RootedObject resultObj(cx, &resultVal.toObject()); + + // Following step numbers are for 11.1.3.2.1. + // For 11.1.3.2.2 and 11.1.3.2.3, steps 7-16 corresponds to steps 11-20. + + // Steps 7-8. + RootedValue doneVal(cx); + if (!GetProperty(cx, resultObj, resultObj, cx->names().done, &doneVal)) + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + bool done = ToBoolean(doneVal); + + // Steps 9-10. + RootedValue value(cx); + if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value)) + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + + // Steps 13-14. + RootedValue onFulfilled(cx, Int32Value(done + ? PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone + : PromiseHandlerAsyncFromSyncIteratorValueUnwrapNotDone)); + RootedValue onRejected(cx, Int32Value(PromiseHandlerThrower)); + + // Steps 11-12, 15. + auto extra = [](Handle<PromiseReactionRecord*> reaction) { + }; + if (!InternalAwait(cx, value, resultPromise, onFulfilled, onRejected, extra)) + return false; + + // Step 16. + args.rval().setObject(*resultPromise); + return true; +} + +static MOZ_MUST_USE bool +AsyncGeneratorResumeNext(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj); + +// Async Iteration proposal 11.4.3.3. +MOZ_MUST_USE bool +js::AsyncGeneratorResolve(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue value, bool done) +{ + // Step 1 (implicit). + + // Steps 2-3. + MOZ_ASSERT(!asyncGenObj->isQueueEmpty()); + + // Step 4. + Rooted<AsyncGeneratorRequest*> request( + cx, AsyncGeneratorObject::dequeueRequest(cx, asyncGenObj)); + if (!request) + return false; + + // Step 5. + RootedObject resultPromise(cx, request->promise()); + + // Step 6. + RootedObject resultObj(cx, CreateIterResultObject(cx, value, done)); + if (!resultObj) + return false; + + RootedValue resultValue(cx, ObjectValue(*resultObj)); + + // Step 7. + if (!ResolvePromiseInternal(cx, resultPromise, resultValue)) + return false; + + // Step 8. + if (!AsyncGeneratorResumeNext(cx, asyncGenObj)) + return false; + + // Step 9. + return true; +} + +// Async Iteration proposal 11.4.3.4. +MOZ_MUST_USE bool +js::AsyncGeneratorReject(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue exception) +{ + // Step 1 (implicit). + + // Steps 2-3. + MOZ_ASSERT(!asyncGenObj->isQueueEmpty()); + + // Step 4. + Rooted<AsyncGeneratorRequest*> request( + cx, AsyncGeneratorObject::dequeueRequest(cx, asyncGenObj)); + if (!request) + return false; + + // Step 5. + RootedObject resultPromise(cx, request->promise()); + + // Step 6. + if (!RejectMaybeWrappedPromise(cx, resultPromise, exception)) + return false; + + // Step 7. + if (!AsyncGeneratorResumeNext(cx, asyncGenObj)) + return false; + + // Step 8. + return true; +} + +// Async Iteration proposal 11.4.3.5. +static MOZ_MUST_USE bool +AsyncGeneratorResumeNext(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj) +{ + // Step 1 (implicit). + + // Steps 2-3. + MOZ_ASSERT(!asyncGenObj->isExecuting()); + + // Step 4. + if (asyncGenObj->isAwaitingYieldReturn() || asyncGenObj->isAwaitingReturn()) + return true; + + // Steps 5-6. + if (asyncGenObj->isQueueEmpty()) + return true; + + // Steps 7-8. + Rooted<AsyncGeneratorRequest*> request( + cx, AsyncGeneratorObject::peekRequest(cx, asyncGenObj)); + if (!request) + return false; + + // Step 9. + CompletionKind completionKind = request->completionKind(); + + // Step 10. + if (completionKind != CompletionKind::Normal) { + // Step 10.a. + if (asyncGenObj->isSuspendedStart()) + asyncGenObj->setCompleted(); + + // Step 10.b. + if (asyncGenObj->isCompleted()) { + RootedValue value(cx, request->completionValue()); + + // Step 10.b.i. + if (completionKind == CompletionKind::Return) { + // Steps 10.b.i.1. + asyncGenObj->setAwaitingReturn(); + + // Steps 10.b.i.4-6 (reordered). + RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncGeneratorResumeNextReturnFulfilled)); + RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncGeneratorResumeNextReturnRejected)); + + // Steps 10.b.i.2-3, 7-10. + auto extra = [&](Handle<PromiseReactionRecord*> reaction) { + reaction->setIsAsyncGenerator(asyncGenObj); + }; + return InternalAwait(cx, value, nullptr, onFulfilled, onRejected, extra); + } + + // Step 10.b.ii.1. + MOZ_ASSERT(completionKind == CompletionKind::Throw); + + // Steps 10.b.ii.2-3. + return AsyncGeneratorReject(cx, asyncGenObj, value); + } + } else if (asyncGenObj->isCompleted()) { + // Step 11. + return AsyncGeneratorResolve(cx, asyncGenObj, UndefinedHandleValue, true); + } + + // Step 12. + MOZ_ASSERT(asyncGenObj->isSuspendedStart() || asyncGenObj->isSuspendedYield()); + + // Step 16 (reordered). + asyncGenObj->setExecuting(); + + RootedValue argument(cx, request->completionValue()); + + if (completionKind == CompletionKind::Return) { + // 11.4.3.7 AsyncGeneratorYield step 8.b-e. + // Since we don't have the place that handles return from yield + // inside the generator, handle the case here, with extra state + // State_AwaitingYieldReturn. + asyncGenObj->setAwaitingYieldReturn(); + + RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncGeneratorYieldReturnAwaitedFulfilled)); + RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncGeneratorYieldReturnAwaitedRejected)); + + auto extra = [&](Handle<PromiseReactionRecord*> reaction) { + reaction->setIsAsyncGenerator(asyncGenObj); + }; + return InternalAwait(cx, argument, nullptr, onFulfilled, onRejected, extra); + } + + // Steps 13-15, 17-21. + return AsyncGeneratorResume(cx, asyncGenObj, completionKind, argument); +} + +// Async Iteration proposal 11.4.3.6. +MOZ_MUST_USE bool +js::AsyncGeneratorEnqueue(JSContext* cx, HandleValue asyncGenVal, + CompletionKind completionKind, HandleValue completionValue, + MutableHandleValue result) +{ + // Step 1 (implicit). + + // Step 2. + RootedObject resultPromise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); + if (!resultPromise) + return false; + + // Step 3. + if (!asyncGenVal.isObject() || !asyncGenVal.toObject().is<AsyncGeneratorObject>()) { + // Step 3.a. + RootedValue badGeneratorError(cx); + if (!GetTypeError(cx, JSMSG_NOT_AN_ASYNC_GENERATOR, &badGeneratorError)) + return false; + + // Step 3.b. + if (!RejectMaybeWrappedPromise(cx, resultPromise, badGeneratorError)) + return false; + + // Step 3.c. + result.setObject(*resultPromise); + return true; + } + + Rooted<AsyncGeneratorObject*> asyncGenObj( + cx, &asyncGenVal.toObject().as<AsyncGeneratorObject>()); + + // Step 5 (reordered). + Rooted<AsyncGeneratorRequest*> request( + cx, AsyncGeneratorRequest::create(cx, completionKind, completionValue, resultPromise)); + if (!request) + return false; + + // Steps 4, 6. + if (!AsyncGeneratorObject::enqueueRequest(cx, asyncGenObj, request)) + return false; + + // Step 7. + if (!asyncGenObj->isExecuting()) { + // Step 8. + if (!AsyncGeneratorResumeNext(cx, asyncGenObj)) + return false; + } + + // Step 9. + result.setObject(*resultPromise); + return true; +} + // ES2016, 25.4.5.3. bool js::Promise_then(JSContext* cx, unsigned argc, Value* vp) @@ -2284,12 +2775,12 @@ PerformPromiseThen(JSContext* cx, Handle<PromiseObject*> promise, HandleValue on // Step 3. RootedValue onFulfilled(cx, onFulfilled_); if (!IsCallable(onFulfilled)) - onFulfilled = Int32Value(PROMISE_HANDLER_IDENTITY); + onFulfilled = Int32Value(PromiseHandlerIdentity); // Step 4. RootedValue onRejected(cx, onRejected_); if (!IsCallable(onRejected)) - onRejected = Int32Value(PROMISE_HANDLER_THROWER); + onRejected = Int32Value(PromiseHandlerThrower); RootedObject incumbentGlobal(cx); if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal)) @@ -2638,6 +3129,9 @@ PromiseObject::resolve(JSContext* cx, Handle<PromiseObject*> promise, HandleValu if (promise->state() != JS::PromiseState::Pending) return true; + if (PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_RESOLVE_FUNCTION)) + return ResolvePromiseInternal(cx, promise, resolutionValue); + RootedObject resolveFun(cx, GetResolveFunctionFromPromise(promise)); RootedValue funVal(cx, ObjectValue(*resolveFun)); @@ -2661,6 +3155,9 @@ PromiseObject::reject(JSContext* cx, Handle<PromiseObject*> promise, HandleValue if (promise->state() != JS::PromiseState::Pending) return true; + if (PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_REJECT_FUNCTION)) + return RejectMaybeWrappedPromise(cx, promise, rejectionValue); + RootedValue funVal(cx, promise->getFixedSlot(PromiseSlot_RejectFunction)); MOZ_ASSERT(IsCallable(funVal)); @@ -2739,6 +3236,7 @@ CreatePromisePrototype(JSContext* cx, JSProtoKey key) static const JSFunctionSpec promise_methods[] = { JS_SELF_HOSTED_FN("catch", "Promise_catch", 1, 0), JS_FN("then", Promise_then, 2, 0), + JS_SELF_HOSTED_FN("finally", "Promise_finally", 1, 0), JS_FS_END }; diff --git a/js/src/builtin/Promise.h b/js/src/builtin/Promise.h index c76dc358c..b04bd0e2a 100644 --- a/js/src/builtin/Promise.h +++ b/js/src/builtin/Promise.h @@ -44,6 +44,8 @@ class PromiseObject : public NativeObject static PromiseObject* create(JSContext* cx, HandleObject executor, HandleObject proto = nullptr, bool needsWrapping = false); + static PromiseObject* createSkippingExecutor(JSContext* cx); + static JSObject* unforgeableResolve(JSContext* cx, HandleValue value); static JSObject* unforgeableReject(JSContext* cx, HandleValue value); @@ -128,6 +130,14 @@ OriginalPromiseThen(JSContext* cx, Handle<PromiseObject*> promise, HandleValue onFulfilled, HandleValue onRejected, MutableHandleObject dependent, bool createDependent); +/** + * PromiseResolve ( C, x ) + * + * The abstract operation PromiseResolve, given a constructor and a value, + * returns a new promise resolved with that value. + */ +MOZ_MUST_USE JSObject* +PromiseResolve(JSContext* cx, HandleObject constructor, HandleValue value); MOZ_MUST_USE PromiseObject* CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal); @@ -141,6 +151,26 @@ AsyncFunctionThrown(JSContext* cx, Handle<PromiseObject*> resultPromise); MOZ_MUST_USE bool AsyncFunctionAwait(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value); +class AsyncGeneratorObject; + +MOZ_MUST_USE bool +AsyncGeneratorAwait(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, HandleValue value); + +MOZ_MUST_USE bool +AsyncGeneratorResolve(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue value, bool done); + +MOZ_MUST_USE bool +AsyncGeneratorReject(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue exception); + +MOZ_MUST_USE bool +AsyncGeneratorEnqueue(JSContext* cx, HandleValue asyncGenVal, CompletionKind completionKind, + HandleValue completionValue, MutableHandleValue result); + +bool +AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind completionKind); + /** * A PromiseTask represents a task that can be dispatched to a helper thread * (via StartPromiseTask), executed (by implementing PromiseTask::execute()), diff --git a/js/src/builtin/Promise.js b/js/src/builtin/Promise.js index 704cbab0b..91a1e1f56 100644 --- a/js/src/builtin/Promise.js +++ b/js/src/builtin/Promise.js @@ -14,3 +14,72 @@ function Promise_catch(onRejected) { // Steps 1-2. return callContentFunction(this.then, this, undefined, onRejected); } + +// Promise.prototype.finally(onFinally) +// See https://tc39.es/proposal-promise-finally/ +function Promise_finally(onFinally) { + // Step 1. + var promise = this; + + // Step 2. + if (!IsObject(promise)) + ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "Promise", "finally", "value"); + + // Step 3. + var C = SpeciesConstructor(promise, GetBuiltinConstructor("Promise")); + + // Step 4. + assert(IsConstructor(C), "SpeciesConstructor returns a constructor function"); + + // Steps 5-6. + var thenFinally, catchFinally; + if (!IsCallable(onFinally)) { + thenFinally = onFinally; + catchFinally = onFinally; + } else { + // ThenFinally Function. + // The parentheses prevent the inferring of a function name. + (thenFinally) = function(value) { + // Steps 1-2 (implicit). + + // Step 3. + var result = onFinally(); + + // Steps 4-5 (implicit). + + // Step 6. + var promise = PromiseResolve(C, result); + + // Step 7. + // FIXME: spec issue - "be equivalent to a function that" is not a defined spec term. + // https://github.com/tc39/ecma262/issues/933 + + // Step 8. + return callContentFunction(promise.then, promise, function() { return value; }); + }; + + // CatchFinally Function. + // The parentheses prevent the inferring of a function name. + (catchFinally) = function(reason) { + // Steps 1-2 (implicit). + + // Step 3. + var result = onFinally(); + + // Steps 4-5 (implicit). + + // Step 6. + var promise = PromiseResolve(C, result); + + // Step 7. + // FIXME: spec issue - "be equivalent to a function that" is not a defined spec term. + // https://github.com/tc39/ecma262/issues/933 + + // Step 8. + return callContentFunction(promise.then, promise, function() { throw reason; }); + }; + } + + // Step 7. + return callContentFunction(promise.then, promise, thenFinally, catchFinally); +} diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index 8e8bb2417..22c958d4c 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -2800,11 +2800,11 @@ ASTSerializer::generatorExpression(ParseNode* pn, MutableHandleValue dst) LOCAL_ASSERT(next->isKind(PNK_SEMI) && next->pn_kid->isKind(PNK_YIELD) && - next->pn_kid->pn_left); + next->pn_kid->pn_kid); RootedValue body(cx); - return expression(next->pn_kid->pn_left, &body) && + return expression(next->pn_kid->pn_kid, &body) && builder.generatorExpression(body, blocks, filter, isLegacy, &pn->pn_pos, dst); } @@ -3146,7 +3146,7 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) case PNK_YIELD_STAR: { - MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); + MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos)); RootedValue arg(cx); return expression(pn->pn_left, &arg) && @@ -3155,10 +3155,10 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) case PNK_YIELD: { - MOZ_ASSERT_IF(pn->pn_left, pn->pn_pos.encloses(pn->pn_left->pn_pos)); + MOZ_ASSERT_IF(pn->pn_kid, pn->pn_pos.encloses(pn->pn_kid->pn_pos)); RootedValue arg(cx); - return optExpression(pn->pn_left, &arg) && + return optExpression(pn->pn_kid, &arg) && builder.yieldExpression(arg, NotDelegating, &pn->pn_pos, dst); } @@ -3422,10 +3422,10 @@ ASTSerializer::function(ParseNode* pn, ASTType type, MutableHandleValue dst) RootedFunction func(cx, pn->pn_funbox->function()); GeneratorStyle generatorStyle = - pn->pn_funbox->isGenerator() - ? (pn->pn_funbox->isLegacyGenerator() - ? GeneratorStyle::Legacy - : GeneratorStyle::ES6) + pn->pn_funbox->isStarGenerator() + ? GeneratorStyle::ES6 + : pn->pn_funbox->isLegacyGenerator() + ? GeneratorStyle::Legacy : GeneratorStyle::None; bool isAsync = pn->pn_funbox->isAsync(); @@ -3480,7 +3480,7 @@ ASTSerializer::functionArgsAndBody(ParseNode* pn, NodeVector& args, NodeVector& ParseNode* pnstart = pnbody->pn_head; // Skip over initial yield in generator. - if (pnstart && pnstart->isKind(PNK_YIELD)) { + if (pnstart && pnstart->isKind(PNK_INITIALYIELD)) { MOZ_ASSERT(pnstart->getOp() == JSOP_INITIALYIELD); pnstart = pnstart->pn_next; } diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp index 7cf20d23c..55e0c8578 100644 --- a/js/src/builtin/RegExp.cpp +++ b/js/src/builtin/RegExp.cpp @@ -178,7 +178,7 @@ CheckPatternSyntax(JSContext* cx, HandleAtom pattern, RegExpFlag flags) CompileOptions options(cx); frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr); return irregexp::ParsePatternSyntax(dummyTokenStream, cx->tempLifoAlloc(), pattern, - flags & UnicodeFlag); + flags & UnicodeFlag, flags & DotAllFlag); } enum RegExpSharedUse { @@ -664,6 +664,29 @@ js::regexp_multiline(JSContext* cx, unsigned argc, JS::Value* vp) return CallNonGenericMethod<IsRegExpInstanceOrPrototype, regexp_multiline_impl>(cx, args); } +// ES 2018 dotAll +MOZ_ALWAYS_INLINE bool +regexp_dotall_impl(JSContext* cx, const CallArgs& args) +{ + MOZ_ASSERT(IsRegExpInstanceOrPrototype(args.thisv())); + + if (!IsRegExpObject(args.thisv())) { + args.rval().setUndefined(); + return true; + } + + Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>()); + args.rval().setBoolean(reObj->dotall()); + return true; +} + +bool +js::regexp_dotall(JSContext* cx, unsigned argc, JS::Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return CallNonGenericMethod<IsRegExpInstanceOrPrototype, regexp_dotall_impl>(cx, args); +} + // ES 2017 draft rev32 21.2.5.10. MOZ_ALWAYS_INLINE bool regexp_source_impl(JSContext* cx, const CallArgs& args) @@ -759,6 +782,7 @@ const JSPropertySpec js::regexp_properties[] = { JS_PSG("source", regexp_source, 0), JS_PSG("sticky", regexp_sticky, 0), JS_PSG("unicode", regexp_unicode, 0), + JS_PSG("dotall", regexp_dotall, 0), JS_PS_END }; @@ -771,6 +795,7 @@ const JSFunctionSpec js::regexp_methods[] = { JS_SELF_HOSTED_FN("exec", "RegExp_prototype_Exec", 1,0), JS_SELF_HOSTED_FN("test", "RegExpTest" , 1,0), JS_SELF_HOSTED_SYM_FN(match, "RegExpMatch", 1,0), + JS_SELF_HOSTED_SYM_FN(matchAll, "RegExpMatchAll", 1, 0), JS_SELF_HOSTED_SYM_FN(replace, "RegExpReplace", 2,0), JS_SELF_HOSTED_SYM_FN(search, "RegExpSearch", 1,0), JS_SELF_HOSTED_SYM_FN(split, "RegExpSplit", 2,0), @@ -1642,6 +1667,13 @@ js::RegExpPrototypeOptimizableRaw(JSContext* cx, JSObject* proto) if (unicodeGetter != regexp_unicode) return false; + JSNative dotAllGetter; + if (!GetOwnNativeGetterPure(cx, proto, NameToId(cx->names().dotall), &dotAllGetter)) + return false; + + if (dotAllGetter != regexp_dotall) + return false; + // Check if @@match, @@search, and exec are own data properties, // those values should be tested in selfhosted JS. bool has = false; diff --git a/js/src/builtin/RegExp.h b/js/src/builtin/RegExp.h index 4e0ff6948..f808f5146 100644 --- a/js/src/builtin/RegExp.h +++ b/js/src/builtin/RegExp.h @@ -153,6 +153,8 @@ extern MOZ_MUST_USE bool regexp_sticky(JSContext* cx, unsigned argc, JS::Value* vp); extern MOZ_MUST_USE bool regexp_unicode(JSContext* cx, unsigned argc, JS::Value* vp); +extern MOZ_MUST_USE bool +regexp_dotall(JSContext* cx, unsigned argc, JS::Value* vp); } /* namespace js */ diff --git a/js/src/builtin/RegExp.js b/js/src/builtin/RegExp.js index 0b849292c..91212066c 100644 --- a/js/src/builtin/RegExp.js +++ b/js/src/builtin/RegExp.js @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // ES6 draft rev34 (2015/02/20) 21.2.5.3 get RegExp.prototype.flags +// Updated for ES2018 /s (dotAll) function RegExpFlagsGetter() { // Steps 1-2. var R = this; @@ -31,6 +32,10 @@ function RegExpFlagsGetter() { // Steps 16-18. if (R.sticky) result += "y"; + + // ES2018 + if (R.dotall) + result += "s"; // Step 19. return result; @@ -1026,3 +1031,138 @@ function RegExpSpecies() { return this; } _SetCanonicalName(RegExpSpecies, "get [Symbol.species]"); + +// String.prototype.matchAll proposal. +// +// RegExp.prototype [ @@matchAll ] ( string ) +function RegExpMatchAll(string) { + // Step 1. + var rx = this; + + // Step 2. + if (!IsObject(rx)) + ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, rx === null ? "null" : typeof rx); + + if (rx.flags === undefined || rx.flags === null) + ThrowTypeError(JSMSG_FLAGS_UNDEFINED_OR_NULL); + + // Step 3. + var str = ToString(string); + + // Step 4. + var C = SpeciesConstructor(rx, GetBuiltinConstructor("RegExp")); + + // Step 5. + var flags = ToString(rx.flags); + + // Step 2.b.iii; located here because it needs to check |flags|. + if (!callFunction(std_String_includes, flags, "g")) { + ThrowTypeError(JSMSG_REQUIRES_GLOBAL_REGEXP, "matchAll"); + } + + // Step 6. + var matcher = new C(rx, flags); + + // Steps 7-8. + matcher.lastIndex = ToLength(rx.lastIndex); + + // Steps 9-12. + // Note, always global because non-global throws as per + // https://github.com/tc39/ecma262/pull/1716 + var flags = REGEXP_GLOBAL_FLAG | + (callFunction(std_String_includes, flags, "u") ? REGEXP_UNICODE_FLAG : 0); + + // Step 13. + return CreateRegExpStringIterator(matcher, str, flags); +} + +// String.prototype.matchAll proposal. +// +// CreateRegExpStringIterator ( R, S, global, fullUnicode ) +function CreateRegExpStringIterator(regexp, string, flags) { + // Step 1. + assert(typeof string === "string", "|string| is a string value"); + + // Steps 2-3. + assert(typeof flags === "number", "|flags| is a number value"); + + // Steps 4-9. + var iterator = NewRegExpStringIterator(); + UnsafeSetReservedSlot(iterator, REGEXP_STRING_ITERATOR_REGEXP_SLOT, regexp); + UnsafeSetReservedSlot(iterator, REGEXP_STRING_ITERATOR_STRING_SLOT, string); + UnsafeSetReservedSlot(iterator, REGEXP_STRING_ITERATOR_FLAGS_SLOT, flags | 0); + UnsafeSetReservedSlot(iterator, REGEXP_STRING_ITERATOR_DONE_SLOT, false); + + // Step 10. + return iterator; +} + +// String.prototype.matchAll proposal. +// +// %RegExpStringIteratorPrototype%.next ( ) +function RegExpStringIteratorNext() { + // Steps 1-3. + var obj; + if (!IsObject(this) || (obj = GuardToRegExpStringIterator(this)) === null) { + return callFunction(CallRegExpStringIteratorMethodIfWrapped, this, + "RegExpStringIteratorNext"); + } + + var result = { value: undefined, done: false }; + + // Step 4. + var done = UnsafeGetReservedSlot(obj, REGEXP_STRING_ITERATOR_DONE_SLOT); + if (done) { + result.done = true; + return result; + } + + // Step 5. + var regexp = UnsafeGetObjectFromReservedSlot(obj, REGEXP_STRING_ITERATOR_REGEXP_SLOT); + + // Step 6. + var string = UnsafeGetStringFromReservedSlot(obj, REGEXP_STRING_ITERATOR_STRING_SLOT); + + // Steps 7-8. + var flags = UnsafeGetInt32FromReservedSlot(obj, REGEXP_STRING_ITERATOR_FLAGS_SLOT); + var global = !!(flags & REGEXP_GLOBAL_FLAG); + var fullUnicode = !!(flags & REGEXP_UNICODE_FLAG); + + // Step 9. + var match = RegExpExec(regexp, string, false); + + // Step 10. + if (match === null) { + // Step 10.a. + UnsafeSetReservedSlot(obj, REGEXP_STRING_ITERATOR_DONE_SLOT, true); + + // Step 10.b. + result.done = true; + return result; + } + + // Step 11.a. + if (global) { + // Step 11.a.i. + var matchStr = ToString(match[0]); + + // Step 11.a.ii. + if (matchStr.length === 0) { + // Step 11.a.ii.1. + var thisIndex = ToLength(regexp.lastIndex); + + // Step 11.a.ii.2. + var nextIndex = fullUnicode ? AdvanceStringIndex(string, thisIndex) : thisIndex + 1; + + // Step 11.a.ii.3. + regexp.lastIndex = nextIndex; + } + } else { + // Step 11.b.i. + UnsafeSetReservedSlot(obj, REGEXP_STRING_ITERATOR_DONE_SLOT, true); + } + + // Steps 11.a.iii and 11.b.ii. + result.value = match; + return result; +} diff --git a/js/src/builtin/SelfHostingDefines.h b/js/src/builtin/SelfHostingDefines.h index b57c17269..117ac7ffd 100644 --- a/js/src/builtin/SelfHostingDefines.h +++ b/js/src/builtin/SelfHostingDefines.h @@ -71,8 +71,6 @@ // Used for list, i.e. Array and String, iterators. #define ITERATOR_SLOT_NEXT_INDEX 1 #define ITERATOR_SLOT_ITEM_KIND 2 -// Used for ListIterator. -#define ITERATOR_SLOT_NEXT_METHOD 2 #define ITEM_KIND_KEY 0 #define ITEM_KIND_VALUE 1 @@ -92,6 +90,12 @@ #define REGEXP_MULTILINE_FLAG 0x04 #define REGEXP_STICKY_FLAG 0x08 #define REGEXP_UNICODE_FLAG 0x10 +#define REGEXP_DOTALL_FLAG 0x20 + +#define REGEXP_STRING_ITERATOR_REGEXP_SLOT 0 +#define REGEXP_STRING_ITERATOR_STRING_SLOT 1 +#define REGEXP_STRING_ITERATOR_FLAGS_SLOT 2 +#define REGEXP_STRING_ITERATOR_DONE_SLOT 3 #define MODULE_OBJECT_ENVIRONMENT_SLOT 2 diff --git a/js/src/builtin/String.js b/js/src/builtin/String.js index f830b1aa2..d07ec6127 100644 --- a/js/src/builtin/String.js +++ b/js/src/builtin/String.js @@ -63,6 +63,33 @@ function String_generic_match(thisValue, regexp) { return callFunction(String_match, thisValue, regexp); } +// String.prototype.matchAll proposal. +// +// String.prototype.matchAll ( regexp ) +function String_matchAll(regexp) { + // Step 1. + RequireObjectCoercible(this); + + // Step 2. + if (regexp !== undefined && regexp !== null) { + // Step 2.a. + var matcher = GetMethod(regexp, std_matchAll); + + // Step 2.b. + if (matcher !== undefined) + return callContentFunction(matcher, regexp, this); + } + + // Step 3. + var string = ToString(this); + + // Step 4. + var rx = RegExpCreate(regexp, "g"); + + // Step 5. + return callContentFunction(GetMethod(rx, std_matchAll), rx, string); +} + /** * A helper function implementing the logic for both String.prototype.padStart * and String.prototype.padEnd as described in ES7 Draft March 29, 2016 diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 992fe2c97..a9a307da7 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -239,8 +239,11 @@ GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp) value = BooleanValue(true); if (!JS_SetProperty(cx, info, "intl-api", value)) return false; - +#ifdef XP_SOLARIS + value = BooleanValue(false); +#else value = BooleanValue(true); +#endif if (!JS_SetProperty(cx, info, "mapped-array-buffer", value)) return false; @@ -1325,7 +1328,6 @@ OOMTest(JSContext* cx, unsigned argc, Value* vp) } #endif -#ifdef SPIDERMONKEY_PROMISE static bool SettlePromiseNow(JSContext* cx, unsigned argc, Value* vp) { @@ -1434,43 +1436,6 @@ RejectPromise(JSContext* cx, unsigned argc, Value* vp) return result; } -#else - -static const js::Class FakePromiseClass = { - "Promise", JSCLASS_IS_ANONYMOUS -}; - -static bool -MakeFakePromise(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - RootedObject obj(cx, NewObjectWithGivenProto(cx, &FakePromiseClass, nullptr)); - if (!obj) - return false; - - JS::dbg::onNewPromise(cx, obj); - args.rval().setObject(*obj); - return true; -} - -static bool -SettleFakePromise(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - if (!args.requireAtLeast(cx, "settleFakePromise", 1)) - return false; - if (!args[0].isObject() || args[0].toObject().getClass() != &FakePromiseClass) { - JS_ReportErrorASCII(cx, "first argument must be a (fake) Promise object"); - return false; - } - - RootedObject promise(cx, &args[0].toObject()); - JS::dbg::onPromiseSettled(cx, promise); - return true; -} -#endif // SPIDERMONKEY_PROMISE - static unsigned finalizeCount = 0; static void @@ -3897,10 +3862,10 @@ ConvertRegExpTreeToObject(JSContext* cx, irregexp::RegExpTree* tree) return nullptr; return obj; } - if (tree->IsLookahead()) { - if (!StringProp(cx, obj, "type", "Lookahead")) + if (tree->IsLookaround()) { + if (!StringProp(cx, obj, "type", "Lookaround")) return nullptr; - irregexp::RegExpLookahead* t = tree->AsLookahead(); + irregexp::RegExpLookaround* t = tree->AsLookaround(); if (!BooleanProp(cx, obj, "is_positive", t->is_positive())) return nullptr; if (!TreeProp(cx, obj, "body", t->body())) @@ -3972,6 +3937,7 @@ ParseRegExp(JSContext* cx, unsigned argc, Value* vp) flags & MultilineFlag, match_only, flags & UnicodeFlag, flags & IgnoreCaseFlag, flags & GlobalFlag, flags & StickyFlag, + flags & DotAllFlag, &data)) { return false; @@ -4186,7 +4152,6 @@ static const JSFunctionSpecWithHelp TestingFunctions[] = { " This is also disabled when --fuzzing-safe is specified."), #endif -#ifdef SPIDERMONKEY_PROMISE JS_FN_HELP("settlePromiseNow", SettlePromiseNow, 1, 0, "settlePromiseNow(promise)", " 'Settle' a 'promise' immediately. This just marks the promise as resolved\n" @@ -4203,20 +4168,6 @@ JS_FN_HELP("resolvePromise", ResolvePromise, 2, 0, JS_FN_HELP("rejectPromise", RejectPromise, 2, 0, "rejectPromise(promise, reason)", " Reject a Promise by calling the JSAPI function JS::RejectPromise."), -#else - JS_FN_HELP("makeFakePromise", MakeFakePromise, 0, 0, -"makeFakePromise()", -" Create an object whose [[Class]] name is 'Promise' and call\n" -" JS::dbg::onNewPromise on it before returning it. It doesn't actually have\n" -" any of the other behavior associated with promises."), - - JS_FN_HELP("settleFakePromise", SettleFakePromise, 1, 0, -"settleFakePromise(promise)", -" 'Settle' a 'promise' created by makeFakePromise(). This doesn't have any\n" -" observable effects outside of firing any onPromiseSettled hooks set on\n" -" Debugger instances that are observing the given promise's global as a\n" -" debuggee."), -#endif // SPIDERMONKEY_PROMISE JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0, "makeFinalizeObserver()", diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index ff3680774..50bf0b836 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -2215,7 +2215,6 @@ const ObjectOps TypedObject::objectOps_ = { TypedObject::obj_setProperty, TypedObject::obj_getOwnPropertyDescriptor, TypedObject::obj_deleteProperty, - nullptr, nullptr, /* watch/unwatch */ nullptr, /* getElements */ TypedObject::obj_enumerate, nullptr, /* thisValue */ diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index d6adfac2c..aed1114bd 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -20,6 +20,10 @@ #include <float.h> #endif +#if defined(XP_SOLARIS) +#include <ieeefp.h> +#endif + #ifdef HAVE_SSIZE_T #include <sys/types.h> #endif diff --git a/js/src/ctypes/libffi/src/x86/win32.S b/js/src/ctypes/libffi/src/x86/win32.S index daf0e799c..4f702e8b1 100644 --- a/js/src/ctypes/libffi/src/x86/win32.S +++ b/js/src/ctypes/libffi/src/x86/win32.S @@ -1158,8 +1158,24 @@ L_ffi_closure_SYSV_inner$stub: .byte 0x7c /* .sleb128 -4; CIE Data Alignment Factor */ .byte 0x8 /* CIE RA Column */ #ifdef __PIC__ - .byte 0x1 /* .uleb128 0x1; Augmentation size */ - .byte 0x1b /* FDE Encoding (pcrel sdata4) */ +# if defined __sun__ && defined __svr4__ +/* 32-bit Solaris 2/x86 uses datarel encoding for PIC. GNU ld before 2.22 + doesn't correctly sort .eh_frame_hdr with mixed encodings, so match this. */ +# define FDE_ENCODING 0x30 /* datarel */ +# define FDE_ENCODE(X) X@GOTOFF +# else +# define FDE_ENCODING 0x1b /* pcrel sdata4 */ +# if defined HAVE_AS_X86_PCREL +# define FDE_ENCODE(X) X-. +# else +# define FDE_ENCODE(X) X@rel +# endif +# endif +#else +# define FDE_ENCODING 0 /* absolute */ +# define FDE_ENCODE(X) X +.byte 0x1 /* .uleb128 0x1; Augmentation size */ +.byte FDE_ENCODING #endif .byte 0xc /* DW_CFA_def_cfa CFA = r4 + 4 = 4(%esp) */ .byte 0x4 /* .uleb128 0x4 */ @@ -1176,7 +1192,7 @@ L_ffi_closure_SYSV_inner$stub: #if defined __PIC__ && defined HAVE_AS_X86_PCREL .long .LFB1-. /* FDE initial location */ #else - .long .LFB1 + .long FDE_ENCODE(.LFB1) #endif .long .LFE1-.LFB1 /* FDE address range */ #ifdef __PIC__ @@ -1207,7 +1223,7 @@ L_ffi_closure_SYSV_inner$stub: #if defined __PIC__ && defined HAVE_AS_X86_PCREL .long .LFB3-. /* FDE initial location */ #else - .long .LFB3 + .long FDE_ENCODE(.LFB3) #endif .long .LFE3-.LFB3 /* FDE address range */ #ifdef __PIC__ @@ -1240,7 +1256,7 @@ L_ffi_closure_SYSV_inner$stub: #if defined __PIC__ && defined HAVE_AS_X86_PCREL .long .LFB4-. /* FDE initial location */ #else - .long .LFB4 + .long FDE_ENCODE(.LFB4) #endif .long .LFE4-.LFB4 /* FDE address range */ #ifdef __PIC__ @@ -1278,7 +1294,7 @@ L_ffi_closure_SYSV_inner$stub: #if defined __PIC__ && defined HAVE_AS_X86_PCREL .long .LFB5-. /* FDE initial location */ #else - .long .LFB5 + .long FDE_ENCODE(.LFB5) #endif .long .LFE5-.LFB5 /* FDE address range */ #ifdef __PIC__ diff --git a/js/src/doc/Debugger/Conventions.md b/js/src/doc/Debugger/Conventions.md index e8bd3ee43..5de7bc171 100644 --- a/js/src/doc/Debugger/Conventions.md +++ b/js/src/doc/Debugger/Conventions.md @@ -110,8 +110,8 @@ resumption value has one of the following forms: the `new` expression returns the frame's `this` value. Similarly, if the function is the constructor for a subclass, then a non-object value may result in a TypeError. - If the frame is a generator or async function, then <i>value</i> must - conform to the iterator protocol: it must be a non-proxy object of the form + If the frame is a generator function, then <i>value</i> must conform to the + iterator protocol: it must be a non-proxy object of the form <code>{ done: <i>boolean</i>, value: <i>v</i> }</code>, where both `done` and `value` are ordinary properties. diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index a1abbfeda..ccfe3cd2e 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -727,5 +727,18 @@ frontend::CompileStandaloneAsyncFunction(JSContext* cx, MutableHandleFunction fu BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope, TraceLogger_ParserCompileFunction); + return compiler.compileStandaloneFunction(fun, NotGenerator, AsyncFunction, parameterListEnd); +} + +bool +frontend::CompileStandaloneAsyncGenerator(JSContext* cx, MutableHandleFunction fun, + const ReadOnlyCompileOptions& options, + JS::SourceBufferHolder& srcBuf, + Maybe<uint32_t> parameterListEnd) +{ + RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope()); + + BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope, + TraceLogger_ParserCompileFunction); return compiler.compileStandaloneFunction(fun, StarGenerator, AsyncFunction, parameterListEnd); } diff --git a/js/src/frontend/BytecodeCompiler.h b/js/src/frontend/BytecodeCompiler.h index 0bc1ab2ab..029fbe632 100644 --- a/js/src/frontend/BytecodeCompiler.h +++ b/js/src/frontend/BytecodeCompiler.h @@ -85,6 +85,12 @@ CompileStandaloneAsyncFunction(JSContext* cx, MutableHandleFunction fun, mozilla::Maybe<uint32_t> parameterListEnd); MOZ_MUST_USE bool +CompileStandaloneAsyncGenerator(JSContext* cx, MutableHandleFunction fun, + const ReadOnlyCompileOptions& options, + JS::SourceBufferHolder& srcBuf, + mozilla::Maybe<uint32_t> parameterListEnd); + +MOZ_MUST_USE bool CompileAsyncFunctionBody(JSContext* cx, MutableHandleFunction fun, const ReadOnlyCompileOptions& options, Handle<PropertyNameVector> formals, JS::SourceBufferHolder& srcBuf); diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 309d6c290..f4574b248 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -100,7 +100,7 @@ class BytecodeEmitter::NestableControl : public Nestable<BytecodeEmitter::Nestab NestableControl(BytecodeEmitter* bce, StatementKind kind) : Nestable<NestableControl>(&bce->innermostNestableControl), kind_(kind), - emitterScope_(bce->innermostEmitterScope) + emitterScope_(bce->innermostEmitterScopeNoCheck()) { } public: @@ -389,7 +389,10 @@ class BytecodeEmitter::EmitterScope : public Nestable<BytecodeEmitter::EmitterSc nextFrameSlot_ = bi.nextFrameSlot(); if (nextFrameSlot_ > bce->maxFixedSlots) bce->maxFixedSlots = nextFrameSlot_; - MOZ_ASSERT_IF(bce->sc->isFunctionBox() && bce->sc->asFunctionBox()->isGenerator(), + MOZ_ASSERT_IF(bce->sc->isFunctionBox() && + (bce->sc->asFunctionBox()->isStarGenerator() || + bce->sc->asFunctionBox()->isLegacyGenerator() || + bce->sc->asFunctionBox()->isAsync()), bce->maxFixedSlots == 0); } @@ -423,7 +426,7 @@ class BytecodeEmitter::EmitterScope : public Nestable<BytecodeEmitter::EmitterSc // enclosing BCE. if ((*bce)->parent) { *bce = (*bce)->parent; - return (*bce)->innermostEmitterScope; + return (*bce)->innermostEmitterScopeNoCheck(); } return nullptr; @@ -457,7 +460,7 @@ class BytecodeEmitter::EmitterScope : public Nestable<BytecodeEmitter::EmitterSc public: explicit EmitterScope(BytecodeEmitter* bce) - : Nestable<EmitterScope>(&bce->innermostEmitterScope), + : Nestable<EmitterScope>(&bce->innermostEmitterScope_), nameCache_(bce->cx->frontendCollectionPool()), hasEnvironment_(false), environmentChainLength_(0), @@ -863,7 +866,7 @@ BytecodeEmitter::EmitterScope::enterLexical(BytecodeEmitter* bce, ScopeKind kind Handle<LexicalScope::Data*> bindings) { MOZ_ASSERT(kind != ScopeKind::NamedLambda && kind != ScopeKind::StrictNamedLambda); - MOZ_ASSERT(this == bce->innermostEmitterScope); + MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck()); if (!ensureCache(bce)) return false; @@ -932,7 +935,7 @@ BytecodeEmitter::EmitterScope::enterLexical(BytecodeEmitter* bce, ScopeKind kind bool BytecodeEmitter::EmitterScope::enterNamedLambda(BytecodeEmitter* bce, FunctionBox* funbox) { - MOZ_ASSERT(this == bce->innermostEmitterScope); + MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck()); MOZ_ASSERT(funbox->namedLambdaBindings()); if (!ensureCache(bce)) @@ -999,7 +1002,7 @@ BytecodeEmitter::EmitterScope::enterComprehensionFor(BytecodeEmitter* bce, bool BytecodeEmitter::EmitterScope::enterParameterExpressionVar(BytecodeEmitter* bce) { - MOZ_ASSERT(this == bce->innermostEmitterScope); + MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck()); if (!ensureCache(bce)) return false; @@ -1032,7 +1035,7 @@ BytecodeEmitter::EmitterScope::enterParameterExpressionVar(BytecodeEmitter* bce) bool BytecodeEmitter::EmitterScope::enterFunction(BytecodeEmitter* bce, FunctionBox* funbox) { - MOZ_ASSERT(this == bce->innermostEmitterScope); + MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck()); // If there are parameter expressions, there is an extra var scope. if (!funbox->hasExtraBodyVarScope()) @@ -1123,7 +1126,7 @@ BytecodeEmitter::EmitterScope::enterFunctionExtraBodyVar(BytecodeEmitter* bce, F MOZ_ASSERT(funbox->hasParameterExprs); MOZ_ASSERT(funbox->extraVarScopeBindings() || funbox->needsExtraBodyVarEnvironmentRegardlessOfBindings()); - MOZ_ASSERT(this == bce->innermostEmitterScope); + MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck()); // The extra var scope is never popped once it's entered. It replaces the // function scope as the var emitter scope. @@ -1209,7 +1212,7 @@ class DynamicBindingIter : public BindingIter bool BytecodeEmitter::EmitterScope::enterGlobal(BytecodeEmitter* bce, GlobalSharedContext* globalsc) { - MOZ_ASSERT(this == bce->innermostEmitterScope); + MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck()); bce->setVarEmitterScope(this); @@ -1269,7 +1272,7 @@ BytecodeEmitter::EmitterScope::enterGlobal(BytecodeEmitter* bce, GlobalSharedCon bool BytecodeEmitter::EmitterScope::enterEval(BytecodeEmitter* bce, EvalSharedContext* evalsc) { - MOZ_ASSERT(this == bce->innermostEmitterScope); + MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck()); bce->setVarEmitterScope(this); @@ -1324,7 +1327,7 @@ BytecodeEmitter::EmitterScope::enterEval(BytecodeEmitter* bce, EvalSharedContext bool BytecodeEmitter::EmitterScope::enterModule(BytecodeEmitter* bce, ModuleSharedContext* modulesc) { - MOZ_ASSERT(this == bce->innermostEmitterScope); + MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck()); bce->setVarEmitterScope(this); @@ -1381,7 +1384,7 @@ BytecodeEmitter::EmitterScope::enterModule(BytecodeEmitter* bce, ModuleSharedCon bool BytecodeEmitter::EmitterScope::enterWith(BytecodeEmitter* bce) { - MOZ_ASSERT(this == bce->innermostEmitterScope); + MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck()); if (!ensureCache(bce)) return false; @@ -1409,7 +1412,7 @@ BytecodeEmitter::EmitterScope::leave(BytecodeEmitter* bce, bool nonLocal) { // If we aren't leaving the scope due to a non-local jump (e.g., break), // we must be the innermost scope. - MOZ_ASSERT_IF(!nonLocal, this == bce->innermostEmitterScope); + MOZ_ASSERT_IF(!nonLocal, this == bce->innermostEmitterScopeNoCheck()); ScopeKind kind = scope(bce)->kind(); switch (kind) { @@ -1985,6 +1988,8 @@ class MOZ_STACK_CLASS IfThenElseEmitter class ForOfLoopControl : public LoopControl { + using EmitterScope = BytecodeEmitter::EmitterScope; + // The stack depth of the iterator. int32_t iterDepth_; @@ -2026,12 +2031,16 @@ class ForOfLoopControl : public LoopControl bool allowSelfHosted_; + IteratorKind iterKind_; + public: - ForOfLoopControl(BytecodeEmitter* bce, int32_t iterDepth, bool allowSelfHosted) + ForOfLoopControl(BytecodeEmitter* bce, int32_t iterDepth, bool allowSelfHosted, + IteratorKind iterKind) : LoopControl(bce, StatementKind::ForOfLoop), iterDepth_(iterDepth), numYieldsAtBeginCodeNeedingIterClose_(UINT32_MAX), - allowSelfHosted_(allowSelfHosted) + allowSelfHosted_(allowSelfHosted), + iterKind_(iterKind) { } @@ -2043,7 +2052,7 @@ class ForOfLoopControl : public LoopControl return false; MOZ_ASSERT(numYieldsAtBeginCodeNeedingIterClose_ == UINT32_MAX); - numYieldsAtBeginCodeNeedingIterClose_ = bce->yieldOffsetList.numYields; + numYieldsAtBeginCodeNeedingIterClose_ = bce->yieldAndAwaitOffsetList.numYields; return true; } @@ -2073,8 +2082,8 @@ class ForOfLoopControl : public LoopControl MOZ_ASSERT(slotFromTop == unsigned(bce->stackDepth - iterDepth_)); if (!bce->emitDupAt(slotFromTop)) // ITER ... EXCEPTION ITER return false; - if (!emitIteratorClose(bce, CompletionKind::Throw)) // ITER ... EXCEPTION - return false; + if (!emitIteratorCloseInInnermostScope(bce, CompletionKind::Throw)) + return false; // ITER ... EXCEPTION if (!ifIteratorIsNotClosed.emitEnd()) // ITER ... EXCEPTION return false; @@ -2085,7 +2094,7 @@ class ForOfLoopControl : public LoopControl // If any yields were emitted, then this for-of loop is inside a star // generator and must handle the case of Generator.return. Like in // yield*, it is handled with a finally block. - uint32_t numYieldsEmitted = bce->yieldOffsetList.numYields; + uint32_t numYieldsEmitted = bce->yieldAndAwaitOffsetList.numYields; if (numYieldsEmitted > numYieldsAtBeginCodeNeedingIterClose_) { if (!tryCatch_->emitFinally()) return false; @@ -2097,8 +2106,8 @@ class ForOfLoopControl : public LoopControl return false; if (!bce->emitDupAt(slotFromTop + 1)) // ITER ... FTYPE FVALUE ITER return false; - if (!emitIteratorClose(bce, CompletionKind::Normal)) // ITER ... FTYPE FVALUE - return false; + if (!emitIteratorCloseInInnermostScope(bce, CompletionKind::Normal)) + return false; // ITER ... FTYPE FVALUE if (!ifGeneratorClosing.emitEnd()) // ITER ... FTYPE FVALUE return false; } @@ -2112,16 +2121,27 @@ class ForOfLoopControl : public LoopControl return true; } - bool emitIteratorClose(BytecodeEmitter* bce, - CompletionKind completionKind = CompletionKind::Normal) { + bool emitIteratorCloseInInnermostScope(BytecodeEmitter* bce, + CompletionKind completionKind = CompletionKind::Normal) { + return emitIteratorCloseInScope(bce, *bce->innermostEmitterScope(), completionKind); + } + + bool emitIteratorCloseInScope(BytecodeEmitter* bce, + EmitterScope& currentScope, + CompletionKind completionKind = CompletionKind::Normal) { ptrdiff_t start = bce->offset(); - if (!bce->emitIteratorClose(completionKind, allowSelfHosted_)) + if (!bce->emitIteratorCloseInScope(currentScope, iterKind_, completionKind, + allowSelfHosted_)) + { return false; + } ptrdiff_t end = bce->offset(); return bce->tryNoteList.append(JSTRY_FOR_OF_ITERCLOSE, 0, start, end); } - bool emitPrepareForNonLocalJump(BytecodeEmitter* bce, bool isTarget) { + bool emitPrepareForNonLocalJumpFromScope(BytecodeEmitter* bce, + EmitterScope& currentScope, + bool isTarget) { // Pop unnecessary values from the stack. Effectively this means // leaving try-catch block. However, the performing IteratorClose can // reach the depth for try-catch, and effectively re-enter the @@ -2138,7 +2158,7 @@ class ForOfLoopControl : public LoopControl if (!bce->emit1(JSOP_SWAP)) // UNDEF ITER return false; - if (!emitIteratorClose(bce)) // UNDEF + if (!emitIteratorCloseInScope(bce, currentScope, CompletionKind::Normal)) // UNDEF return false; if (isTarget) { @@ -2181,13 +2201,16 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent, bodyScopeIndex(UINT32_MAX), varEmitterScope(nullptr), innermostNestableControl(nullptr), - innermostEmitterScope(nullptr), + innermostEmitterScope_(nullptr), innermostTDZCheckCache(nullptr), +#ifdef DEBUG + unstableEmitterScope(false), +#endif constList(cx), scopeList(cx), tryNoteList(cx), scopeNoteList(cx), - yieldOffsetList(cx), + yieldAndAwaitOffsetList(cx), typesetCount(0), hasSingletons(false), hasTryFinally(false), @@ -2239,13 +2262,13 @@ BytecodeEmitter::findInnermostNestableControl(Predicate predicate) const NameLocation BytecodeEmitter::lookupName(JSAtom* name) { - return innermostEmitterScope->lookup(this, name); + return innermostEmitterScope()->lookup(this, name); } Maybe<NameLocation> BytecodeEmitter::locationOfNameBoundInScope(JSAtom* name, EmitterScope* target) { - return innermostEmitterScope->locationBoundInScope(this, name, target); + return innermostEmitterScope()->locationBoundInScope(this, name, target); } Maybe<NameLocation> @@ -2706,7 +2729,7 @@ class NonLocalExitControl : bce_(bce), savedScopeNoteIndex_(bce->scopeNoteList.length()), savedDepth_(bce->stackDepth), - openScopeNoteIndex_(bce->innermostEmitterScope->noteIndex()), + openScopeNoteIndex_(bce->innermostEmitterScope()->noteIndex()), kind_(kind) { } @@ -2752,9 +2775,11 @@ NonLocalExitControl::prepareForNonLocalJump(BytecodeEmitter::NestableControl* ta using NestableControl = BytecodeEmitter::NestableControl; using EmitterScope = BytecodeEmitter::EmitterScope; - EmitterScope* es = bce_->innermostEmitterScope; + EmitterScope* es = bce_->innermostEmitterScope(); int npops = 0; + AutoCheckUnstableEmitterScope cues(bce_); + // For 'continue', 'break', and 'return' statements, emit IteratorClose // bytecode inline. 'continue' statements do not call IteratorClose for // the loop they are continuing. @@ -2805,8 +2830,11 @@ NonLocalExitControl::prepareForNonLocalJump(BytecodeEmitter::NestableControl* ta return false; ForOfLoopControl& loopinfo = control->as<ForOfLoopControl>(); - if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ false)) // ... + if (!loopinfo.emitPrepareForNonLocalJumpFromScope(bce_, *es, + /* isTarget = */ false)) + { // ... return false; + } } else { npops += 3; } @@ -2833,8 +2861,11 @@ NonLocalExitControl::prepareForNonLocalJump(BytecodeEmitter::NestableControl* ta if (target && emitIteratorCloseAtTarget && target->is<ForOfLoopControl>()) { ForOfLoopControl& loopinfo = target->as<ForOfLoopControl>(); - if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ true)) // ... UNDEF UNDEF UNDEF + if (!loopinfo.emitPrepareForNonLocalJumpFromScope(bce_, *es, + /* isTarget = */ true)) + { // ... UNDEF UNDEF UNDEF return false; + } } EmitterScope* targetEmitterScope = target ? target->emitterScope() : bce_->varEmitterScope; @@ -2869,7 +2900,7 @@ BytecodeEmitter::emitGoto(NestableControl* target, JumpList* jumplist, SrcNoteTy Scope* BytecodeEmitter::innermostScope() const { - return innermostEmitterScope->scope(this); + return innermostEmitterScope()->scope(this); } bool @@ -3172,10 +3203,11 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) *answer = true; return true; + case PNK_INITIALYIELD: case PNK_YIELD_STAR: case PNK_YIELD: case PNK_AWAIT: - MOZ_ASSERT(pn->isArity(PN_BINARY)); + MOZ_ASSERT(pn->isArity(PN_UNARY)); *answer = true; return true; @@ -3528,7 +3560,7 @@ BytecodeEmitter::needsImplicitThis() return true; // Otherwise see if the current point is under a 'with'. - for (EmitterScope* es = innermostEmitterScope; es; es = es->enclosingInFrame()) { + for (EmitterScope* es = innermostEmitterScope(); es; es = es->enclosingInFrame()) { if (es->scope(this)->kind() == ScopeKind::With) return true; } @@ -3718,6 +3750,18 @@ BytecodeEmitter::emitFinishIteratorResult(bool done) } bool +BytecodeEmitter::emitToIteratorResult(bool done) +{ + if (!emitPrepareIteratorResult()) // VALUE OBJ + return false; + if (!emit1(JSOP_SWAP)) // OBJ VALUE + return false; + if (!emitFinishIteratorResult(done)) // RESULT + return false; + return true; +} + +bool BytecodeEmitter::emitGetNameAtLocation(JSAtom* name, const NameLocation& loc, bool callContext) { switch (loc.kind()) { @@ -4783,7 +4827,9 @@ BytecodeEmitter::isRunOnceLambda() FunctionBox* funbox = sc->asFunctionBox(); return !funbox->argumentsHasLocalBinding() && - !funbox->isGenerator() && + !funbox->isStarGenerator() && + !funbox->isLegacyGenerator() && + !funbox->isAsync() && !funbox->function()->explicitName(); } @@ -4793,26 +4839,26 @@ BytecodeEmitter::emitYieldOp(JSOp op) if (op == JSOP_FINALYIELDRVAL) return emit1(JSOP_FINALYIELDRVAL); - MOZ_ASSERT(op == JSOP_INITIALYIELD || op == JSOP_YIELD); + MOZ_ASSERT(op == JSOP_INITIALYIELD || op == JSOP_YIELD || op == JSOP_AWAIT); ptrdiff_t off; if (!emitN(op, 3, &off)) return false; - uint32_t yieldIndex = yieldOffsetList.length(); - if (yieldIndex >= JS_BIT(24)) { + uint32_t yieldAndAwaitIndex = yieldAndAwaitOffsetList.length(); + if (yieldAndAwaitIndex >= JS_BIT(24)) { reportError(nullptr, JSMSG_TOO_MANY_YIELDS); return false; } if (op == JSOP_YIELD) - yieldOffsetList.numYields++; + yieldAndAwaitOffsetList.numYields++; else - yieldOffsetList.numAwaits++; + yieldAndAwaitOffsetList.numAwaits++; - SET_UINT24(code(off), yieldIndex); + SET_UINT24(code(off), yieldAndAwaitIndex); - if (!yieldOffsetList.append(offset())) + if (!yieldAndAwaitOffsetList.append(offset())) return false; return emit1(JSOP_DEBUGAFTERYIELD); @@ -5165,7 +5211,7 @@ BytecodeEmitter::emitSetOrInitializeDestructuring(ParseNode* target, Destructuri // destructuring declaration needs to initialize the name in // the function scope. The innermost scope is the var scope, // and its enclosing scope is the function scope. - EmitterScope* funScope = innermostEmitterScope->enclosingInFrame(); + EmitterScope* funScope = innermostEmitterScope()->enclosingInFrame(); NameLocation paramLoc = *locationOfNameBoundInScope(name, funScope); if (!emitSetOrInitializeNameAtLocation(name, paramLoc, emitSwapScopeAndRhs, true)) return false; @@ -5228,7 +5274,8 @@ BytecodeEmitter::emitSetOrInitializeDestructuring(ParseNode* target, Destructuri } bool -BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted /* = false */) +BytecodeEmitter::emitIteratorNext(ParseNode* pn, IteratorKind iterKind /* = IteratorKind::Sync */, + bool allowSelfHosted /* = false */) { MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting, ".next() iteration is prohibited in self-hosted code because it " @@ -5242,6 +5289,12 @@ BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted /* = false return false; if (!emitCall(JSOP_CALL, 0, pn)) // ... RESULT return false; + + if (iterKind == IteratorKind::Async) { + if (!emitAwaitInInnermostScope()) // ... RESULT + return false; + } + if (!emitCheckIsObj(CheckIsObjectKind::IteratorNext)) // ... RESULT return false; checkTypeSet(JSOP_CALL); @@ -5249,8 +5302,10 @@ BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted /* = false } bool -BytecodeEmitter::emitIteratorClose(CompletionKind completionKind /* = CompletionKind::Normal */, - bool allowSelfHosted /* = false */) +BytecodeEmitter::emitIteratorCloseInScope(EmitterScope& currentScope, + IteratorKind iterKind /* = IteratorKind::Sync */, + CompletionKind completionKind /* = CompletionKind::Normal */, + bool allowSelfHosted /* = false */) { MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting, ".close() on iterators is prohibited in self-hosted code because it " @@ -5334,6 +5389,18 @@ BytecodeEmitter::emitIteratorClose(CompletionKind completionKind /* = Completion return false; checkTypeSet(JSOP_CALL); + if (iterKind == IteratorKind::Async) { + if (completionKind != CompletionKind::Throw) { + // Await clobbers rval, so save the current rval. + if (!emit1(JSOP_GETRVAL)) // ... ... RESULT RVAL + return false; + if (!emit1(JSOP_SWAP)) // ... ... RVAL RESULT + return false; + } + if (!emitAwaitInScope(currentScope)) // ... ... RVAL? RESULT + return false; + } + if (completionKind == CompletionKind::Throw) { if (!emit1(JSOP_SWAP)) // ... RET ITER RESULT UNDEF return false; @@ -5343,7 +5410,7 @@ BytecodeEmitter::emitIteratorClose(CompletionKind completionKind /* = Completion if (!tryCatch->emitCatch()) // ... RET ITER RESULT return false; - // Just ignore the exception thrown by call. + // Just ignore the exception thrown by call and await. if (!emit1(JSOP_EXCEPTION)) // ... RET ITER RESULT EXC return false; if (!emit1(JSOP_POP)) // ... RET ITER RESULT @@ -5360,8 +5427,15 @@ BytecodeEmitter::emitIteratorClose(CompletionKind completionKind /* = Completion if (!emit1(JSOP_POP)) // ... RESULT return false; } else { - if (!emitCheckIsObj(CheckIsObjectKind::IteratorReturn)) // ... RESULT + if (!emitCheckIsObj(CheckIsObjectKind::IteratorReturn)) // ... RVAL? RESULT return false; + + if (iterKind == IteratorKind::Async) { + if (!emit1(JSOP_SWAP)) // ... RESULT RVAL + return false; + if (!emit1(JSOP_SETRVAL)) // ... RESULT + return false; + } } if (!ifReturnMethodIsDefined.emitElse()) @@ -5587,7 +5661,7 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav // For an empty pattern [], call IteratorClose unconditionally. Nothing // else needs to be done. if (!pattern->pn_head) - return emitIteratorClose(); // ... OBJ + return emitIteratorCloseInInnermostScope(); // ... OBJ // Push an initial FALSE value for DONE. if (!emit1(JSOP_FALSE)) // ... OBJ ITER FALSE @@ -5788,7 +5862,7 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav return false; if (!ifDone.emitElse()) // ... OBJ ITER return false; - if (!emitIteratorClose()) // ... OBJ + if (!emitIteratorCloseInInnermostScope()) // ... OBJ return false; if (!ifDone.emitEnd()) return false; @@ -6966,6 +7040,63 @@ BytecodeEmitter::emitIterator() } bool +BytecodeEmitter::emitAsyncIterator() +{ + // Convert iterable to iterator. + if (!emit1(JSOP_DUP)) // OBJ OBJ + return false; + if (!emit2(JSOP_SYMBOL, uint8_t(JS::SymbolCode::asyncIterator))) // OBJ OBJ @@ASYNCITERATOR + return false; + if (!emitElemOpBase(JSOP_CALLELEM)) // OBJ ITERFN + return false; + + IfThenElseEmitter ifAsyncIterIsUndefined(this); + if (!emit1(JSOP_DUP)) // OBJ ITERFN ITERFN + return false; + if (!emit1(JSOP_UNDEFINED)) // OBJ ITERFN ITERFN UNDEF + return false; + if (!emit1(JSOP_EQ)) // OBJ ITERFN EQ + return false; + if (!ifAsyncIterIsUndefined.emitIfElse()) // OBJ ITERFN + return false; + + if (!emit1(JSOP_POP)) // OBJ + return false; + if (!emit1(JSOP_DUP)) // OBJ OBJ + return false; + if (!emit2(JSOP_SYMBOL, uint8_t(JS::SymbolCode::iterator))) // OBJ OBJ @@ITERATOR + return false; + if (!emitElemOpBase(JSOP_CALLELEM)) // OBJ ITERFN + return false; + if (!emit1(JSOP_SWAP)) // ITERFN OBJ + return false; + if (!emitCall(JSOP_CALLITER, 0)) // ITER + return false; + checkTypeSet(JSOP_CALLITER); + if (!emitCheckIsObj(CheckIsObjectKind::GetIterator)) // ITER + return false; + + if (!emit1(JSOP_TOASYNCITER)) // ITER + return false; + + if (!ifAsyncIterIsUndefined.emitElse()) // OBJ ITERFN + return false; + + if (!emit1(JSOP_SWAP)) // ITERFN OBJ + return false; + if (!emitCall(JSOP_CALLITER, 0)) // ITER + return false; + checkTypeSet(JSOP_CALLITER); + if (!emitCheckIsObj(CheckIsObjectKind::GetIterator)) // ITER + return false; + + if (!ifAsyncIterIsUndefined.emitEnd()) // ITER + return false; + + return true; +} + +bool BytecodeEmitter::emitSpread(bool allowSelfHosted) { LoopControl loopInfo(this, StatementKind::Spread); @@ -7018,7 +7149,7 @@ BytecodeEmitter::emitSpread(bool allowSelfHosted) if (!emitDupAt(2)) // ITER ARR I ITER return false; - if (!emitIteratorNext(nullptr, allowSelfHosted)) // ITER ARR I RESULT + if (!emitIteratorNext(nullptr, IteratorKind::Sync, allowSelfHosted)) // ITER ARR I RESULT return false; if (!emit1(JSOP_DUP)) // ITER ARR I RESULT RESULT return false; @@ -7118,6 +7249,13 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte MOZ_ASSERT(forOfHead->isKind(PNK_FOROF)); MOZ_ASSERT(forOfHead->isArity(PN_TERNARY)); + unsigned iflags = forOfLoop->pn_iflags; + IteratorKind iterKind = (iflags & JSITER_FORAWAITOF) + ? IteratorKind::Async + : IteratorKind::Sync; + MOZ_ASSERT_IF(iterKind == IteratorKind::Async, sc->asFunctionBox()); + MOZ_ASSERT_IF(iterKind == IteratorKind::Async, sc->asFunctionBox()->isAsync()); + ParseNode* forHeadExpr = forOfHead->pn_kid3; // Certain builtins (e.g. Array.from) are implemented in self-hosting @@ -7133,8 +7271,13 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte // Evaluate the expression being iterated. if (!emitTree(forHeadExpr)) // ITERABLE return false; - if (!emitIterator()) // ITER - return false; + if (iterKind == IteratorKind::Async) { + if (!emitAsyncIterator()) // ITER + return false; + } else { + if (!emitIterator()) // ITER + return false; + } int32_t iterDepth = stackDepth; @@ -7145,7 +7288,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte if (!emit1(JSOP_UNDEFINED)) // ITER RESULT UNDEF return false; - ForOfLoopControl loopInfo(this, iterDepth, allowSelfHostedIter); + ForOfLoopControl loopInfo(this, iterDepth, allowSelfHostedIter, iterKind); // Annotate so IonMonkey can find the loop-closing jump. unsigned noteIndex; @@ -7170,7 +7313,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte // bindings inducing an environment, recreate the current environment. DebugOnly<ParseNode*> forOfTarget = forOfHead->pn_kid1; MOZ_ASSERT(forOfTarget->isKind(PNK_LET) || forOfTarget->isKind(PNK_CONST)); - MOZ_ASSERT(headLexicalEmitterScope == innermostEmitterScope); + MOZ_ASSERT(headLexicalEmitterScope == innermostEmitterScope()); MOZ_ASSERT(headLexicalEmitterScope->scope(this)->kind() == ScopeKind::Lexical); if (headLexicalEmitterScope->hasEnvironment()) { @@ -7241,7 +7384,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte if (!emitDupAt(1)) // ITER UNDEF ITER return false; - if (!emitIteratorNext(forOfHead, allowSelfHostedIter)) // ITER UNDEF RESULT + if (!emitIteratorNext(forOfHead, iterKind, allowSelfHostedIter)) // ITER UNDEF RESULT return false; if (!emit1(JSOP_SWAP)) // ITER RESULT UNDEF @@ -7352,7 +7495,7 @@ BytecodeEmitter::emitForIn(ParseNode* forInLoop, EmitterScope* headLexicalEmitte // it must be the innermost one. If that scope has closed-over // bindings inducing an environment, recreate the current environment. MOZ_ASSERT(forInTarget->isKind(PNK_LET) || forInTarget->isKind(PNK_CONST)); - MOZ_ASSERT(headLexicalEmitterScope == innermostEmitterScope); + MOZ_ASSERT(headLexicalEmitterScope == innermostEmitterScope()); MOZ_ASSERT(headLexicalEmitterScope->scope(this)->kind() == ScopeKind::Lexical); if (headLexicalEmitterScope->hasEnvironment()) { @@ -7481,7 +7624,7 @@ BytecodeEmitter::emitCStyleFor(ParseNode* pn, EmitterScope* headLexicalEmitterSc // exists for the head, it must be the innermost one. If that scope // has closed-over bindings inducing an environment, recreate the // current environment. - MOZ_ASSERT(headLexicalEmitterScope == innermostEmitterScope); + MOZ_ASSERT(headLexicalEmitterScope == innermostEmitterScope()); MOZ_ASSERT(headLexicalEmitterScope->scope(this)->kind() == ScopeKind::Lexical); if (headLexicalEmitterScope->hasEnvironment()) { @@ -7529,7 +7672,7 @@ BytecodeEmitter::emitCStyleFor(ParseNode* pn, EmitterScope* headLexicalEmitterSc // ES 13.7.4.8 step 3.e. The per-iteration freshening. if (forLoopRequiresFreshening) { - MOZ_ASSERT(headLexicalEmitterScope == innermostEmitterScope); + MOZ_ASSERT(headLexicalEmitterScope == innermostEmitterScope()); MOZ_ASSERT(headLexicalEmitterScope->scope(this)->kind() == ScopeKind::Lexical); if (headLexicalEmitterScope->hasEnvironment()) { @@ -8061,7 +8204,8 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) MOZ_ASSERT(fun->isArrow() == (pn->getOp() == JSOP_LAMBDA_ARROW)); if (funbox->isAsync()) { MOZ_ASSERT(!needsProto); - return emitAsyncWrapper(index, funbox->needsHomeObject(), fun->isArrow()); + return emitAsyncWrapper(index, funbox->needsHomeObject(), fun->isArrow(), + fun->isStarGenerator()); } if (fun->isArrow()) { @@ -8116,8 +8260,11 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) MOZ_ASSERT(pn->getOp() == JSOP_NOP); switchToPrologue(); if (funbox->isAsync()) { - if (!emitAsyncWrapper(index, fun->isMethod(), fun->isArrow())) + if (!emitAsyncWrapper(index, fun->isMethod(), fun->isArrow(), + fun->isStarGenerator())) + { return false; + } } else { if (!emitIndex32(JSOP_LAMBDA, index)) return false; @@ -8133,10 +8280,12 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) // initialize the binding name of the function in the current scope. bool isAsync = funbox->isAsync(); - auto emitLambda = [index, isAsync](BytecodeEmitter* bce, const NameLocation&, bool) { + bool isStarGenerator = funbox->isStarGenerator(); + auto emitLambda = [index, isAsync, isStarGenerator](BytecodeEmitter* bce, + const NameLocation&, bool) { if (isAsync) { return bce->emitAsyncWrapper(index, /* needsHomeObject = */ false, - /* isArrow = */ false); + /* isArrow = */ false, isStarGenerator); } return bce->emitIndexOp(JSOP_LAMBDA, index); }; @@ -8171,7 +8320,8 @@ BytecodeEmitter::emitAsyncWrapperLambda(unsigned index, bool isArrow) { } bool -BytecodeEmitter::emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow) +BytecodeEmitter::emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow, + bool isStarGenerator) { // needsHomeObject can be true for propertyList for extended class. // In that case push both unwrapped and wrapped function, in order to @@ -8203,8 +8353,13 @@ BytecodeEmitter::emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isA if (!emit1(JSOP_DUP)) return false; } - if (!emit1(JSOP_TOASYNC)) - return false; + if (isStarGenerator) { + if (!emit1(JSOP_TOASYNCGEN)) + return false; + } else { + if (!emit1(JSOP_TOASYNC)) + return false; + } return true; } @@ -8438,7 +8593,8 @@ BytecodeEmitter::emitReturn(ParseNode* pn) if (!updateSourceCoordNotes(pn->pn_pos.begin)) return false; - if (sc->isFunctionBox() && sc->asFunctionBox()->isStarGenerator()) { + bool needsIteratorResult = sc->isFunctionBox() && sc->asFunctionBox()->needsIteratorResult(); + if (needsIteratorResult) { if (!emitPrepareIteratorResult()) return false; } @@ -8447,13 +8603,20 @@ BytecodeEmitter::emitReturn(ParseNode* pn) if (ParseNode* pn2 = pn->pn_kid) { if (!emitTree(pn2)) return false; + + bool isAsyncGenerator = sc->asFunctionBox()->isAsync() && + sc->asFunctionBox()->isStarGenerator(); + if (isAsyncGenerator) { + if (!emitAwaitInInnermostScope()) + return false; + } } else { /* No explicit return value provided */ if (!emit1(JSOP_UNDEFINED)) return false; } - if (sc->isFunctionBox() && sc->asFunctionBox()->isStarGenerator()) { + if (needsIteratorResult) { if (!emitFinishIteratorResult(true)) return false; } @@ -8478,11 +8641,11 @@ BytecodeEmitter::emitReturn(ParseNode* pn) */ ptrdiff_t top = offset(); - bool isGenerator = sc->isFunctionBox() && sc->asFunctionBox()->isGenerator(); + bool needsFinalYield = sc->isFunctionBox() && sc->asFunctionBox()->needsFinalYield(); bool isDerivedClassConstructor = sc->isFunctionBox() && sc->asFunctionBox()->isDerivedClassConstructor(); - if (!emit1((isGenerator || isDerivedClassConstructor) ? JSOP_SETRVAL : JSOP_RETURN)) + if (!emit1((needsFinalYield || isDerivedClassConstructor) ? JSOP_SETRVAL : JSOP_RETURN)) return false; // Make sure that we emit this before popping the blocks in prepareForNonLocalJump, @@ -8497,7 +8660,7 @@ BytecodeEmitter::emitReturn(ParseNode* pn) if (!nle.prepareForNonLocalJumpToOutermost()) return false; - if (isGenerator) { + if (needsFinalYield) { // We know that .generator is on the function scope, as we just exited // all nested scopes. NameLocation loc = @@ -8520,52 +8683,105 @@ BytecodeEmitter::emitReturn(ParseNode* pn) } bool +BytecodeEmitter::emitGetDotGeneratorInScope(EmitterScope& currentScope) +{ + NameLocation loc = *locationOfNameBoundInFunctionScope(cx->names().dotGenerator, ¤tScope); + return emitGetNameAtLocation(cx->names().dotGenerator, loc); +} + +bool +BytecodeEmitter::emitInitialYield(ParseNode* pn) +{ + if (!emitTree(pn->pn_kid)) + return false; + + if (!emitYieldOp(JSOP_INITIALYIELD)) + return false; + + if (!emit1(JSOP_POP)) + return false; + + return true; +} + +bool BytecodeEmitter::emitYield(ParseNode* pn) { MOZ_ASSERT(sc->isFunctionBox()); + MOZ_ASSERT(pn->getOp() == JSOP_YIELD); - if (pn->getOp() == JSOP_YIELD) { - if (sc->asFunctionBox()->isStarGenerator()) { - if (!emitPrepareIteratorResult()) - return false; - } - if (pn->pn_left) { - if (!emitTree(pn->pn_left)) - return false; - } else { - if (!emit1(JSOP_UNDEFINED)) - return false; - } - if (sc->asFunctionBox()->isStarGenerator()) { - if (!emitFinishIteratorResult(false)) - return false; - } + bool needsIteratorResult = sc->asFunctionBox()->needsIteratorResult(); + if (needsIteratorResult) { + if (!emitPrepareIteratorResult()) + return false; + } + if (pn->pn_kid) { + if (!emitTree(pn->pn_kid)) + return false; } else { - MOZ_ASSERT(pn->getOp() == JSOP_INITIALYIELD); + if (!emit1(JSOP_UNDEFINED)) + return false; } - if (!emitTree(pn->pn_right)) + // 11.4.3.7 AsyncGeneratorYield step 5. + bool isAsyncGenerator = sc->asFunctionBox()->isAsync(); + if (isAsyncGenerator) { + if (!emitAwaitInInnermostScope()) // RESULT + return false; + } + + if (needsIteratorResult) { + if (!emitFinishIteratorResult(false)) + return false; + } + + if (!emitGetDotGeneratorInInnermostScope()) return false; - if (!emitYieldOp(pn->getOp())) + if (!emitYieldOp(JSOP_YIELD)) return false; - if (pn->getOp() == JSOP_INITIALYIELD && !emit1(JSOP_POP)) + return true; +} + +bool +BytecodeEmitter::emitAwaitInInnermostScope(ParseNode* pn) +{ + MOZ_ASSERT(sc->isFunctionBox()); + MOZ_ASSERT(pn->getOp() == JSOP_AWAIT); + + if (!emitTree(pn->pn_kid)) return false; + return emitAwaitInInnermostScope(); +} +bool +BytecodeEmitter::emitAwaitInScope(EmitterScope& currentScope) +{ + if (!emitGetDotGeneratorInScope(currentScope)) + return false; + if (!emitYieldOp(JSOP_AWAIT)) + return false; return true; } bool -BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen) +BytecodeEmitter::emitYieldStar(ParseNode* iter) { MOZ_ASSERT(sc->isFunctionBox()); MOZ_ASSERT(sc->asFunctionBox()->isStarGenerator()); + bool isAsyncGenerator = sc->asFunctionBox()->isAsync(); + if (!emitTree(iter)) // ITERABLE return false; - if (!emitIterator()) // ITER - return false; + if (isAsyncGenerator) { + if (!emitAsyncIterator()) // ITER + return false; + } else { + if (!emitIterator()) // ITER + return false; + } // Initial send value is undefined. if (!emit1(JSOP_UNDEFINED)) // ITER RECEIVED @@ -8586,8 +8802,14 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen) MOZ_ASSERT(this->stackDepth == startDepth); + // 11.4.3.7 AsyncGeneratorYield step 5. + if (isAsyncGenerator) { + if (!emitAwaitInInnermostScope()) // NEXT ITER RESULT + return false; + } + // Load the generator object. - if (!emitTree(gen)) // ITER RESULT GENOBJ + if (!emitGetDotGeneratorInInnermostScope()) // NEXT ITER RESULT GENOBJ return false; // Yield RESULT as-is, without re-boxing. @@ -8623,7 +8845,8 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen) // // If the iterator does not have a "throw" method, it calls IteratorClose // and then throws a TypeError. - if (!emitIteratorClose()) // ITER RESULT EXCEPTION + IteratorKind iterKind = isAsyncGenerator ? IteratorKind::Async : IteratorKind::Sync; + if (!emitIteratorCloseInInnermostScope(iterKind)) // NEXT ITER RESULT EXCEPTION return false; if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_ITERATOR_NO_THROW)) // throw return false; @@ -8639,6 +8862,12 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen) if (!emitCall(JSOP_CALL, 1, iter)) // ITER OLDRESULT RESULT return false; checkTypeSet(JSOP_CALL); + + if (isAsyncGenerator) { + if (!emitAwaitInInnermostScope()) // NEXT ITER OLDRESULT RESULT + return false; + } + if (!emitCheckIsObj(CheckIsObjectKind::IteratorThrow)) // ITER OLDRESULT RESULT return false; if (!emit1(JSOP_SWAP)) // ITER RESULT OLDRESULT @@ -8705,6 +8934,11 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen) return false; checkTypeSet(JSOP_CALL); + if (iterKind == IteratorKind::Async) { + if (!emitAwaitInInnermostScope()) // ... FTYPE FVALUE RESULT + return false; + } + // Step v. if (!emitCheckIsObj(CheckIsObjectKind::IteratorReturn)) // ITER OLDRESULT FTYPE FVALUE RESULT return false; @@ -8779,9 +9013,15 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen) return false; if (!emitCall(JSOP_CALL, 1, iter)) // ITER RESULT return false; + checkTypeSet(JSOP_CALL); + + if (isAsyncGenerator) { + if (!emitAwaitInInnermostScope()) // NEXT ITER RESULT RESULT + return false; + } + if (!emitCheckIsObj(CheckIsObjectKind::IteratorNext)) // ITER RESULT return false; - checkTypeSet(JSOP_CALL); MOZ_ASSERT(this->stackDepth == startDepth); if (!emitJumpTargetAndPatch(checkResult)) // checkResult: @@ -10131,7 +10371,7 @@ BytecodeEmitter::emitFunctionFormalParameters(ParseNode* pn) { ParseNode* funBody = pn->last(); FunctionBox* funbox = sc->asFunctionBox(); - EmitterScope* funScope = innermostEmitterScope; + EmitterScope* funScope = innermostEmitterScope(); bool hasParameterExprs = funbox->hasParameterExprs; bool hasRest = funbox->hasRest(); @@ -10312,22 +10552,26 @@ BytecodeEmitter::emitFunctionBody(ParseNode* funBody) if (!emitTree(funBody)) return false; - if (funbox->isGenerator()) { + if (funbox->needsFinalYield()) { // If we fall off the end of a generator, do a final yield. - if (funbox->isStarGenerator() && !emitPrepareIteratorResult()) - return false; + bool needsIteratorResult = funbox->needsIteratorResult(); + if (needsIteratorResult) { + if (!emitPrepareIteratorResult()) + return false; + } if (!emit1(JSOP_UNDEFINED)) return false; - if (sc->asFunctionBox()->isStarGenerator() && !emitFinishIteratorResult(true)) - return false; + if (needsIteratorResult) { + if (!emitFinishIteratorResult(true)) + return false; + } if (!emit1(JSOP_SETRVAL)) return false; - NameLocation loc = *locationOfNameBoundInFunctionScope(cx->names().dotGenerator); - if (!emitGetNameAtLocation(cx->names().dotGenerator, loc)) + if (!emitGetDotGeneratorInInnermostScope()) return false; // No need to check for finally blocks, etc as in EmitReturn. @@ -10587,7 +10831,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage:: break; case PNK_YIELD_STAR: - if (!emitYieldStar(pn->pn_left, pn->pn_right)) + if (!emitYieldStar(pn->pn_kid)) return false; break; @@ -10596,12 +10840,21 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage:: return false; break; + case PNK_INITIALYIELD: + if (!emitInitialYield(pn)) + return false; + break; + case PNK_YIELD: - case PNK_AWAIT: if (!emitYield(pn)) return false; break; + case PNK_AWAIT: + if (!emitAwaitInInnermostScope(pn)) + return false; + break; + case PNK_STATEMENTLIST: if (!emitStatementList(pn)) return false; @@ -11281,7 +11534,7 @@ CGScopeNoteList::finish(ScopeNoteArray* array, uint32_t prologueLength) } void -CGYieldOffsetList::finish(YieldOffsetArray& array, uint32_t prologueLength) +CGYieldAndAwaitOffsetList::finish(YieldAndAwaitOffsetArray& array, uint32_t prologueLength) { MOZ_ASSERT(length() == array.length()); diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 595ee6405..8ad409c11 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -10,6 +10,7 @@ #define frontend_BytecodeEmitter_h #include "jscntxt.h" +#include "jsiter.h" #include "jsopcode.h" #include "jsscript.h" @@ -98,15 +99,15 @@ struct CGScopeNoteList { void finish(ScopeNoteArray* array, uint32_t prologueLength); }; -struct CGYieldOffsetList { +struct CGYieldAndAwaitOffsetList { Vector<uint32_t> list; uint32_t numYields; uint32_t numAwaits; - explicit CGYieldOffsetList(ExclusiveContext* cx) : list(cx), numYields(0), numAwaits(0) {} + explicit CGYieldAndAwaitOffsetList(ExclusiveContext* cx) : list(cx), numYields(0), numAwaits(0) {} MOZ_MUST_USE bool append(uint32_t offset) { return list.append(offset); } size_t length() const { return list.length(); } - void finish(YieldOffsetArray& array, uint32_t prologueLength); + void finish(YieldAndAwaitOffsetArray& array, uint32_t prologueLength); }; static size_t MaxBytecodeLength = INT32_MAX; @@ -227,9 +228,23 @@ struct MOZ_STACK_CLASS BytecodeEmitter EmitterScope* varEmitterScope; NestableControl* innermostNestableControl; - EmitterScope* innermostEmitterScope; + EmitterScope* innermostEmitterScope_; TDZCheckCache* innermostTDZCheckCache; +#ifdef DEBUG + bool unstableEmitterScope; + + friend class AutoCheckUnstableEmitterScope; +#endif + + EmitterScope* innermostEmitterScope() const { + MOZ_ASSERT(!unstableEmitterScope); + return innermostEmitterScopeNoCheck(); + } + EmitterScope* innermostEmitterScopeNoCheck() const { + return innermostEmitterScope_; + } + CGConstList constList; /* constants to be included with the script */ CGObjectList objectList; /* list of emitted objects */ CGScopeList scopeList; /* list of emitted scopes */ @@ -237,10 +252,10 @@ struct MOZ_STACK_CLASS BytecodeEmitter CGScopeNoteList scopeNoteList; /* list of emitted block scope notes */ /* - * For each yield op, map the yield index (stored as bytecode operand) to - * the offset of the next op. + * For each yield or await op, map the yield and await index (stored as + * bytecode operand) to the offset of the next op. */ - CGYieldOffsetList yieldOffsetList; + CGYieldAndAwaitOffsetList yieldAndAwaitOffsetList; uint16_t typesetCount; /* Number of JOF_TYPESET opcodes generated */ @@ -318,7 +333,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter EmitterScope* source); mozilla::Maybe<NameLocation> locationOfNameBoundInFunctionScope(JSAtom* name) { - return locationOfNameBoundInFunctionScope(name, innermostEmitterScope); + return locationOfNameBoundInFunctionScope(name, innermostEmitterScope()); } void setVarEmitterScope(EmitterScope* emitterScope) { @@ -606,17 +621,29 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool emitPrepareIteratorResult(); MOZ_MUST_USE bool emitFinishIteratorResult(bool done); MOZ_MUST_USE bool iteratorResultShape(unsigned* shape); + MOZ_MUST_USE bool emitToIteratorResult(bool done); + MOZ_MUST_USE bool emitGetDotGeneratorInInnermostScope() { + return emitGetDotGeneratorInScope(*innermostEmitterScope()); + } + MOZ_MUST_USE bool emitGetDotGeneratorInScope(EmitterScope& currentScope); + + MOZ_MUST_USE bool emitInitialYield(ParseNode* pn); MOZ_MUST_USE bool emitYield(ParseNode* pn); MOZ_MUST_USE bool emitYieldOp(JSOp op); - MOZ_MUST_USE bool emitYieldStar(ParseNode* iter, ParseNode* gen); - + MOZ_MUST_USE bool emitYieldStar(ParseNode* iter); + MOZ_MUST_USE bool emitAwaitInInnermostScope() { + return emitAwaitInScope(*innermostEmitterScope()); + } + MOZ_MUST_USE bool emitAwaitInInnermostScope(ParseNode* pn); + MOZ_MUST_USE bool emitAwaitInScope(EmitterScope& currentScope); MOZ_MUST_USE bool emitPropLHS(ParseNode* pn); MOZ_MUST_USE bool emitPropOp(ParseNode* pn, JSOp op); MOZ_MUST_USE bool emitPropIncDec(ParseNode* pn); MOZ_MUST_USE bool emitAsyncWrapperLambda(unsigned index, bool isArrow); - MOZ_MUST_USE bool emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow); + MOZ_MUST_USE bool emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow, + bool isStarGenerator); MOZ_MUST_USE bool emitComputedPropertyName(ParseNode* computedPropName); @@ -703,11 +730,22 @@ struct MOZ_STACK_CLASS BytecodeEmitter // It will replace that stack value with the corresponding iterator MOZ_MUST_USE bool emitIterator(); + MOZ_MUST_USE bool emitAsyncIterator(); + // Pops iterator from the top of the stack. Pushes the result of |.next()| // onto the stack. - MOZ_MUST_USE bool emitIteratorNext(ParseNode* pn, bool allowSelfHosted = false); - MOZ_MUST_USE bool emitIteratorClose(CompletionKind completionKind = CompletionKind::Normal, - bool allowSelfHosted = false); + MOZ_MUST_USE bool emitIteratorNext(ParseNode* pn, IteratorKind kind = IteratorKind::Sync, + bool allowSelfHosted = false); + MOZ_MUST_USE bool emitIteratorCloseInScope(EmitterScope& currentScope, + IteratorKind iterKind = IteratorKind::Sync, + CompletionKind completionKind = CompletionKind::Normal, + bool allowSelfHosted = false); + MOZ_MUST_USE bool emitIteratorCloseInInnermostScope(IteratorKind iterKind = IteratorKind::Sync, + CompletionKind completionKind = CompletionKind::Normal, + bool allowSelfHosted = false) { + return emitIteratorCloseInScope(*innermostEmitterScope(), iterKind, completionKind, + allowSelfHosted); + } template <typename InnerEmitter> MOZ_MUST_USE bool wrapWithDestructuringIteratorCloseTryNote(int32_t iterDepth, @@ -805,6 +843,31 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool emitSuperElemOp(ParseNode* pn, JSOp op, bool isCall = false); }; +class MOZ_RAII AutoCheckUnstableEmitterScope { +#ifdef DEBUG + bool prev_; + BytecodeEmitter* bce_; +#endif + + public: + AutoCheckUnstableEmitterScope() = delete; + explicit AutoCheckUnstableEmitterScope(BytecodeEmitter* bce) +#ifdef DEBUG + : bce_(bce) +#endif + { +#ifdef DEBUG + prev_ = bce_->unstableEmitterScope; + bce_->unstableEmitterScope = true; +#endif + } + ~AutoCheckUnstableEmitterScope() { +#ifdef DEBUG + bce_->unstableEmitterScope = prev_; +#endif + } +}; + } /* namespace frontend */ } /* namespace js */ diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index 689fa02b4..16294c4a8 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -117,9 +117,10 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result) // These two aren't statements in the spec, but we sometimes insert them // in statement lists anyway. + case PNK_INITIALYIELD: case PNK_YIELD_STAR: case PNK_YIELD: - MOZ_ASSERT(node->isArity(PN_BINARY)); + MOZ_ASSERT(node->isArity(PN_UNARY)); *result = false; return true; @@ -1775,21 +1776,23 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo case PNK_GENEXP: return FoldList(cx, pn, parser, inGenexpLambda); + case PNK_INITIALYIELD: + MOZ_ASSERT(pn->isArity(PN_UNARY)); + MOZ_ASSERT(pn->pn_kid->isKind(PNK_ASSIGN) && + pn->pn_kid->pn_left->isKind(PNK_NAME) && + pn->pn_kid->pn_right->isKind(PNK_GENERATOR)); + return true; + case PNK_YIELD_STAR: - MOZ_ASSERT(pn->isArity(PN_BINARY)); - MOZ_ASSERT(pn->pn_right->isKind(PNK_NAME)); - return Fold(cx, &pn->pn_left, parser, inGenexpLambda); + MOZ_ASSERT(pn->isArity(PN_UNARY)); + return Fold(cx, &pn->pn_kid, parser, inGenexpLambda); case PNK_YIELD: case PNK_AWAIT: - MOZ_ASSERT(pn->isArity(PN_BINARY)); - MOZ_ASSERT(pn->pn_right->isKind(PNK_NAME) || - (pn->pn_right->isKind(PNK_ASSIGN) && - pn->pn_right->pn_left->isKind(PNK_NAME) && - pn->pn_right->pn_right->isKind(PNK_GENERATOR))); - if (!pn->pn_left) + MOZ_ASSERT(pn->isArity(PN_UNARY)); + if (!pn->pn_kid) return true; - return Fold(cx, &pn->pn_left, parser, inGenexpLambda); + return Fold(cx, &pn->pn_kid, parser, inGenexpLambda); case PNK_RETURN: return FoldReturn(cx, pn, parser, inGenexpLambda); diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 2d7f57e1e..44694298b 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -440,20 +440,24 @@ class FullParseHandler return true; } - ParseNode* newYieldExpression(uint32_t begin, ParseNode* value, ParseNode* gen, - JSOp op = JSOP_YIELD) { + ParseNode* newInitialYieldExpression(uint32_t begin, ParseNode* gen) { + TokenPos pos(begin, begin + 1); + return new_<UnaryNode>(PNK_INITIALYIELD, JSOP_INITIALYIELD, pos, gen); + } + + ParseNode* newYieldExpression(uint32_t begin, ParseNode* value) { TokenPos pos(begin, value ? value->pn_pos.end : begin + 1); - return new_<BinaryNode>(PNK_YIELD, op, pos, value, gen); + return new_<UnaryNode>(PNK_YIELD, JSOP_YIELD, pos, value); } - ParseNode* newYieldStarExpression(uint32_t begin, ParseNode* value, ParseNode* gen) { + ParseNode* newYieldStarExpression(uint32_t begin, ParseNode* value) { TokenPos pos(begin, value->pn_pos.end); - return new_<BinaryNode>(PNK_YIELD_STAR, JSOP_NOP, pos, value, gen); + return new_<UnaryNode>(PNK_YIELD_STAR, JSOP_NOP, pos, value); } - ParseNode* newAwaitExpression(uint32_t begin, ParseNode* value, ParseNode* gen) { + ParseNode* newAwaitExpression(uint32_t begin, ParseNode* value) { TokenPos pos(begin, value ? value->pn_pos.end : begin + 1); - return new_<BinaryNode>(PNK_AWAIT, JSOP_YIELD, pos, value, gen); + return new_<UnaryNode>(PNK_AWAIT, JSOP_AWAIT, pos, value); } // Statements @@ -506,8 +510,7 @@ class FullParseHandler if (!genInit) return false; - ParseNode* initialYield = newYieldExpression(yieldPos.begin, nullptr, genInit, - JSOP_INITIALYIELD); + ParseNode* initialYield = newInitialYieldExpression(yieldPos.begin, genInit); if (!initialYield) return false; diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp index dc54d0a88..376be7624 100644 --- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -501,24 +501,25 @@ class NameResolver return false; break; + case PNK_INITIALYIELD: + MOZ_ASSERT(cur->pn_kid->isKind(PNK_ASSIGN) && + cur->pn_kid->pn_left->isKind(PNK_NAME) && + cur->pn_kid->pn_right->isKind(PNK_GENERATOR)); + break; + case PNK_YIELD_STAR: - MOZ_ASSERT(cur->isArity(PN_BINARY)); - MOZ_ASSERT(cur->pn_right->isKind(PNK_NAME)); - if (!resolve(cur->pn_left, prefix)) + MOZ_ASSERT(cur->isArity(PN_UNARY)); + if (!resolve(cur->pn_kid, prefix)) return false; break; case PNK_YIELD: case PNK_AWAIT: - MOZ_ASSERT(cur->isArity(PN_BINARY)); - if (cur->pn_left) { - if (!resolve(cur->pn_left, prefix)) + MOZ_ASSERT(cur->isArity(PN_UNARY)); + if (cur->pn_kid) { + if (!resolve(cur->pn_kid, prefix)) return false; } - MOZ_ASSERT(cur->pn_right->isKind(PNK_NAME) || - (cur->pn_right->isKind(PNK_ASSIGN) && - cur->pn_right->pn_left->isKind(PNK_NAME) && - cur->pn_right->pn_right->isKind(PNK_GENERATOR))); break; case PNK_RETURN: diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index 5fe64e3d3..42ae9451a 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -286,22 +286,24 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) return PushResult::Recyclable; } - // The left half is the expression being yielded. The right half is - // internal goop: a name reference to the invisible '.generator' local - // variable, or an assignment of a PNK_GENERATOR node to the '.generator' - // local, for a synthesized, prepended initial yield. Yum! + // The child is an assignment of a PNK_GENERATOR node to the + // '.generator' local, for a synthesized, prepended initial yield. + case PNK_INITIALYIELD: { + MOZ_ASSERT(pn->isArity(PN_UNARY)); + MOZ_ASSERT(pn->pn_kid->isKind(PNK_ASSIGN) && + pn->pn_kid->pn_left->isKind(PNK_NAME) && + pn->pn_kid->pn_right->isKind(PNK_GENERATOR)); + stack->push(pn->pn_kid); + return PushResult::Recyclable; + } + + // The child is the expression being yielded. case PNK_YIELD_STAR: case PNK_YIELD: case PNK_AWAIT: { - MOZ_ASSERT(pn->isArity(PN_BINARY)); - MOZ_ASSERT(pn->pn_right); - MOZ_ASSERT(pn->pn_right->isKind(PNK_NAME) || - (pn->pn_right->isKind(PNK_ASSIGN) && - pn->pn_right->pn_left->isKind(PNK_NAME) && - pn->pn_right->pn_right->isKind(PNK_GENERATOR))); - if (pn->pn_left) - stack->push(pn->pn_left); - stack->push(pn->pn_right); + MOZ_ASSERT(pn->isArity(PN_UNARY)); + if (pn->pn_kid) + stack->push(pn->pn_kid); return PushResult::Recyclable; } diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 1f20f3988..9a435e270 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -83,6 +83,7 @@ class ObjectBox; F(THROW) \ F(DEBUGGER) \ F(GENERATOR) \ + F(INITIALYIELD) \ F(YIELD) \ F(YIELD_STAR) \ F(GENEXP) \ @@ -418,8 +419,9 @@ IsTypeofKind(ParseNodeKind kind) * PNK_LEXICALSCOPE scope pn_u.scope.bindings: scope bindings * pn_u.scope.body: scope body * PNK_GENERATOR nullary - * PNK_YIELD, binary pn_left: expr or null; pn_right: generator object - * PNK_YIELD_STAR + * PNK_INITIALYIELD unary pn_kid: generator object + * PNK_YIELD, unary pn_kid: expr or null + * PNK_YIELD_STAR, * PNK_ARRAYCOMP list pn_count: 1 * pn_head: list of 1 element, which is block * enclosing for loop(s) and optionally diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 0c279591f..cf9f1e27c 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -2474,10 +2474,8 @@ Parser<SyntaxParseHandler>::finishFunction(bool isStandaloneFunction /* = false } static YieldHandling -GetYieldHandling(GeneratorKind generatorKind, FunctionAsyncKind asyncKind) +GetYieldHandling(GeneratorKind generatorKind) { - if (asyncKind == AsyncFunction) - return YieldIsName; if (generatorKind == NotGenerator) return YieldIsName; return YieldIsKeyword; @@ -2541,7 +2539,7 @@ Parser<FullParseHandler>::standaloneFunction(HandleFunction fun, return null(); funpc.setIsStandaloneFunctionBody(); - YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind); + YieldHandling yieldHandling = GetYieldHandling(generatorKind); AutoAwaitIsKeyword<FullParseHandler> awaitIsKeyword(this, asyncKind == AsyncFunction); if (!functionFormalParametersAndBody(InAllowed, yieldHandling, fn, Statement, parameterListEnd, /* isStandaloneFunction = */ true)) @@ -2699,7 +2697,7 @@ Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHan switch (pc->generatorKind()) { case NotGenerator: - MOZ_ASSERT(pc->lastYieldOffset == startYieldOffset); + MOZ_ASSERT_IF(!pc->isAsync(), pc->lastYieldOffset == startYieldOffset); break; case LegacyGenerator: @@ -2715,12 +2713,12 @@ Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHan break; case StarGenerator: - MOZ_ASSERT_IF(!pc->isAsync(), kind != Arrow); - MOZ_ASSERT_IF(!pc->isAsync(), type == StatementListBody); + MOZ_ASSERT(kind != Arrow); + MOZ_ASSERT(type == StatementListBody); break; } - if (pc->isGenerator()) { + if (pc->needsDotGeneratorName()) { MOZ_ASSERT_IF(!pc->isAsync(), type == StatementListBody); if (!declareDotGeneratorName()) return null(); @@ -2761,9 +2759,9 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind, #endif switch (kind) { case Expression: - flags = (generatorKind == NotGenerator + flags = (generatorKind == NotGenerator && asyncKind == SyncFunction ? JSFunction::INTERPRETED_LAMBDA - : JSFunction::INTERPRETED_LAMBDA_GENERATOR); + : JSFunction::INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC); break; case Arrow: flags = JSFunction::INTERPRETED_LAMBDA_ARROW; @@ -2771,9 +2769,9 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind, break; case Method: MOZ_ASSERT(generatorKind == NotGenerator || generatorKind == StarGenerator); - flags = (generatorKind == NotGenerator + flags = (generatorKind == NotGenerator && asyncKind == SyncFunction ? JSFunction::INTERPRETED_METHOD - : JSFunction::INTERPRETED_METHOD_GENERATOR); + : JSFunction::INTERPRETED_METHOD_GENERATOR_OR_ASYNC); allocKind = gc::AllocKind::FUNCTION_EXTENDED; break; case ClassConstructor: @@ -2799,9 +2797,9 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind, allocKind = gc::AllocKind::FUNCTION_EXTENDED; } #endif - flags = (generatorKind == NotGenerator + flags = (generatorKind == NotGenerator && asyncKind == SyncFunction ? JSFunction::INTERPRETED_NORMAL - : JSFunction::INTERPRETED_GENERATOR); + : JSFunction::INTERPRETED_GENERATOR_OR_ASYNC); } // We store the async wrapper in a slot for later access. @@ -3324,7 +3322,6 @@ Parser<ParseHandler>::functionDefinition(uint32_t toStringStart, Node pn, InHand bool tryAnnexB /* = false */) { MOZ_ASSERT_IF(kind == Statement, funName); - MOZ_ASSERT_IF(asyncKind == AsyncFunction, generatorKind == StarGenerator); // When fully parsing a LazyScript, we do not fully reparse its inner // functions, which are also lazy. Instead, their free variables and @@ -3336,7 +3333,7 @@ Parser<ParseHandler>::functionDefinition(uint32_t toStringStart, Node pn, InHand } RootedObject proto(context); - if (generatorKind == StarGenerator) { + if (generatorKind == StarGenerator || asyncKind == AsyncFunction) { // If we are off the main thread, the generator meta-objects have // already been created by js::StartOffThreadParseScript, so cx will not // be necessary. @@ -3408,7 +3405,7 @@ Parser<FullParseHandler>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunct // parse to avoid the overhead of a lazy syntax-only parse. Although // the prediction may be incorrect, IIFEs are common enough that it // pays off for lots of code. - if (pn->isLikelyIIFE() && generatorKind == NotGenerator) + if (pn->isLikelyIIFE() && generatorKind == NotGenerator && asyncKind == SyncFunction) break; Parser<SyntaxParseHandler>* parser = handler.syntaxParser; @@ -3584,7 +3581,7 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict if (!tokenStream.peekTokenPos(&pn->pn_pos, modifier)) return null(); - YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind); + YieldHandling yieldHandling = GetYieldHandling(generatorKind); FunctionSyntaxKind syntaxKind = Statement; if (fun->isClassConstructor()) syntaxKind = ClassConstructor; @@ -3665,7 +3662,7 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling, return false; uint32_t openedPos = 0; if (tt != TOK_LC) { - if ((funbox->isStarGenerator() && !funbox->isAsync()) || kind == Method || + if (funbox->isStarGenerator() || kind == Method || kind == GetterNoExpressionClosure || kind == SetterNoExpressionClosure || IsConstructorKind(kind)) { error(JSMSG_CURLY_BEFORE_BODY); @@ -3695,7 +3692,7 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling, // whether the arrow function is enclosed in a generator function or not. // Whereas the |yield| in the function body is always parsed as a name. // The same goes when parsing |await| in arrow functions. - YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind(), pc->asyncKind()); + YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind()); Node body; { AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, funbox->isAsync()); @@ -3786,12 +3783,8 @@ Parser<ParseHandler>::functionStmt(uint32_t toStringStart, YieldHandling yieldHa if (!tokenStream.getToken(&tt)) return null(); - GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator; + GeneratorKind generatorKind = NotGenerator; if (tt == TOK_MUL) { - if (asyncKind != SyncFunction) { - error(JSMSG_ASYNC_GENERATOR); - return null(); - } generatorKind = StarGenerator; if (!tokenStream.getToken(&tt)) return null(); @@ -3817,7 +3810,7 @@ Parser<ParseHandler>::functionStmt(uint32_t toStringStart, YieldHandling yieldHa MOZ_ASSERT(declaredInStmt->kind() != StatementKind::Label); MOZ_ASSERT(StatementKindIsBraced(declaredInStmt->kind())); - if (!pc->sc()->strict() && generatorKind == NotGenerator) { + if (!pc->sc()->strict() && generatorKind == NotGenerator && asyncKind == SyncFunction) { // In sloppy mode, try Annex B.3.3 semantics. If making an // additional 'var' binding of the same name does not throw an // early error, do so. This 'var' binding would be assigned @@ -3841,7 +3834,7 @@ Parser<ParseHandler>::functionStmt(uint32_t toStringStart, YieldHandling yieldHa if (!pn) return null(); - YieldHandling newYieldHandling = GetYieldHandling(generatorKind, asyncKind); + YieldHandling newYieldHandling = GetYieldHandling(generatorKind); return functionDefinition(toStringStart, pn, InAllowed, newYieldHandling, name, Statement, generatorKind, asyncKind, tryAnnexB); } @@ -3854,22 +3847,18 @@ Parser<ParseHandler>::functionExpr(uint32_t toStringStart, InvokedPrediction inv MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, asyncKind == AsyncFunction); - GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator; + GeneratorKind generatorKind = NotGenerator; TokenKind tt; if (!tokenStream.getToken(&tt)) return null(); if (tt == TOK_MUL) { - if (asyncKind != SyncFunction) { - error(JSMSG_ASYNC_GENERATOR); - return null(); - } generatorKind = StarGenerator; if (!tokenStream.getToken(&tt)) return null(); } - YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind); + YieldHandling yieldHandling = GetYieldHandling(generatorKind); RootedPropertyName name(context); if (TokenKindIsPossibleIdentifier(tt)) { @@ -5971,6 +5960,7 @@ Parser<ParseHandler>::matchInOrOf(bool* isForInp, bool* isForOfp) template <class ParseHandler> bool Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling, + IteratorKind iterKind, ParseNodeKind* forHeadKind, Node* forInitialPart, Maybe<ParseContext::Scope>& forLoopLexicalScope, @@ -6061,6 +6051,11 @@ Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling, if (!matchInOrOf(&isForIn, &isForOf)) return false; + if (iterKind == IteratorKind::Async && !isForOf) { + error(JSMSG_FOR_AWAIT_NOT_OF); + return false; + } + // If we don't encounter 'in'/'of', we have a for(;;) loop. We've handled // the init expression; the caller handles the rest. Allow the Operand // modifier when regetting: Operand must be used to examine the ';' in @@ -6134,6 +6129,7 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling) ParseContext::Statement stmt(pc, StatementKind::ForLoop); bool isForEach = false; + IteratorKind iterKind = IteratorKind::Sync; unsigned iflags = 0; if (allowsForEachIn()) { @@ -6148,6 +6144,17 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling) } } + if (pc->isAsync()) { + bool matched; + if (!tokenStream.matchToken(&matched, TOK_AWAIT)) + return null(); + + if (matched) { + iflags |= JSITER_FORAWAITOF; + iterKind = IteratorKind::Async; + } + } + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); // PNK_FORHEAD, PNK_FORIN, or PNK_FOROF depending on the loop type. @@ -6185,7 +6192,7 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling) // // In either case the subsequent token can be consistently accessed using // TokenStream::None semantics. - if (!forHeadStart(yieldHandling, &headKind, &startNode, forLoopLexicalScope, + if (!forHeadStart(yieldHandling, iterKind, &headKind, &startNode, forLoopLexicalScope, &iteratedExpr)) { return null(); @@ -6551,29 +6558,6 @@ Parser<ParseHandler>::returnStatement(YieldHandling yieldHandling) template <typename ParseHandler> typename ParseHandler::Node -Parser<ParseHandler>::newYieldExpression(uint32_t begin, typename ParseHandler::Node expr, - bool isYieldStar) -{ - Node generator = newDotGeneratorName(); - if (!generator) - return null(); - if (isYieldStar) - return handler.newYieldStarExpression(begin, expr, generator); - return handler.newYieldExpression(begin, expr, generator); -} - -template <typename ParseHandler> -typename ParseHandler::Node -Parser<ParseHandler>::newAwaitExpression(uint32_t begin, typename ParseHandler::Node expr) -{ - Node generator = newDotGeneratorName(); - if (!generator) - return null(); - return handler.newAwaitExpression(begin, expr, generator); -} - -template <typename ParseHandler> -typename ParseHandler::Node Parser<ParseHandler>::yieldExpression(InHandling inHandling) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD)); @@ -6620,7 +6604,9 @@ Parser<ParseHandler>::yieldExpression(InHandling inHandling) if (!exprNode) return null(); } - return newYieldExpression(begin, exprNode, kind == PNK_YIELD_STAR); + if (kind == PNK_YIELD_STAR) + return handler.newYieldStarExpression(begin, exprNode); + return handler.newYieldExpression(begin, exprNode); } case NotGenerator: @@ -6697,7 +6683,7 @@ Parser<ParseHandler>::yieldExpression(InHandling inHandling) return null(); } - return newYieldExpression(begin, exprNode); + return handler.newYieldExpression(begin, exprNode); } } @@ -8187,7 +8173,6 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl uint32_t toStringStart = pos().begin; tokenStream.ungetToken(); - GeneratorKind generatorKind = NotGenerator; FunctionAsyncKind asyncKind = SyncFunction; if (next == TOK_ASYNC) { @@ -8200,7 +8185,6 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl if (nextSameLine == TOK_ARROW) { tokenStream.ungetToken(); } else { - generatorKind = StarGenerator; asyncKind = AsyncFunction; } } @@ -8210,7 +8194,7 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl return null(); Node arrowFunc = functionDefinition(toStringStart, pn, inHandling, yieldHandling, nullptr, - Arrow, generatorKind, asyncKind); + Arrow, NotGenerator, asyncKind); if (!arrowFunc) return null(); @@ -8454,7 +8438,7 @@ Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling t if (!kid) return null(); pc->lastAwaitOffset = begin; - return newAwaitExpression(begin, kid); + return handler.newAwaitExpression(begin, kid); } } @@ -8712,7 +8696,7 @@ Parser<ParseHandler>::comprehensionTail(GeneratorKind comprehensionKind) return handler.newArrayPush(begin, bodyExpr); MOZ_ASSERT(comprehensionKind == StarGenerator); - Node yieldExpr = newYieldExpression(begin, bodyExpr); + Node yieldExpr = handler.newYieldExpression(begin, bodyExpr); if (!yieldExpr) return null(); yieldExpr = handler.parenthesize(yieldExpr); @@ -9494,11 +9478,6 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, bool isGenerator = false; bool isAsync = false; - if (ltok == TOK_MUL) { - isGenerator = true; - if (!tokenStream.getToken(<ok)) - return null(); - } if (ltok == TOK_ASYNC) { // AsyncMethod[Yield, Await]: @@ -9526,9 +9505,10 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, } } - if (isAsync && isGenerator) { - error(JSMSG_ASYNC_GENERATOR); - return null(); + if (ltok == TOK_MUL) { + isGenerator = true; + if (!tokenStream.getToken(<ok)) + return null(); } propAtom.set(nullptr); @@ -9950,8 +9930,7 @@ Parser<ParseHandler>::methodDefinition(uint32_t toStringStart, PropertyType prop MOZ_CRASH("Parser: methodDefinition: unexpected property type"); } - GeneratorKind generatorKind = (propType == PropertyType::GeneratorMethod || - propType == PropertyType::AsyncMethod) + GeneratorKind generatorKind = propType == PropertyType::GeneratorMethod ? StarGenerator : NotGenerator; @@ -9959,7 +9938,7 @@ Parser<ParseHandler>::methodDefinition(uint32_t toStringStart, PropertyType prop ? AsyncFunction : SyncFunction; - YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind); + YieldHandling yieldHandling = GetYieldHandling(generatorKind); Node pn = handler.newFunctionExpression(); if (!pn) diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 88d2dad18..243a77083 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -12,6 +12,7 @@ #include "mozilla/Array.h" #include "mozilla/Maybe.h" +#include "jsiter.h" #include "jspubtd.h" #include "frontend/BytecodeCompiler.h" @@ -496,10 +497,6 @@ class ParseContext : public Nestable<ParseContext> return sc_->isFunctionBox() ? sc_->asFunctionBox()->generatorKind() : NotGenerator; } - bool isGenerator() const { - return generatorKind() != NotGenerator; - } - bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; } @@ -512,6 +509,10 @@ class ParseContext : public Nestable<ParseContext> return sc_->isFunctionBox() && sc_->asFunctionBox()->isAsync(); } + bool needsDotGeneratorName() const { + return isStarGenerator() || isLegacyGenerator() || isAsync(); + } + FunctionAsyncKind asyncKind() const { return isAsync() ? AsyncFunction : SyncFunction; } @@ -818,7 +819,9 @@ class ParserBase : public StrictModeGetter // whether it's prohibited due to strictness, JS version, or occurrence // inside a star generator. bool yieldExpressionsSupported() { - return (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()) && !pc->isAsync(); + return (versionNumber() >= JSVERSION_1_7 && !pc->isAsync()) || + pc->isStarGenerator() || + pc->isLegacyGenerator(); } virtual bool strictMode() { return pc->sc()->strict(); } @@ -1108,8 +1111,6 @@ class Parser final : public ParserBase, private JS::AutoGCRooter inline Node newName(PropertyName* name); inline Node newName(PropertyName* name, TokenPos pos); - inline Node newYieldExpression(uint32_t begin, Node expr, bool isYieldStar = false); - inline Node newAwaitExpression(uint32_t begin, Node expr); inline bool abortIfSyntaxParser(); @@ -1196,6 +1197,7 @@ class Parser final : public ParserBase, private JS::AutoGCRooter Node forStatement(YieldHandling yieldHandling); bool forHeadStart(YieldHandling yieldHandling, + IteratorKind iterKind, ParseNodeKind* forHeadKind, Node* forInitialPart, mozilla::Maybe<ParseContext::Scope>& forLetImpliedScope, diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index 013444690..8a5f6c0bd 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -520,7 +520,9 @@ class FunctionBox : public ObjectBox, public SharedContext return hasExtensibleScope() || needsHomeObject() || isDerivedClassConstructor() || - isGenerator(); + isStarGenerator() || + isLegacyGenerator() || + isAsync(); } bool hasExtraBodyVarScope() const { @@ -531,7 +533,7 @@ class FunctionBox : public ObjectBox, public SharedContext bool needsExtraBodyVarEnvironmentRegardlessOfBindings() const { MOZ_ASSERT(hasParameterExprs); - return hasExtensibleScope() || isGenerator(); + return hasExtensibleScope() || needsDotGeneratorName(); } bool isLikelyConstructorWrapper() const { @@ -539,10 +541,21 @@ class FunctionBox : public ObjectBox, public SharedContext } GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); } - bool isGenerator() const { return generatorKind() != NotGenerator; } bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; } bool isStarGenerator() const { return generatorKind() == StarGenerator; } FunctionAsyncKind asyncKind() const { return AsyncKindFromBits(asyncKindBits_); } + + bool needsFinalYield() const { + return isStarGenerator() || isLegacyGenerator() || isAsync(); + } + bool needsDotGeneratorName() const { + return isStarGenerator() || isLegacyGenerator() || isAsync(); + } + + bool needsIteratorResult() const { + return isStarGenerator(); + } + bool isAsync() const { return asyncKind() == AsyncFunction; } bool isArrow() const { return function()->isArrow(); } @@ -560,7 +573,7 @@ class FunctionBox : public ObjectBox, public SharedContext // A generator kind can be set at initialization, or when "yield" is // first seen. In both cases the transition can only happen from // NotGenerator. - MOZ_ASSERT(!isGenerator()); + MOZ_ASSERT(!isStarGenerator() && !isLegacyGenerator()); generatorKindBits_ = GeneratorKindAsBits(kind); } @@ -655,7 +668,11 @@ SharedContext::asModuleContext() inline bool SharedContext::allBindingsClosedOver() { - return bindingsAccessedDynamically() || (isFunctionBox() && asFunctionBox()->isGenerator()); + return bindingsAccessedDynamically() || + (isFunctionBox() && + (asFunctionBox()->isStarGenerator() || + asFunctionBox()->isLegacyGenerator() || + asFunctionBox()->isAsync())); } } // namespace frontend diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index a604b599f..d919f1354 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -301,9 +301,9 @@ class SyntaxParseHandler MOZ_MUST_USE bool addSpreadProperty(Node literal, uint32_t begin, Node inner) { return true; } MOZ_MUST_USE bool addObjectMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; } MOZ_MUST_USE bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op, bool isStatic) { return true; } - Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; } - Node newYieldStarExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; } - Node newAwaitExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; } + Node newYieldExpression(uint32_t begin, Node value) { return NodeGeneric; } + Node newYieldStarExpression(uint32_t begin, Node value) { return NodeGeneric; } + Node newAwaitExpression(uint32_t begin, Node value) { return NodeGeneric; } // Statements diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index b8623d545..e07f8df8a 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -1843,6 +1843,8 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) reflags = RegExpFlag(reflags | StickyFlag); else if (c == 'u' && !(reflags & UnicodeFlag)) reflags = RegExpFlag(reflags | UnicodeFlag); + else if (c == 's' && !(reflags & DotAllFlag)) + reflags = RegExpFlag(reflags | DotAllFlag); else break; getChar(); diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index da3ef7d0d..262fc8cbc 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -2846,10 +2846,9 @@ struct UnmarkGrayTracer : public JS::CallbackTracer * * There is an additional complication for certain kinds of edges that are not * contained explicitly in the source object itself, such as from a weakmap key - * to its value, and from an object being watched by a watchpoint to the - * watchpoint's closure. These "implicit edges" are represented in some other - * container object, such as the weakmap or the watchpoint itself. In these - * cases, calling unmark gray on an object won't find all of its children. + * to its value. These "implicit edges" are represented in some other + * container object, such as the weakmap itself. In these cases, calling unmark + * gray on an object won't find all of its children. * * Handling these implicit edges has two parts: * - A special pass enumerating all of the containers that know about the diff --git a/js/src/gc/Memory.cpp b/js/src/gc/Memory.cpp index 268e1e489..418984057 100644 --- a/js/src/gc/Memory.cpp +++ b/js/src/gc/Memory.cpp @@ -415,8 +415,15 @@ InitMemorySubsystem() static inline void* MapMemoryAt(void* desired, size_t length, int prot = PROT_READ | PROT_WRITE, int flags = MAP_PRIVATE | MAP_ANON, int fd = -1, off_t offset = 0) + +// Solaris manages 64-bit address space in a different manner from every other +// AMD64 operating system, but fortunately the fix is the same one +// required for every operating system on 64-bit SPARC, Itanium, and ARM. +// Most people's intuition failed them here and they thought this couldn't +// possibly be correct on AMD64, but for Solaris/illumos it is. + { -#if defined(__ia64__) || (defined(__sparc64__) && defined(__NetBSD__)) || defined(__aarch64__) +#if defined(__ia64__) || (defined(__sparc64__) && defined(__NetBSD__)) || defined(__aarch64__) || (defined(__sun) && defined(__x86_64__)) MOZ_ASSERT((0xffff800000000000ULL & (uintptr_t(desired) + length - 1)) == 0); #endif void* region = mmap(desired, length, prot, flags, fd, offset); @@ -466,7 +473,7 @@ MapMemory(size_t length, int prot = PROT_READ | PROT_WRITE, return nullptr; } return region; -#elif defined(__aarch64__) +#elif defined(__aarch64__) || (defined(__sun) && defined(__x86_64__)) /* * There might be similar virtual address issue on arm64 which depends on * hardware and kernel configurations. But the work around is slightly @@ -678,7 +685,11 @@ MarkPagesUnused(void* p, size_t size) return false; MOZ_ASSERT(OffsetFromAligned(p, pageSize) == 0); - int result = madvise(p, size, MADV_DONTNEED); +#ifdef XP_SOLARIS + int result = posix_madvise(p, size, POSIX_MADV_DONTNEED); +#else + int result = madvise(p, size, MADV_DONTNEED); +#endif return result != -1; } diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index f5969bc1f..7d665e8eb 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -14,7 +14,6 @@ #include "jsgc.h" #include "jsprf.h" #include "jstypes.h" -#include "jswatchpoint.h" #include "builtin/MapObject.h" #include "frontend/BytecodeCompiler.h" diff --git a/js/src/irregexp/NativeRegExpMacroAssembler.cpp b/js/src/irregexp/NativeRegExpMacroAssembler.cpp index 0fb507297..e17eecb9b 100644 --- a/js/src/irregexp/NativeRegExpMacroAssembler.cpp +++ b/js/src/irregexp/NativeRegExpMacroAssembler.cpp @@ -582,7 +582,7 @@ NativeRegExpMacroAssembler::CheckAtStart(Label* on_at_start) } void -NativeRegExpMacroAssembler::CheckNotAtStart(Label* on_not_at_start) +NativeRegExpMacroAssembler::CheckNotAtStart(int cp_offset, Label* on_not_at_start) { JitSpew(SPEW_PREFIX "CheckNotAtStart"); @@ -673,7 +673,7 @@ NativeRegExpMacroAssembler::CheckGreedyLoop(Label* on_tos_equals_current_positio } void -NativeRegExpMacroAssembler::CheckNotBackReference(int start_reg, Label* on_no_match) +NativeRegExpMacroAssembler::CheckNotBackReference(int start_reg, bool read_backward, Label* on_no_match) { JitSpew(SPEW_PREFIX "CheckNotBackReference(%d)", start_reg); @@ -744,8 +744,8 @@ NativeRegExpMacroAssembler::CheckNotBackReference(int start_reg, Label* on_no_ma } void -NativeRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, Label* on_no_match, - bool unicode) +NativeRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, bool read_backward, + Label* on_no_match, bool unicode) { JitSpew(SPEW_PREFIX "CheckNotBackReferenceIgnoreCase(%d, %d)", start_reg, unicode); diff --git a/js/src/irregexp/NativeRegExpMacroAssembler.h b/js/src/irregexp/NativeRegExpMacroAssembler.h index 7a72e252f..fc582dccf 100644 --- a/js/src/irregexp/NativeRegExpMacroAssembler.h +++ b/js/src/irregexp/NativeRegExpMacroAssembler.h @@ -105,9 +105,10 @@ class MOZ_STACK_CLASS NativeRegExpMacroAssembler final : public RegExpMacroAssem void CheckCharacterGT(char16_t limit, jit::Label* on_greater); void CheckCharacterLT(char16_t limit, jit::Label* on_less); void CheckGreedyLoop(jit::Label* on_tos_equals_current_position); - void CheckNotAtStart(jit::Label* on_not_at_start); - void CheckNotBackReference(int start_reg, jit::Label* on_no_match); - void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match, bool unicode); + void CheckNotAtStart(int cp_offset, jit::Label* on_not_at_start); + void CheckNotBackReference(int start_reg, bool read_backward, jit::Label* on_no_match); + void CheckNotBackReferenceIgnoreCase(int start_reg, bool read_backward, + jit::Label* on_no_match, bool unicode); void CheckNotCharacter(unsigned c, jit::Label* on_not_equal); void CheckNotCharacterAfterAnd(unsigned c, unsigned and_with, jit::Label* on_not_equal); void CheckNotCharacterAfterMinusAnd(char16_t c, char16_t minus, char16_t and_with, diff --git a/js/src/irregexp/RegExpAST.cpp b/js/src/irregexp/RegExpAST.cpp index 8dfd99057..43867c312 100644 --- a/js/src/irregexp/RegExpAST.cpp +++ b/js/src/irregexp/RegExpAST.cpp @@ -250,16 +250,16 @@ RegExpCapture::CaptureRegisters() } // ---------------------------------------------------------------------------- -// RegExpLookahead +// RegExpLookaround Interval -RegExpLookahead::CaptureRegisters() +RegExpLookaround::CaptureRegisters() { return body()->CaptureRegisters(); } bool -RegExpLookahead::IsAnchoredAtStart() +RegExpLookaround::IsAnchoredAtStart() { - return is_positive() && body()->IsAnchoredAtStart(); + return is_positive() && type() == LOOKAHEAD && body()->IsAnchoredAtStart(); } diff --git a/js/src/irregexp/RegExpAST.h b/js/src/irregexp/RegExpAST.h index 7bda6fc7e..6f59842bc 100644 --- a/js/src/irregexp/RegExpAST.h +++ b/js/src/irregexp/RegExpAST.h @@ -360,6 +360,7 @@ class RegExpCapture : public RegExpTree virtual int min_match() { return body_->min_match(); } virtual int max_match() { return body_->max_match(); } RegExpTree* body() { return body_; } + void set_body(RegExpTree* body) { body_ = body; } int index() { return index_; } static int StartRegister(int index) { return index * 2; } static int EndRegister(int index) { return index * 2 + 1; } @@ -369,25 +370,29 @@ class RegExpCapture : public RegExpTree int index_; }; -class RegExpLookahead : public RegExpTree +class RegExpLookaround : public RegExpTree { public: - RegExpLookahead(RegExpTree* body, - bool is_positive, - int capture_count, - int capture_from) + enum Type { LOOKAHEAD, LOOKBEHIND }; + + RegExpLookaround(RegExpTree* body, + bool is_positive, + int capture_count, + int capture_from, + Type type) : body_(body), is_positive_(is_positive), capture_count_(capture_count), - capture_from_(capture_from) + capture_from_(capture_from), + type_(type) {} virtual void* Accept(RegExpVisitor* visitor, void* data); virtual RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success); - virtual RegExpLookahead* AsLookahead(); + virtual RegExpLookaround* AsLookaround(); virtual Interval CaptureRegisters(); - virtual bool IsLookahead(); + virtual bool IsLookaround(); virtual bool IsAnchoredAtStart(); virtual int min_match() { return 0; } virtual int max_match() { return 0; } @@ -395,12 +400,14 @@ class RegExpLookahead : public RegExpTree bool is_positive() { return is_positive_; } int capture_count() { return capture_count_; } int capture_from() { return capture_from_; } + Type type() { return type_; } private: RegExpTree* body_; bool is_positive_; int capture_count_; int capture_from_; + Type type_; }; typedef InfallibleVector<RegExpCapture*, 1> RegExpCaptureVector; @@ -417,8 +424,14 @@ class RegExpBackReference : public RegExpTree RegExpNode* on_success); virtual RegExpBackReference* AsBackReference(); virtual bool IsBackReference(); - virtual int min_match() { return 0; } - virtual int max_match() { return capture_->max_match(); } + virtual int min_match() override { return 0; } + // The capture may not be completely parsed yet, if the reference occurs + // before the capture. In the ordinary case, nothing has been captured yet, + // so the back reference must have the length 0. If the back reference is + // inside a lookbehind, effectively making it a forward reference, we return + virtual int max_match() override { + return capture_->body() ? capture_->max_match() : 0; + } int index() { return capture_->index(); } RegExpCapture* capture() { return capture_; } private: diff --git a/js/src/irregexp/RegExpBytecode.h b/js/src/irregexp/RegExpBytecode.h index f31b78c59..ea3f80b4f 100644 --- a/js/src/irregexp/RegExpBytecode.h +++ b/js/src/irregexp/RegExpBytecode.h @@ -82,16 +82,19 @@ V(CHECK_LT, 35, 8) /* bc8 pad8 uc16 addr32 */ \ V(CHECK_GT, 36, 8) /* bc8 pad8 uc16 addr32 */ \ V(CHECK_NOT_BACK_REF, 37, 8) /* bc8 reg_idx24 addr32 */ \ V(CHECK_NOT_BACK_REF_NO_CASE, 38, 8) /* bc8 reg_idx24 addr32 */ \ -V(CHECK_NOT_REGS_EQUAL, 39, 12) /* bc8 regidx24 reg_idx32 addr32 */ \ -V(CHECK_REGISTER_LT, 40, 12) /* bc8 reg_idx24 value32 addr32 */ \ -V(CHECK_REGISTER_GE, 41, 12) /* bc8 reg_idx24 value32 addr32 */ \ -V(CHECK_REGISTER_EQ_POS, 42, 8) /* bc8 reg_idx24 addr32 */ \ -V(CHECK_AT_START, 43, 8) /* bc8 pad24 addr32 */ \ -V(CHECK_NOT_AT_START, 44, 8) /* bc8 pad24 addr32 */ \ -V(CHECK_GREEDY, 45, 8) /* bc8 pad24 addr32 */ \ -V(ADVANCE_CP_AND_GOTO, 46, 8) /* bc8 offset24 addr32 */ \ -V(SET_CURRENT_POSITION_FROM_END, 47, 4) /* bc8 idx24 */ \ -V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE, 48, 8) /* bc8 reg_idx24 addr32 */ +V(CHECK_NOT_BACK_REF_BACKWARD, 39, 8) /* bc8 reg_idx24 addr32 */ \ +V(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD, 40, 8) /* bc8 reg_idx24 addr32 */ \ +V(CHECK_NOT_REGS_EQUAL, 41, 12) /* bc8 regidx24 reg_idx32 addr32 */ \ +V(CHECK_REGISTER_LT, 42, 12) /* bc8 reg_idx24 value32 addr32 */ \ +V(CHECK_REGISTER_GE, 43, 12) /* bc8 reg_idx24 value32 addr32 */ \ +V(CHECK_REGISTER_EQ_POS, 44, 8) /* bc8 reg_idx24 addr32 */ \ +V(CHECK_AT_START, 45, 8) /* bc8 pad24 addr32 */ \ +V(CHECK_NOT_AT_START, 46, 8) /* bc8 pad24 addr32 */ \ +V(CHECK_GREEDY, 47, 8) /* bc8 pad24 addr32 */ \ +V(ADVANCE_CP_AND_GOTO, 48, 8) /* bc8 offset24 addr32 */ \ +V(SET_CURRENT_POSITION_FROM_END, 49, 4) /* bc8 idx24 */ \ +V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE, 50, 8) /* bc8 reg_idx24 addr32 */ \ +V(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_UNICODE, 51, 8) /* bc8 reg_idx24 addr32 */ #define DECLARE_BYTECODES(name, code, length) \ static const int BC_##name = code; diff --git a/js/src/irregexp/RegExpEngine.cpp b/js/src/irregexp/RegExpEngine.cpp index 4d691a5dc..62f94c3e7 100644 --- a/js/src/irregexp/RegExpEngine.cpp +++ b/js/src/irregexp/RegExpEngine.cpp @@ -721,6 +721,8 @@ ActionNode::EmptyMatchCheck(int start_register, int TextNode::EatsAtLeast(int still_to_find, int budget, bool not_at_start) { + if (read_backward()) + return 0; int answer = Length(); if (answer >= still_to_find) return answer; @@ -736,8 +738,7 @@ TextNode::EatsAtLeast(int still_to_find, int budget, bool not_at_start) int TextNode::GreedyLoopTextLength() { - TextElement elm = elements()[elements().length() - 1]; - return elm.cp_offset() + elm.length(); + return Length(); } RegExpNode* @@ -887,6 +888,8 @@ AssertionNode::FillInBMInfo(int offset, int budget, BoyerMooreLookahead* bm, boo int BackReferenceNode::EatsAtLeast(int still_to_find, int budget, bool not_at_start) { + if (read_backward()) + return 0; if (budget <= 0) return 0; return on_success()->EatsAtLeast(still_to_find, budget - 1, not_at_start); @@ -1578,6 +1581,9 @@ class irregexp::RegExpCompiler current_expansion_factor_ = value; } + bool read_backward() { return read_backward_; } + void set_read_backward(bool value) { read_backward_ = value; } + JSContext* cx() const { return cx_; } LifoAlloc* alloc() const { return alloc_; } @@ -1595,6 +1601,7 @@ class irregexp::RegExpCompiler bool unicode_; bool reg_exp_too_big_; int current_expansion_factor_; + bool read_backward_; FrequencyCollator frequency_collator_; JSContext* cx_; LifoAlloc* alloc_; @@ -1624,6 +1631,7 @@ RegExpCompiler::RegExpCompiler(JSContext* cx, LifoAlloc* alloc, int capture_coun unicode_(unicode), reg_exp_too_big_(false), current_expansion_factor_(1), + read_backward_(false), frequency_collator_(), cx_(cx), alloc_(alloc) @@ -1747,7 +1755,7 @@ irregexp::CompilePattern(JSContext* cx, RegExpShared* shared, RegExpCompileData* // at the start of input. ChoiceNode* first_step_node = alloc.newInfallible<ChoiceNode>(&alloc, 2); RegExpNode* char_class = - alloc.newInfallible<TextNode>(alloc.newInfallible<RegExpCharacterClass>('*'), loop_node); + alloc.newInfallible<TextNode>(alloc.newInfallible<RegExpCharacterClass>('*'), false, loop_node); first_step_node->AddAlternative(GuardedAlternative(captured_body)); first_step_node->AddAlternative(GuardedAlternative(char_class)); node = first_step_node; @@ -1850,19 +1858,19 @@ RegExpAtom::ToNode(RegExpCompiler* compiler, RegExpNode* on_success) TextElementVector* elms = compiler->alloc()->newInfallible<TextElementVector>(*compiler->alloc()); elms->append(TextElement::Atom(this)); - return compiler->alloc()->newInfallible<TextNode>(elms, on_success); + return compiler->alloc()->newInfallible<TextNode>(elms, compiler->read_backward(), on_success); } RegExpNode* RegExpText::ToNode(RegExpCompiler* compiler, RegExpNode* on_success) { - return compiler->alloc()->newInfallible<TextNode>(&elements_, on_success); + return compiler->alloc()->newInfallible<TextNode>(&elements_, compiler->read_backward(), on_success); } RegExpNode* RegExpCharacterClass::ToNode(RegExpCompiler* compiler, RegExpNode* on_success) { - return compiler->alloc()->newInfallible<TextNode>(this, on_success); + return compiler->alloc()->newInfallible<TextNode>(this, compiler->read_backward(), on_success); } RegExpNode* @@ -2003,7 +2011,8 @@ RegExpQuantifier::ToNode(int min, alternation->AddAlternative(GuardedAlternative(body->ToNode(compiler, answer))); } answer = alternation; - if (not_at_start) alternation->set_not_at_start(); + if (not_at_start && !compiler->read_backward()) + alternation->set_not_at_start(); } return answer; } @@ -2015,8 +2024,9 @@ RegExpQuantifier::ToNode(int min, int reg_ctr = needs_counter ? compiler->AllocateRegister() : RegExpCompiler::kNoRegister; - LoopChoiceNode* center = alloc->newInfallible<LoopChoiceNode>(alloc, body->min_match() == 0); - if (not_at_start) + LoopChoiceNode* center = alloc->newInfallible<LoopChoiceNode>(alloc, body->min_match() == 0, + compiler->read_backward()); + if (not_at_start && !compiler->read_backward()) center->set_not_at_start(); RegExpNode* loop_return = needs_counter ? static_cast<RegExpNode*>(ActionNode::IncrementRegister(reg_ctr, center)) @@ -2092,7 +2102,7 @@ RegExpAssertion::ToNode(RegExpCompiler* compiler, CharacterRange::AddClassEscape(alloc, 'n', newline_ranges); RegExpCharacterClass* newline_atom = alloc->newInfallible<RegExpCharacterClass>('n'); TextNode* newline_matcher = - alloc->newInfallible<TextNode>(newline_atom, + alloc->newInfallible<TextNode>(newline_atom, false, ActionNode::PositiveSubmatchSuccess(stack_pointer_register, position_register, 0, // No captures inside. @@ -2124,6 +2134,7 @@ RegExpBackReference::ToNode(RegExpCompiler* compiler, RegExpNode* on_success) { return compiler->alloc()->newInfallible<BackReferenceNode>(RegExpCapture::StartRegister(index()), RegExpCapture::EndRegister(index()), + compiler->read_backward(), on_success); } @@ -2134,7 +2145,7 @@ RegExpEmpty::ToNode(RegExpCompiler* compiler, RegExpNode* on_success) } RegExpNode* -RegExpLookahead::ToNode(RegExpCompiler* compiler, RegExpNode* on_success) +RegExpLookaround::ToNode(RegExpCompiler* compiler, RegExpNode* on_success) { int stack_pointer_register = compiler->AllocateRegister(); int position_register = compiler->AllocateRegister(); @@ -2145,6 +2156,10 @@ RegExpLookahead::ToNode(RegExpCompiler* compiler, RegExpNode* on_success) int register_start = register_of_first_capture + capture_from_ * registers_per_capture; + RegExpNode* result; + bool was_reading_backward = compiler->read_backward(); + compiler->set_read_backward(type() == LOOKBEHIND); + if (is_positive()) { RegExpNode* bodyNode = body()->ToNode(compiler, @@ -2153,37 +2168,39 @@ RegExpLookahead::ToNode(RegExpCompiler* compiler, RegExpNode* on_success) register_count, register_start, on_success)); - return ActionNode::BeginSubmatch(stack_pointer_register, + result = ActionNode::BeginSubmatch(stack_pointer_register, + position_register, + bodyNode); + } else { + // We use a ChoiceNode for a negative lookahead because it has most of + // the characteristics we need. It has the body of the lookahead as its + // first alternative and the expression after the lookahead of the second + // alternative. If the first alternative succeeds then the + // NegativeSubmatchSuccess will unwind the stack including everything the + // choice node set up and backtrack. If the first alternative fails then + // the second alternative is tried, which is exactly the desired result + // for a negative lookahead. The NegativeLookaheadChoiceNode is a special + // ChoiceNode that knows to ignore the first exit when calculating quick + // checks. + LifoAlloc* alloc = compiler->alloc(); + + RegExpNode* success = + alloc->newInfallible<NegativeSubmatchSuccess>(alloc, + stack_pointer_register, + position_register, + register_count, + register_start); + GuardedAlternative body_alt(body()->ToNode(compiler, success)); + + ChoiceNode* choice_node = + alloc->newInfallible<NegativeLookaheadChoiceNode>(alloc, body_alt, GuardedAlternative(on_success)); + + result = ActionNode::BeginSubmatch(stack_pointer_register, position_register, - bodyNode); - } - - // We use a ChoiceNode for a negative lookahead because it has most of - // the characteristics we need. It has the body of the lookahead as its - // first alternative and the expression after the lookahead of the second - // alternative. If the first alternative succeeds then the - // NegativeSubmatchSuccess will unwind the stack including everything the - // choice node set up and backtrack. If the first alternative fails then - // the second alternative is tried, which is exactly the desired result - // for a negative lookahead. The NegativeLookaheadChoiceNode is a special - // ChoiceNode that knows to ignore the first exit when calculating quick - // checks. - LifoAlloc* alloc = compiler->alloc(); - - RegExpNode* success = - alloc->newInfallible<NegativeSubmatchSuccess>(alloc, - stack_pointer_register, - position_register, - register_count, - register_start); - GuardedAlternative body_alt(body()->ToNode(compiler, success)); - - ChoiceNode* choice_node = - alloc->newInfallible<NegativeLookaheadChoiceNode>(alloc, body_alt, GuardedAlternative(on_success)); - - return ActionNode::BeginSubmatch(stack_pointer_register, - position_register, - choice_node); + choice_node); + } + compiler->set_read_backward(was_reading_backward); + return result; } RegExpNode* @@ -2198,8 +2215,14 @@ RegExpCapture::ToNode(RegExpTree* body, RegExpCompiler* compiler, RegExpNode* on_success) { + MOZ_ASSERT(body); int start_reg = RegExpCapture::StartRegister(index); int end_reg = RegExpCapture::EndRegister(index); + if (compiler->read_backward()) { + // std::swap(start_reg, end_reg); + start_reg = RegExpCapture::EndRegister(index); + end_reg = RegExpCapture::StartRegister(index); + } RegExpNode* store_end = ActionNode::StorePosition(end_reg, true, on_success); RegExpNode* body_node = body->ToNode(compiler, store_end); return ActionNode::StorePosition(start_reg, true, body_node); @@ -2210,8 +2233,15 @@ RegExpAlternative::ToNode(RegExpCompiler* compiler, RegExpNode* on_success) { const RegExpTreeVector& children = nodes(); RegExpNode* current = on_success; - for (int i = children.length() - 1; i >= 0; i--) - current = children[i]->ToNode(compiler, current); + if (compiler->read_backward()) { + for (int i = 0; i < children.length(); i++) { + current = children[i]->ToNode(compiler, current); + } + } else { + for (int i = children.length() - 1; i >= 0; i--) { + current = children[i]->ToNode(compiler, current); + } + } return current; } @@ -2764,7 +2794,6 @@ Trace::InvalidateCurrentCharacter() void Trace::AdvanceCurrentPositionInTrace(int by, RegExpCompiler* compiler) { - MOZ_ASSERT(by > 0); // We don't have an instruction for shifting the current character register // down or for using a shifted value for anything so lets just forget that // we preloaded any characters into it. @@ -3109,9 +3138,9 @@ AssertionNode::Emit(RegExpCompiler* compiler, Trace* trace) return; } if (trace->at_start() == Trace::UNKNOWN) { - assembler->CheckNotAtStart(trace->backtrack()); + assembler->CheckNotAtStart(trace->cp_offset(), trace->backtrack()); Trace at_start_trace = *trace; - at_start_trace.set_at_start(true); + at_start_trace.set_at_start(Trace::TRUE_VALUE); on_success()->Emit(compiler, &at_start_trace); return; } @@ -3814,9 +3843,10 @@ TextNode::TextEmitPass(RegExpCompiler* compiler, jit::Label* backtrack = trace->backtrack(); QuickCheckDetails* quick_check = trace->quick_check_performed(); int element_count = elements().length(); + int backward_offset = read_backward() ? -Length() : 0; for (int i = preloaded ? 0 : element_count - 1; i >= 0; i--) { TextElement elm = elements()[i]; - int cp_offset = trace->cp_offset() + elm.cp_offset(); + int cp_offset = trace->cp_offset() + elm.cp_offset() + backward_offset; if (elm.text_type() == TextElement::ATOM) { const CharacterVector& quarks = elm.atom()->data(); for (int j = preloaded ? 0 : quarks.length() - 1; j >= 0; j--) { @@ -3844,11 +3874,12 @@ TextNode::TextEmitPass(RegExpCompiler* compiler, break; } if (emit_function != nullptr) { + bool bounds_check = *checked_up_to < cp_offset + j || read_backward(); bool bound_checked = emit_function(compiler, quarks[j], backtrack, cp_offset + j, - *checked_up_to < cp_offset + j, + bounds_check, preloaded); if (bound_checked) UpdateBoundsCheck(cp_offset + j, checked_up_to); } @@ -3859,13 +3890,14 @@ TextNode::TextEmitPass(RegExpCompiler* compiler, if (first_element_checked && i == 0) continue; if (DeterminedAlready(quick_check, elm.cp_offset())) continue; RegExpCharacterClass* cc = elm.char_class(); + bool bounds_check = *checked_up_to < cp_offset || read_backward(); EmitCharClass(alloc(), assembler, cc, ascii, backtrack, cp_offset, - *checked_up_to < cp_offset, + bounds_check, preloaded); UpdateBoundsCheck(cp_offset, checked_up_to); } @@ -3945,8 +3977,11 @@ TextNode::Emit(RegExpCompiler* compiler, Trace* trace) } Trace successor_trace(*trace); - successor_trace.set_at_start(false); - successor_trace.AdvanceCurrentPositionInTrace(Length(), compiler); + // If we advance backward, we may end up at the start. + successor_trace.AdvanceCurrentPositionInTrace( + read_backward() ? -Length() : Length(), compiler); + successor_trace.set_at_start(read_backward() ? Trace::UNKNOWN + : Trace::FALSE_VALUE); RecursionCheck rc(compiler); on_success()->Emit(compiler, &successor_trace); } @@ -4118,6 +4153,8 @@ ChoiceNode::CalculatePreloadCharacters(RegExpCompiler* compiler, int eats_at_lea RegExpNode* TextNode::GetSuccessorOfOmnivorousTextNode(RegExpCompiler* compiler) { + if (read_backward()) return NULL; + if (elements().length() != 1) return nullptr; @@ -4165,7 +4202,7 @@ ChoiceNode::GreedyLoopTextLengthForAlternative(GuardedAlternative* alternative) SeqRegExpNode* seq_node = static_cast<SeqRegExpNode*>(node); node = seq_node->on_success(); } - return length; + return read_backward() ? -length : length; } // Creates a list of AlternativeGenerations. If the list has a reasonable @@ -4240,7 +4277,7 @@ ChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace) jit::Label greedy_loop_label; Trace counter_backtrack_trace; counter_backtrack_trace.set_backtrack(&greedy_loop_label); - if (not_at_start()) counter_backtrack_trace.set_at_start(false); + if (not_at_start()) counter_backtrack_trace.set_at_start(Trace::FALSE_VALUE); if (choice_count > 1 && text_length != kNodeIsTooComplexForGreedyLoops) { // Here we have special handling for greedy loops containing only text nodes @@ -4256,7 +4293,7 @@ ChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace) current_trace = &counter_backtrack_trace; jit::Label greedy_match_failed; Trace greedy_match_trace; - if (not_at_start()) greedy_match_trace.set_at_start(false); + if (not_at_start()) greedy_match_trace.set_at_start(Trace::FALSE_VALUE); greedy_match_trace.set_backtrack(&greedy_match_failed); jit::Label loop_label; macro_assembler->Bind(&loop_label); @@ -4605,11 +4642,14 @@ BackReferenceNode::Emit(RegExpCompiler* compiler, Trace* trace) MOZ_ASSERT(start_reg_ + 1 == end_reg_); if (compiler->ignore_case()) { assembler->CheckNotBackReferenceIgnoreCase(start_reg_, + read_backward(), trace->backtrack(), compiler->unicode()); } else { - assembler->CheckNotBackReference(start_reg_, trace->backtrack()); + assembler->CheckNotBackReference(start_reg_, read_backward(), trace->backtrack()); } + // We are going to advance backward, so we may end up at the start. + if (read_backward()) trace->set_at_start(Trace::UNKNOWN); on_success()->Emit(compiler, trace); } @@ -4977,7 +5017,6 @@ QuickCheckDetails::Clear() void QuickCheckDetails::Advance(int by, bool ascii) { - MOZ_ASSERT(by >= 0); if (by >= characters_) { Clear(); return; diff --git a/js/src/irregexp/RegExpEngine.h b/js/src/irregexp/RegExpEngine.h index 1a8fd4b22..c4409dcca 100644 --- a/js/src/irregexp/RegExpEngine.h +++ b/js/src/irregexp/RegExpEngine.h @@ -119,7 +119,7 @@ InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* chars, size_t VISIT(Atom) \ VISIT(Quantifier) \ VISIT(Capture) \ - VISIT(Lookahead) \ + VISIT(Lookaround) \ VISIT(BackReference) \ VISIT(Empty) \ VISIT(Text) @@ -763,15 +763,19 @@ class TextNode : public SeqRegExpNode { public: TextNode(TextElementVector* elements, + bool read_backward, RegExpNode* on_success) : SeqRegExpNode(on_success), - elements_(elements) + elements_(elements), + read_backward_(read_backward) {} TextNode(RegExpCharacterClass* that, + bool read_backward, RegExpNode* on_success) : SeqRegExpNode(on_success), - elements_(alloc()->newInfallible<TextElementVector>(*alloc())) + elements_(alloc()->newInfallible<TextElementVector>(*alloc())), + read_backward_(read_backward) { elements_->append(TextElement::CharClass(that)); } @@ -784,6 +788,7 @@ class TextNode : public SeqRegExpNode int characters_filled_in, bool not_at_start); TextElementVector& elements() { return *elements_; } + bool read_backward() { return read_backward_; } void MakeCaseIndependent(bool is_ascii, bool unicode); virtual int GreedyLoopTextLength(); virtual RegExpNode* GetSuccessorOfOmnivorousTextNode( @@ -814,6 +819,7 @@ class TextNode : public SeqRegExpNode int* checked_up_to); int Length(); TextElementVector* elements_; + bool read_backward_; }; class AssertionNode : public SeqRegExpNode @@ -882,15 +888,18 @@ class BackReferenceNode : public SeqRegExpNode public: BackReferenceNode(int start_reg, int end_reg, + bool read_backward, RegExpNode* on_success) : SeqRegExpNode(on_success), start_reg_(start_reg), - end_reg_(end_reg) + end_reg_(end_reg), + read_backward_(read_backward) {} virtual void Accept(NodeVisitor* visitor); int start_register() { return start_reg_; } int end_register() { return end_reg_; } + bool read_backward() { return read_backward_; } virtual void Emit(RegExpCompiler* compiler, Trace* trace); virtual int EatsAtLeast(int still_to_find, int recursion_depth, @@ -909,6 +918,7 @@ class BackReferenceNode : public SeqRegExpNode private: int start_reg_; int end_reg_; + bool read_backward_; }; class EndNode : public RegExpNode @@ -1053,6 +1063,7 @@ class ChoiceNode : public RegExpNode void set_being_calculated(bool b) { being_calculated_ = b; } virtual bool try_to_emit_quick_check_for_alternative(int i) { return true; } virtual RegExpNode* FilterASCII(int depth, bool ignore_case, bool unicode); + virtual bool read_backward() { return false; } protected: int GreedyLoopTextLengthForAlternative(GuardedAlternative* alternative); @@ -1111,11 +1122,13 @@ class NegativeLookaheadChoiceNode : public ChoiceNode class LoopChoiceNode : public ChoiceNode { public: - explicit LoopChoiceNode(LifoAlloc* alloc, bool body_can_be_zero_length) + explicit LoopChoiceNode(LifoAlloc* alloc, bool body_can_be_zero_length, + bool read_backward) : ChoiceNode(alloc, 2), loop_node_(nullptr), continue_node_(nullptr), - body_can_be_zero_length_(body_can_be_zero_length) + body_can_be_zero_length_(body_can_be_zero_length), + read_backward_(read_backward) {} void AddLoopAlternative(GuardedAlternative alt); @@ -1133,6 +1146,7 @@ class LoopChoiceNode : public ChoiceNode RegExpNode* loop_node() { return loop_node_; } RegExpNode* continue_node() { return continue_node_; } bool body_can_be_zero_length() { return body_can_be_zero_length_; } + virtual bool read_backward() { return read_backward_; } virtual void Accept(NodeVisitor* visitor); virtual RegExpNode* FilterASCII(int depth, bool ignore_case, bool unicode); @@ -1147,6 +1161,7 @@ class LoopChoiceNode : public ChoiceNode RegExpNode* loop_node_; RegExpNode* continue_node_; bool body_can_be_zero_length_; + bool read_backward_; }; // Improve the speed that we scan for an initial point where a non-anchored @@ -1422,8 +1437,8 @@ class Trace } TriBool at_start() { return at_start_; } - void set_at_start(bool at_start) { - at_start_ = at_start ? TRUE_VALUE : FALSE_VALUE; + void set_at_start(TriBool at_start) { + at_start_ = at_start; } jit::Label* backtrack() { return backtrack_; } jit::Label* loop_label() { return loop_label_; } diff --git a/js/src/irregexp/RegExpInterpreter.cpp b/js/src/irregexp/RegExpInterpreter.cpp index 7fd2d983a..d09b4671e 100644 --- a/js/src/irregexp/RegExpInterpreter.cpp +++ b/js/src/irregexp/RegExpInterpreter.cpp @@ -222,8 +222,8 @@ irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* cha } break; BYTECODE(LOAD_CURRENT_CHAR) { - size_t pos = current + (insn >> BYTECODE_SHIFT); - if (pos >= length) { + int pos = current + (insn >> BYTECODE_SHIFT); + if (pos >= (int)length || pos < 0) { pc = byteCode + Load32Aligned(pc + 4); } else { current_char = chars[pos]; @@ -238,8 +238,8 @@ irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* cha break; } BYTECODE(LOAD_2_CURRENT_CHARS) { - size_t pos = current + (insn >> BYTECODE_SHIFT); - if (pos + 2 > length) { + int pos = current + (insn >> BYTECODE_SHIFT); + if (pos + 2 > (int)length || pos < 0) { pc = byteCode + Load32Aligned(pc + 4); } else { CharT next = chars[pos + 1]; @@ -425,6 +425,30 @@ irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* cha pc += BC_CHECK_NOT_BACK_REF_LENGTH; break; } + BYTECODE(CHECK_NOT_BACK_REF_BACKWARD) { + int from = registers[insn >> BYTECODE_SHIFT]; + int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from; + if (from < 0 || len <= 0) { + pc += BC_CHECK_NOT_BACK_REF_BACKWARD_LENGTH; + break; + } + if (int(current) - len < 0) { + pc = byteCode + Load32Aligned(pc + 4); + break; + } else { + int i; + for (i = 0; i < len; i++) { + if (chars[from + i] != chars[int(current) - len + i]) { + pc = byteCode + Load32Aligned(pc + 4); + break; + } + } + if (i < len) break; + current -= len; + } + pc += BC_CHECK_NOT_BACK_REF_BACKWARD_LENGTH; + break; + } BYTECODE(CHECK_NOT_BACK_REF_NO_CASE) { int from = registers[insn >> BYTECODE_SHIFT]; int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from; @@ -465,6 +489,46 @@ irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* cha } break; } + BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD) { + int from = registers[insn >> BYTECODE_SHIFT]; + int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from; + if (from < 0 || len <= 0) { + pc += BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_LENGTH; + break; + } + if (int(current) - len < 0) { + pc = byteCode + Load32Aligned(pc + 4); + break; + } + if (CaseInsensitiveCompareStrings(chars + from, chars + int(current) - len, len * sizeof(CharT))) { + current -= len; + pc += BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_LENGTH; + } else { + pc = byteCode + Load32Aligned(pc + 4); + } + break; + + } + BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_UNICODE) { + int from = registers[insn >> BYTECODE_SHIFT]; + int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from; + if (from < 0 || len <= 0) { + pc += BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_LENGTH; + break; + } + if (int(current) - len < 0) { + pc = byteCode + Load32Aligned(pc + 4); + break; + } + if (CaseInsensitiveCompareUCStrings(chars + from, chars + int(current) - len, len * sizeof(CharT))) { + current -= len; + pc += BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_LENGTH; + } else { + pc = byteCode + Load32Aligned(pc + 4); + } + break; + + } BYTECODE(CHECK_AT_START) if (current == 0) pc = byteCode + Load32Aligned(pc + 4); @@ -472,7 +536,7 @@ irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* cha pc += BC_CHECK_AT_START_LENGTH; break; BYTECODE(CHECK_NOT_AT_START) - if (current == 0) + if (current + (insn >> BYTECODE_SHIFT) == 0) pc += BC_CHECK_NOT_AT_START_LENGTH; else pc = byteCode + Load32Aligned(pc + 4); diff --git a/js/src/irregexp/RegExpMacroAssembler.cpp b/js/src/irregexp/RegExpMacroAssembler.cpp index d66d0d204..6b1ceba8a 100644 --- a/js/src/irregexp/RegExpMacroAssembler.cpp +++ b/js/src/irregexp/RegExpMacroAssembler.cpp @@ -226,32 +226,37 @@ InterpretedRegExpMacroAssembler::CheckGreedyLoop(jit::Label* on_tos_equals_curre } void -InterpretedRegExpMacroAssembler::CheckNotAtStart(jit::Label* on_not_at_start) +InterpretedRegExpMacroAssembler::CheckNotAtStart(int cp_offset, jit::Label* on_not_at_start) { - Emit(BC_CHECK_NOT_AT_START, 0); + Emit(BC_CHECK_NOT_AT_START, cp_offset); EmitOrLink(on_not_at_start); } void -InterpretedRegExpMacroAssembler::CheckNotBackReference(int start_reg, jit::Label* on_no_match) +InterpretedRegExpMacroAssembler::CheckNotBackReference(int start_reg, bool read_backward, + jit::Label* on_no_match) { MOZ_ASSERT(start_reg >= 0); MOZ_ASSERT(start_reg <= kMaxRegister); - Emit(BC_CHECK_NOT_BACK_REF, start_reg); + Emit(read_backward ? BC_CHECK_NOT_BACK_REF_BACKWARD : BC_CHECK_NOT_BACK_REF, + start_reg); EmitOrLink(on_no_match); } void InterpretedRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, + bool read_backward, jit::Label* on_no_match, bool unicode) { MOZ_ASSERT(start_reg >= 0); MOZ_ASSERT(start_reg <= kMaxRegister); if (unicode) - Emit(BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE, start_reg); + Emit(read_backward ? BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD_UNICODE : BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE, + start_reg); else - Emit(BC_CHECK_NOT_BACK_REF_NO_CASE, start_reg); + Emit(read_backward ? BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD : BC_CHECK_NOT_BACK_REF_NO_CASE, + start_reg); EmitOrLink(on_no_match); } diff --git a/js/src/irregexp/RegExpMacroAssembler.h b/js/src/irregexp/RegExpMacroAssembler.h index dca2edf90..c5def92f2 100644 --- a/js/src/irregexp/RegExpMacroAssembler.h +++ b/js/src/irregexp/RegExpMacroAssembler.h @@ -110,10 +110,10 @@ class MOZ_STACK_CLASS RegExpMacroAssembler virtual void CheckCharacterGT(char16_t limit, jit::Label* on_greater) = 0; virtual void CheckCharacterLT(char16_t limit, jit::Label* on_less) = 0; virtual void CheckGreedyLoop(jit::Label* on_tos_equals_current_position) = 0; - virtual void CheckNotAtStart(jit::Label* on_not_at_start) = 0; - virtual void CheckNotBackReference(int start_reg, jit::Label* on_no_match) = 0; - virtual void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match, - bool unicode) = 0; + virtual void CheckNotAtStart(int cp_offset, jit::Label* on_not_at_start) = 0; + virtual void CheckNotBackReference(int start_reg, bool read_backward, jit::Label* on_no_match) = 0; + virtual void CheckNotBackReferenceIgnoreCase(int start_reg, bool read_backward, + jit::Label* on_no_match, bool unicode) = 0; // Check the current character for a match with a literal character. If we // fail to match then goto the on_failure label. End of input always @@ -245,9 +245,10 @@ class MOZ_STACK_CLASS InterpretedRegExpMacroAssembler final : public RegExpMacro void CheckCharacterGT(char16_t limit, jit::Label* on_greater); void CheckCharacterLT(char16_t limit, jit::Label* on_less); void CheckGreedyLoop(jit::Label* on_tos_equals_current_position); - void CheckNotAtStart(jit::Label* on_not_at_start); - void CheckNotBackReference(int start_reg, jit::Label* on_no_match); - void CheckNotBackReferenceIgnoreCase(int start_reg, jit::Label* on_no_match, bool unicode); + void CheckNotAtStart(int cp_offset, jit::Label* on_not_at_start); + void CheckNotBackReference(int start_reg, bool read_backward, jit::Label* on_no_match); + void CheckNotBackReferenceIgnoreCase(int start_reg, bool read_backward, + jit::Label* on_no_match, bool unicode); void CheckNotCharacter(unsigned c, jit::Label* on_not_equal); void CheckNotCharacterAfterAnd(unsigned c, unsigned and_with, jit::Label* on_not_equal); void CheckNotCharacterAfterMinusAnd(char16_t c, char16_t minus, char16_t and_with, diff --git a/js/src/irregexp/RegExpParser.cpp b/js/src/irregexp/RegExpParser.cpp index 8bd88047a..1ad044e8e 100644 --- a/js/src/irregexp/RegExpParser.cpp +++ b/js/src/irregexp/RegExpParser.cpp @@ -222,11 +222,12 @@ RegExpBuilder::AddQuantifierToAtom(int min, int max, template <typename CharT> RegExpParser<CharT>::RegExpParser(frontend::TokenStream& ts, LifoAlloc* alloc, const CharT* chars, const CharT* end, bool multiline_mode, - bool unicode, bool ignore_case) + bool unicode, bool ignore_case, bool dotall) : ts(ts), alloc(alloc), captures_(nullptr), next_pos_(chars), + captures_started_(0), end_(end), current_(kEndMarker), capture_count_(0), @@ -234,6 +235,7 @@ RegExpParser<CharT>::RegExpParser(frontend::TokenStream& ts, LifoAlloc* alloc, multiline_(multiline_mode), unicode_(unicode), ignore_case_(ignore_case), + dotall_(dotall), simple_(false), contains_anchor_(false), is_scanned_for_captures_(false) @@ -418,7 +420,8 @@ RangeAtom(LifoAlloc* alloc, char16_t from, char16_t to) static inline RegExpTree* NegativeLookahead(LifoAlloc* alloc, char16_t from, char16_t to) { - return alloc->newInfallible<RegExpLookahead>(RangeAtom(alloc, from, to), false, 0, 0); + return alloc->newInfallible<RegExpLookaround>(RangeAtom(alloc, from, to), false, + 0, 0, RegExpLookaround::LOOKAHEAD); } static bool @@ -1213,6 +1216,38 @@ RegExpParser<CharT>::ParseBackReferenceIndex(int* index_out) return true; } +template <typename CharT> +RegExpCapture* +RegExpParser<CharT>::GetCapture(int index) { + // The index for the capture groups are one-based. Its index in the list is + // zero-based. + int known_captures = + is_scanned_for_captures_ ? capture_count_ : captures_started_; + MOZ_ASSERT(index <= known_captures); + if (captures_ == NULL) { + captures_ = alloc->newInfallible<RegExpCaptureVector>(*alloc); + } + while ((int)captures_->length() < known_captures) { + RegExpCapture* capture = alloc->newInfallible<RegExpCapture>(nullptr, captures_->length() + 1); + captures_->append(capture); + } + return (*captures_)[index - 1]; +} + + +template <typename CharT> +bool +RegExpParser<CharT>::RegExpParserState::IsInsideCaptureGroup(int index) { + for (RegExpParserState* s = this; s != NULL; s = s->previous_state()) { + if (s->group_type() != CAPTURE) continue; + // Return true if we found the matching capture index. + if (index == s->capture_index()) return true; + // Abort if index is larger than what has been parsed up till this state. + if (index > s->capture_index()) return false; + } + return false; +} + // QuantifierPrefix :: // { DecimalDigits } // { DecimalDigits , } @@ -1350,7 +1385,7 @@ UnicodeEverythingAtom(LifoAlloc* alloc) { RegExpBuilder* builder = alloc->newInfallible<RegExpBuilder>(alloc); - // everything except \x0a, \x0d, \u2028 and \u2029 + // Everything except \x0a, \x0d, \u2028 and \u2029 CharacterRangeVector* ranges = alloc->newInfallible<CharacterRangeVector>(*alloc); ranges->append(CharacterRange::Range(0x0, 0x09)); @@ -1380,6 +1415,38 @@ UnicodeEverythingAtom(LifoAlloc* alloc) return builder->ToRegExp(); } +static inline RegExpTree* +UnicodeDotAllAtom(LifoAlloc* alloc) +{ + RegExpBuilder* builder = alloc->newInfallible<RegExpBuilder>(alloc); + + // Full range excluding surrogates because /s was specified + + CharacterRangeVector* ranges = alloc->newInfallible<CharacterRangeVector>(*alloc); + ranges->append(CharacterRange::Range(0x0, unicode::LeadSurrogateMin - 1)); + ranges->append(CharacterRange::Range(unicode::TrailSurrogateMax + 1, unicode::UTF16Max)); + builder->AddAtom(alloc->newInfallible<RegExpCharacterClass>(ranges, false)); + + builder->NewAlternative(); + + builder->AddAtom(RangeAtom(alloc, unicode::LeadSurrogateMin, unicode::LeadSurrogateMax)); + builder->AddAtom(NegativeLookahead(alloc, unicode::TrailSurrogateMin, + unicode::TrailSurrogateMax)); + + builder->NewAlternative(); + + builder->AddAssertion(alloc->newInfallible<RegExpAssertion>( + RegExpAssertion::NOT_AFTER_LEAD_SURROGATE)); + builder->AddAtom(RangeAtom(alloc, unicode::TrailSurrogateMin, unicode::TrailSurrogateMax)); + + builder->NewAlternative(); + + builder->AddAtom(RangeAtom(alloc, unicode::LeadSurrogateMin, unicode::LeadSurrogateMax)); + builder->AddAtom(RangeAtom(alloc, unicode::TrailSurrogateMin, unicode::TrailSurrogateMax)); + + return builder->ToRegExp(); +} + RegExpTree* UnicodeCharacterClassEscapeAtom(LifoAlloc* alloc, char16_t char_class, bool ignore_case) { @@ -1423,24 +1490,24 @@ RegExpTree* RegExpParser<CharT>::ParseDisjunction() { // Used to store current state while parsing subexpressions. - RegExpParserState initial_state(alloc, nullptr, INITIAL, 0); - RegExpParserState* stored_state = &initial_state; + RegExpParserState initial_state(alloc, nullptr, INITIAL, RegExpLookaround::LOOKAHEAD, 0); + RegExpParserState* state = &initial_state; // Cache the builder in a local variable for quick access. RegExpBuilder* builder = initial_state.builder(); while (true) { switch (current()) { case kEndMarker: - if (stored_state->IsSubexpression()) { + if (state->IsSubexpression()) { // Inside a parenthesized group when hitting end of input. return ReportError(JSMSG_MISSING_PAREN); } - MOZ_ASSERT(INITIAL == stored_state->group_type()); + MOZ_ASSERT(INITIAL == state->group_type()); // Parsing completed successfully. return builder->ToRegExp(); case ')': { - if (!stored_state->IsSubexpression()) + if (!state->IsSubexpression()) return ReportError(JSMSG_UNMATCHED_RIGHT_PAREN); - MOZ_ASSERT(INITIAL != stored_state->group_type()); + MOZ_ASSERT(INITIAL != state->group_type()); Advance(); // End disjunction parsing and convert builder content to new single @@ -1449,29 +1516,30 @@ RegExpParser<CharT>::ParseDisjunction() int end_capture_index = captures_started(); - int capture_index = stored_state->capture_index(); - SubexpressionType group_type = stored_state->group_type(); - - // Restore previous state. - stored_state = stored_state->previous_state(); - builder = stored_state->builder(); + int capture_index = state->capture_index(); + SubexpressionType group_type = state->group_type(); // Build result of subexpression. if (group_type == CAPTURE) { - RegExpCapture* capture = alloc->newInfallible<RegExpCapture>(body, capture_index); - (*captures_)[capture_index - 1] = capture; + RegExpCapture* capture = GetCapture(capture_index); + capture->set_body(body); body = capture; } else if (group_type != GROUPING) { - MOZ_ASSERT(group_type == POSITIVE_LOOKAHEAD || - group_type == NEGATIVE_LOOKAHEAD); - bool is_positive = (group_type == POSITIVE_LOOKAHEAD); - body = alloc->newInfallible<RegExpLookahead>(body, + MOZ_ASSERT(group_type == POSITIVE_LOOKAROUND || + group_type == NEGATIVE_LOOKAROUND); + bool is_positive = (group_type == POSITIVE_LOOKAROUND); + body = alloc->newInfallible<RegExpLookaround>(body, is_positive, end_capture_index - capture_index, - capture_index); + capture_index, + state->lookaround_type()); } + + // Restore previous state. + state = state->previous_state(); + builder = state->builder(); builder->AddAtom(body); - if (unicode_ && (group_type == POSITIVE_LOOKAHEAD || group_type == NEGATIVE_LOOKAHEAD)) + if (unicode_ && (group_type == POSITIVE_LOOKAROUND || group_type == NEGATIVE_LOOKAROUND)) continue; // For compatability with JSC and ES3, we allow quantifiers after // lookaheads, and break in all cases. @@ -1506,19 +1574,32 @@ RegExpParser<CharT>::ParseDisjunction() } case '.': { Advance(); - // everything except \x0a, \x0d, \u2028 and \u2029 + if (unicode_) { - builder->AddAtom(UnicodeEverythingAtom(alloc)); + if (dotall_) { + // Everything + builder->AddAtom(UnicodeDotAllAtom(alloc)); + } else { + // Everything except \x0a, \x0d, \u2028 and \u2029 + builder->AddAtom(UnicodeEverythingAtom(alloc)); + } break; } CharacterRangeVector* ranges = alloc->newInfallible<CharacterRangeVector>(*alloc); - CharacterRange::AddClassEscape(alloc, '.', ranges); + if (dotall_) { + // Everything + CharacterRange::AddClassEscape(alloc, '*', ranges); + } else { + // Everything except \x0a, \x0d, \u2028 and \u2029 + CharacterRange::AddClassEscape(alloc, '.', ranges); + } RegExpTree* atom = alloc->newInfallible<RegExpCharacterClass>(ranges, false); builder->AddAtom(atom); break; } case '(': { SubexpressionType subexpr_type = CAPTURE; + RegExpLookaround::Type lookaround_type = state->lookaround_type(); Advance(); if (current() == '?') { switch (Next()) { @@ -1526,26 +1607,39 @@ RegExpParser<CharT>::ParseDisjunction() subexpr_type = GROUPING; break; case '=': - subexpr_type = POSITIVE_LOOKAHEAD; + lookaround_type = RegExpLookaround::LOOKAHEAD; + subexpr_type = POSITIVE_LOOKAROUND; break; case '!': - subexpr_type = NEGATIVE_LOOKAHEAD; + lookaround_type = RegExpLookaround::LOOKAHEAD; + subexpr_type = NEGATIVE_LOOKAROUND; break; + case '<': + Advance(); + lookaround_type = RegExpLookaround::LOOKBEHIND; + if (Next() == '=') { + subexpr_type = POSITIVE_LOOKAROUND; + break; + } else if (Next() == '!') { + subexpr_type = NEGATIVE_LOOKAROUND; + break; + } + // We didn't get a positive or negative after '<'. + // That's an error. + return ReportError(JSMSG_INVALID_GROUP); default: return ReportError(JSMSG_INVALID_GROUP); } Advance(2); } else { - if (captures_ == nullptr) - captures_ = alloc->newInfallible<RegExpCaptureVector>(*alloc); if (captures_started() >= kMaxCaptures) return ReportError(JSMSG_TOO_MANY_PARENS); - captures_->append((RegExpCapture*) nullptr); + captures_started_++; } // Store current state and begin new disjunction parsing. - stored_state = alloc->newInfallible<RegExpParserState>(alloc, stored_state, subexpr_type, - captures_started()); - builder = stored_state->builder(); + state = alloc->newInfallible<RegExpParserState>(alloc, state, subexpr_type, + lookaround_type, captures_started_); + builder = state->builder(); continue; } case '[': { @@ -1600,19 +1694,18 @@ RegExpParser<CharT>::ParseDisjunction() case '7': case '8': case '9': { int index = 0; if (ParseBackReferenceIndex(&index)) { - RegExpCapture* capture = nullptr; - if (captures_ != nullptr && index <= (int) captures_->length()) { - capture = (*captures_)[index - 1]; - } - if (capture == nullptr) { - builder->AddEmpty(); - break; + if (state->IsInsideCaptureGroup(index)) { + // The backreference is inside the capture group it refers to. + // Nothing can possibly have been captured yet. + builder->AddEmpty(); + } else { + RegExpCapture* capture = GetCapture(index); + RegExpTree* atom = alloc->newInfallible<RegExpBackReference>(capture); + if (unicode_) + builder->AddAtom(UnicodeBackReferenceAtom(alloc, atom)); + else + builder->AddAtom(atom); } - RegExpTree* atom = alloc->newInfallible<RegExpBackReference>(capture); - if (unicode_) - builder->AddAtom(UnicodeBackReferenceAtom(alloc, atom)); - else - builder->AddAtom(atom); break; } if (unicode_) @@ -1832,7 +1925,7 @@ template <typename CharT> static bool ParsePattern(frontend::TokenStream& ts, LifoAlloc& alloc, const CharT* chars, size_t length, bool multiline, bool match_only, bool unicode, bool ignore_case, - bool global, bool sticky, RegExpCompileData* data) + bool global, bool sticky, bool dotall, RegExpCompileData* data) { if (match_only) { // Try to strip a leading '.*' from the RegExp, but only if it is not @@ -1859,7 +1952,7 @@ ParsePattern(frontend::TokenStream& ts, LifoAlloc& alloc, const CharT* chars, si } } - RegExpParser<CharT> parser(ts, &alloc, chars, chars + length, multiline, unicode, ignore_case); + RegExpParser<CharT> parser(ts, &alloc, chars, chars + length, multiline, unicode, ignore_case, dotall); data->tree = parser.ParsePattern(); if (!data->tree) return false; @@ -1873,33 +1966,33 @@ ParsePattern(frontend::TokenStream& ts, LifoAlloc& alloc, const CharT* chars, si bool irregexp::ParsePattern(frontend::TokenStream& ts, LifoAlloc& alloc, JSAtom* str, bool multiline, bool match_only, bool unicode, bool ignore_case, - bool global, bool sticky, RegExpCompileData* data) + bool global, bool sticky, bool dotall, RegExpCompileData* data) { JS::AutoCheckCannotGC nogc; return str->hasLatin1Chars() ? ::ParsePattern(ts, alloc, str->latin1Chars(nogc), str->length(), - multiline, match_only, unicode, ignore_case, global, sticky, data) + multiline, match_only, unicode, ignore_case, global, sticky, dotall, data) : ::ParsePattern(ts, alloc, str->twoByteChars(nogc), str->length(), - multiline, match_only, unicode, ignore_case, global, sticky, data); + multiline, match_only, unicode, ignore_case, global, sticky, dotall, data); } template <typename CharT> static bool ParsePatternSyntax(frontend::TokenStream& ts, LifoAlloc& alloc, const CharT* chars, size_t length, - bool unicode) + bool unicode, bool dotall) { LifoAllocScope scope(&alloc); - RegExpParser<CharT> parser(ts, &alloc, chars, chars + length, false, unicode, false); + RegExpParser<CharT> parser(ts, &alloc, chars, chars + length, false, unicode, dotall, false); return parser.ParsePattern() != nullptr; } bool irregexp::ParsePatternSyntax(frontend::TokenStream& ts, LifoAlloc& alloc, JSAtom* str, - bool unicode) + bool unicode, bool dotall) { JS::AutoCheckCannotGC nogc; return str->hasLatin1Chars() - ? ::ParsePatternSyntax(ts, alloc, str->latin1Chars(nogc), str->length(), unicode) - : ::ParsePatternSyntax(ts, alloc, str->twoByteChars(nogc), str->length(), unicode); + ? ::ParsePatternSyntax(ts, alloc, str->latin1Chars(nogc), str->length(), unicode, dotall) + : ::ParsePatternSyntax(ts, alloc, str->twoByteChars(nogc), str->length(), unicode, dotall); } diff --git a/js/src/irregexp/RegExpParser.h b/js/src/irregexp/RegExpParser.h index 0a7e61858..ee57f0436 100644 --- a/js/src/irregexp/RegExpParser.h +++ b/js/src/irregexp/RegExpParser.h @@ -44,11 +44,11 @@ namespace irregexp { bool ParsePattern(frontend::TokenStream& ts, LifoAlloc& alloc, JSAtom* str, bool multiline, bool match_only, bool unicode, bool ignore_case, - bool global, bool sticky, RegExpCompileData* data); + bool global, bool sticky, bool dotall, RegExpCompileData* data); bool ParsePatternSyntax(frontend::TokenStream& ts, LifoAlloc& alloc, JSAtom* str, - bool unicode); + bool unicode, bool dotall); // A BufferedVector is an automatically growing list, just like (and backed // by) a Vector, that is optimized for the case of adding and removing @@ -178,7 +178,7 @@ class RegExpParser public: RegExpParser(frontend::TokenStream& ts, LifoAlloc* alloc, const CharT* chars, const CharT* end, bool multiline_mode, bool unicode, - bool ignore_case); + bool ignore_case, bool dotall); RegExpTree* ParsePattern(); RegExpTree* ParseDisjunction(); @@ -229,7 +229,7 @@ class RegExpParser bool simple() { return simple_; } bool contains_anchor() { return contains_anchor_; } void set_contains_anchor() { contains_anchor_ = true; } - int captures_started() { return captures_ == nullptr ? 0 : captures_->length(); } + int captures_started() { return captures_started_; } const CharT* position() { return next_pos_ - 1; } static const int kMaxCaptures = 1 << 16; @@ -239,8 +239,8 @@ class RegExpParser enum SubexpressionType { INITIAL, CAPTURE, // All positive values represent captures. - POSITIVE_LOOKAHEAD, - NEGATIVE_LOOKAHEAD, + POSITIVE_LOOKAROUND, + NEGATIVE_LOOKAROUND, GROUPING }; @@ -249,10 +249,12 @@ class RegExpParser RegExpParserState(LifoAlloc* alloc, RegExpParserState* previous_state, SubexpressionType group_type, + RegExpLookaround::Type lookaround_type, int disjunction_capture_index) : previous_state_(previous_state), builder_(alloc->newInfallible<RegExpBuilder>(alloc)), group_type_(group_type), + lookaround_type_(lookaround_type), disjunction_capture_index_(disjunction_capture_index) {} // Parser state of containing expression, if any. @@ -262,11 +264,16 @@ class RegExpParser RegExpBuilder* builder() { return builder_; } // Type of regexp being parsed (parenthesized group or entire regexp). SubexpressionType group_type() { return group_type_; } + // Lookahead or Lookbehind. + RegExpLookaround::Type lookaround_type() { return lookaround_type_; } // Index in captures array of first capture in this sub-expression, if any. // Also the capture index of this sub-expression itself, if group_type // is CAPTURE. int capture_index() { return disjunction_capture_index_; } + // Check whether the parser is inside a capture group with the given index. + bool IsInsideCaptureGroup(int index); + private: // Linked list implementation of stack of states. RegExpParserState* previous_state_; @@ -274,10 +281,15 @@ class RegExpParser RegExpBuilder* builder_; // Stored disjunction type (capture, look-ahead or grouping), if any. SubexpressionType group_type_; + // Stored read direction. + RegExpLookaround::Type lookaround_type_; // Stored disjunction's capture index (if any). int disjunction_capture_index_; }; + // Return the 1-indexed RegExpCapture object, allocate if necessary. + RegExpCapture* GetCapture(int index); + widechar current() { return current_; } bool has_more() { return has_more_; } bool has_next() { return next_pos_ < end_; } @@ -294,12 +306,14 @@ class RegExpParser const CharT* next_pos_; const CharT* end_; widechar current_; + int captures_started_; // The capture count is only valid after we have scanned for captures. int capture_count_; bool has_more_; bool multiline_; bool unicode_; bool ignore_case_; + bool dotall_; bool simple_; bool contains_anchor_; bool is_scanned_for_captures_; diff --git a/js/src/jit-test/modules/empty.js b/js/src/jit-test/modules/empty.js new file mode 100644 index 000000000..bd9ec079d --- /dev/null +++ b/js/src/jit-test/modules/empty.js @@ -0,0 +1 @@ +// Intentionally empty. diff --git a/js/src/jit-test/modules/export-circular-nonexisting-binding-1.js b/js/src/jit-test/modules/export-circular-nonexisting-binding-1.js new file mode 100644 index 000000000..2b91b6a28 --- /dev/null +++ b/js/src/jit-test/modules/export-circular-nonexisting-binding-1.js @@ -0,0 +1,4 @@ +import "export-circular-nonexisting-binding-2.js"; + +export* from "empty.js"; +export {x} from "empty.js"; diff --git a/js/src/jit-test/modules/export-circular-nonexisting-binding-2.js b/js/src/jit-test/modules/export-circular-nonexisting-binding-2.js new file mode 100644 index 000000000..ba7dcc1b4 --- /dev/null +++ b/js/src/jit-test/modules/export-circular-nonexisting-binding-2.js @@ -0,0 +1 @@ +export {x} from "export-circular-nonexisting-binding-1.js"; diff --git a/js/src/jit-test/modules/export-star-circular-1.js b/js/src/jit-test/modules/export-star-circular-1.js new file mode 100644 index 000000000..9a0771b02 --- /dev/null +++ b/js/src/jit-test/modules/export-star-circular-1.js @@ -0,0 +1 @@ +export* from "export-star-circular-2.js"; diff --git a/js/src/jit-test/modules/export-star-circular-2.js b/js/src/jit-test/modules/export-star-circular-2.js new file mode 100644 index 000000000..b273d9cef --- /dev/null +++ b/js/src/jit-test/modules/export-star-circular-2.js @@ -0,0 +1,3 @@ +export {y as x} from "export-star-circular-1.js"; + +export var y = "pass"; diff --git a/js/src/jit-test/tests/auto-regress/bug466654.js b/js/src/jit-test/tests/auto-regress/bug466654.js deleted file mode 100644 index 6c82c425b..000000000 --- a/js/src/jit-test/tests/auto-regress/bug466654.js +++ /dev/null @@ -1,8 +0,0 @@ -// |jit-test| error:TypeError - -// Binary: cache/js-dbg-32-29add08d84ae-linux -// Flags: -j -// -this.watch('y', /x/g ); -for each (y in ['q', 'q', 'q']) continue; -gc(); diff --git a/js/src/jit-test/tests/auto-regress/bug516897.js b/js/src/jit-test/tests/auto-regress/bug516897.js deleted file mode 100644 index e3caf4e6e..000000000 --- a/js/src/jit-test/tests/auto-regress/bug516897.js +++ /dev/null @@ -1,6 +0,0 @@ -// Binary: cache/js-dbg-64-38754465ffde-linux -// Flags: -// -this.__defineSetter__("x", gc); -this.watch("x",function(){return}); -x = 3; diff --git a/js/src/jit-test/tests/auto-regress/bug537854.js b/js/src/jit-test/tests/auto-regress/bug537854.js deleted file mode 100644 index 80fb3c14a..000000000 --- a/js/src/jit-test/tests/auto-regress/bug537854.js +++ /dev/null @@ -1,4 +0,0 @@ -// Binary: cache/js-dbg-64-9d51f2a931f7-linux -// Flags: -// -({x:function(){}}).watch('x',function(){}); diff --git a/js/src/jit-test/tests/auto-regress/bug560796.js b/js/src/jit-test/tests/auto-regress/bug560796.js deleted file mode 100644 index 4ab93567e..000000000 --- a/js/src/jit-test/tests/auto-regress/bug560796.js +++ /dev/null @@ -1,9 +0,0 @@ -// Binary: cache/js-dbg-64-a6d7a5677b4c-linux -// Flags: -// -this.__defineSetter__("x", function(){}) -this.watch("x", "".localeCompare) -window = x -Object.defineProperty(this, "x", ({ - set: window -})) diff --git a/js/src/jit-test/tests/auto-regress/bug638735.js b/js/src/jit-test/tests/auto-regress/bug638735.js index 63071aa7c..c941f5369 100644 --- a/js/src/jit-test/tests/auto-regress/bug638735.js +++ b/js/src/jit-test/tests/auto-regress/bug638735.js @@ -4,7 +4,6 @@ var o9 = Function.prototype; var o13 = Array; function f5(o) { -o.watch('p3', function() {}); ox1 = new Proxy(o, {}); } f5(o9); diff --git a/js/src/jit-test/tests/auto-regress/bug639413.js b/js/src/jit-test/tests/auto-regress/bug639413.js deleted file mode 100644 index d8dd58eaf..000000000 --- a/js/src/jit-test/tests/auto-regress/bug639413.js +++ /dev/null @@ -1,9 +0,0 @@ -// |jit-test| error:TypeError - -// Binary: cache/js-dbg-32-1c8e91b2e3a4-linux -// Flags: -// -a = evalcx("lazy"); -a.watch("x", function() {}); -({}).watch("x", function() {}); -a.__defineGetter__("y", {}); diff --git a/js/src/jit-test/tests/auto-regress/bug698899.js b/js/src/jit-test/tests/auto-regress/bug698899.js deleted file mode 100644 index 644f45ec2..000000000 --- a/js/src/jit-test/tests/auto-regress/bug698899.js +++ /dev/null @@ -1,9 +0,0 @@ -// Binary: cache/js-dbg-32-f951e9151626-linux -// Flags: -m -n -// -o = evalcx("lazy").__proto__ -gc() -try { - o.watch() -} catch (e) {} -o.constructor() diff --git a/js/src/jit-test/tests/auto-regress/bug746397.js b/js/src/jit-test/tests/auto-regress/bug746397.js deleted file mode 100644 index d915ca7bb..000000000 --- a/js/src/jit-test/tests/auto-regress/bug746397.js +++ /dev/null @@ -1,10 +0,0 @@ -// |jit-test| error:ReferenceError - -// Binary: cache/js-dbg-64-67bf9a4a1f77-linux -// Flags: --ion-eager -// - -(function () { - var a = ['x', 'y']; - obj.watch(a[+("0")], counter); -})(); diff --git a/js/src/jit-test/tests/auto-regress/bug769192.js b/js/src/jit-test/tests/auto-regress/bug769192.js deleted file mode 100644 index 531e37912..000000000 --- a/js/src/jit-test/tests/auto-regress/bug769192.js +++ /dev/null @@ -1,6 +0,0 @@ -// |jit-test| error:TypeError - -// Binary: cache/js-dbg-64-bf8f2961d0cc-linux -// Flags: -// -Object.watch.call(new Uint8ClampedArray, "length", function() {}); diff --git a/js/src/jit-test/tests/baseline/bug843444.js b/js/src/jit-test/tests/baseline/bug843444.js deleted file mode 100644 index 3a77402ac..000000000 --- a/js/src/jit-test/tests/baseline/bug843444.js +++ /dev/null @@ -1,8 +0,0 @@ -gczeal(8, 1) -function recurse(x) { - recurse; - if (x < 20) - recurse(x + 1); -}; -this.watch(5, (function () {})) -recurse(0) diff --git a/js/src/jit-test/tests/basic/bug1220766.js b/js/src/jit-test/tests/basic/bug1220766.js deleted file mode 100644 index fca11eafe..000000000 --- a/js/src/jit-test/tests/basic/bug1220766.js +++ /dev/null @@ -1,3 +0,0 @@ -iter = getSelfHostedValue("CreateListIterator")([]); -iter.next(); -iter.next(); diff --git a/js/src/jit-test/tests/basic/bug510437.js b/js/src/jit-test/tests/basic/bug510437.js deleted file mode 100644 index 2418b9b11..000000000 --- a/js/src/jit-test/tests/basic/bug510437.js +++ /dev/null @@ -1,13 +0,0 @@ -// Don't crash or assert. - -var d; -this.watch("d", eval); -(function () { - (eval("\ - (function () {\ - for (let x = 0; x < 2; ++x) {\ - d = x\ - }\ - })\ -"))() -})() diff --git a/js/src/jit-test/tests/basic/bug605015.js b/js/src/jit-test/tests/basic/bug605015.js deleted file mode 100644 index a35f7b6c7..000000000 --- a/js/src/jit-test/tests/basic/bug605015.js +++ /dev/null @@ -1,9 +0,0 @@ -// |jit-test| error: TypeError
-// don't assert
-
-print(this.watch("x",
-function() {
- Object.defineProperty(this, "x", ({
- get: (Int8Array)
- }))
-}))(x = /x/)
diff --git a/js/src/jit-test/tests/basic/bug631305.js b/js/src/jit-test/tests/basic/bug631305.js deleted file mode 100644 index b0cbbbb24..000000000 --- a/js/src/jit-test/tests/basic/bug631305.js +++ /dev/null @@ -1,9 +0,0 @@ -var n = 0; -var a = []; -for (var i = 0; i < 20; i++) - a[i] = {}; -a[18].watch("p", function () { n++; }); -delete a[18].p; -for (var i = 0; i < 20; i++) - a[i].p = 0; -assertEq(n, 1); diff --git a/js/src/jit-test/tests/basic/bug662562.js b/js/src/jit-test/tests/basic/bug662562.js deleted file mode 100644 index 45b48589a..000000000 --- a/js/src/jit-test/tests/basic/bug662562.js +++ /dev/null @@ -1,6 +0,0 @@ -// |jit-test| error: TypeError -function f(o) { - o.watch("x", this); -} -var c = evalcx(""); -f(c); diff --git a/js/src/jit-test/tests/basic/bug690292.js b/js/src/jit-test/tests/basic/bug690292.js deleted file mode 100644 index 43ab56dd7..000000000 --- a/js/src/jit-test/tests/basic/bug690292.js +++ /dev/null @@ -1,12 +0,0 @@ - -done = false; -try { - function x() {} - print(this.watch("d", Object.create)) - var d = {} -} catch (e) {} -try { - eval("d = ''") - done = true; -} catch (e) {} -assertEq(done, false); diff --git a/js/src/jit-test/tests/basic/bug696748.js b/js/src/jit-test/tests/basic/bug696748.js index fe171f976..33fb4d52f 100644 --- a/js/src/jit-test/tests/basic/bug696748.js +++ b/js/src/jit-test/tests/basic/bug696748.js @@ -1,6 +1,3 @@ -try { -this.watch("b", "".substring); -} catch(exc1) {} eval("\ var URI = '';\ test();\ diff --git a/js/src/jit-test/tests/basic/bug831846.js b/js/src/jit-test/tests/basic/bug831846.js deleted file mode 100644 index 30bb3aa86..000000000 --- a/js/src/jit-test/tests/basic/bug831846.js +++ /dev/null @@ -1,3 +0,0 @@ -// |jit-test| error:TypeError - -evalcx('').watch("", /()/); diff --git a/js/src/jit-test/tests/basic/testAssigningWatchedDeletedProperty.js b/js/src/jit-test/tests/basic/testAssigningWatchedDeletedProperty.js deleted file mode 100644 index c22eabed0..000000000 --- a/js/src/jit-test/tests/basic/testAssigningWatchedDeletedProperty.js +++ /dev/null @@ -1,7 +0,0 @@ -var o = {}; -o.watch("p", function() { }); - -for (var i = 0; i < 10; i++) { - o.p = 123; - delete o.p; -} diff --git a/js/src/jit-test/tests/basic/testBug566556.js b/js/src/jit-test/tests/basic/testBug566556.js deleted file mode 100644 index 244be57d2..000000000 --- a/js/src/jit-test/tests/basic/testBug566556.js +++ /dev/null @@ -1,9 +0,0 @@ -var msg = ""; -try { - this.__defineSetter__('x', Object.create); - this.watch('x', function() {}); - x = 3; -} catch (e) { - msg = e.toString(); -} -assertEq(msg, "TypeError: undefined is not an object or null"); diff --git a/js/src/jit-test/tests/basic/testBug578044.js b/js/src/jit-test/tests/basic/testBug578044.js deleted file mode 100644 index c5b811dc9..000000000 --- a/js/src/jit-test/tests/basic/testBug578044.js +++ /dev/null @@ -1,13 +0,0 @@ -this.watch("x", Object.create) -try { - (function() { - this.__defineGetter__("x", - function() { - return this - }) - })() -} catch(e) {} -Object.defineProperty(x, "x", ({ - set: Uint16Array -})) - diff --git a/js/src/jit-test/tests/basic/testBug584650.js b/js/src/jit-test/tests/basic/testBug584650.js deleted file mode 100644 index b6c9d8ab7..000000000 --- a/js/src/jit-test/tests/basic/testBug584650.js +++ /dev/null @@ -1,9 +0,0 @@ -if (typeof gczeal != "function") - gczeal = function() {} - -// don't crash -x = (evalcx('lazy')) -x.watch("", function () {}) -gczeal(1) -for (w in x) {} - diff --git a/js/src/jit-test/tests/basic/testBug780288-1.js b/js/src/jit-test/tests/basic/testBug780288-1.js deleted file mode 100644 index 90746a04a..000000000 --- a/js/src/jit-test/tests/basic/testBug780288-1.js +++ /dev/null @@ -1,20 +0,0 @@ -s = newGlobal() -try { - evalcx("\ - Object.defineProperty(this,\"i\",{enumerable:true,get:function(){t}});\ - for each(y in this)true\ - ", s) -} catch (e) {} -try { - evalcx("\ - for(z=0,(7).watch(\"\",eval);;g){\ - if(z=1){({t:function(){}})\ - }\ - ", s) -} catch (e) {} -try { - evalcx("\ - Object.defineProperty(this,\"g2\",{get:function(){return this}});\ - g2.y()\ - ", s) -} catch (e) {} diff --git a/js/src/jit-test/tests/basic/testBug780288-2.js b/js/src/jit-test/tests/basic/testBug780288-2.js deleted file mode 100644 index 8c4c1737c..000000000 --- a/js/src/jit-test/tests/basic/testBug780288-2.js +++ /dev/null @@ -1,20 +0,0 @@ -s = newGlobal() -try { - evalcx("\ - Object.defineProperty(this,\"i\",{enumerable:true,get:function(){t}});\ - for each(y in this)true\ - ", s) -} catch (e) {} -try { - evalcx("\ - for(z=0,(7).watch(\"\",eval);;g){\ - if(z=1){({t:function(){}})\ - }\ - ", s) -} catch (e) {} -try { - evalcx("\ - Object.defineProperty(this,\"g2\",{get:function(){return this}});\ - g2.y(\"\")\ - ", s) -} catch (e) {} diff --git a/js/src/jit-test/tests/basic/testEvalCalledFromWatchOverSetter.js b/js/src/jit-test/tests/basic/testEvalCalledFromWatchOverSetter.js deleted file mode 100644 index bcd60fd80..000000000 --- a/js/src/jit-test/tests/basic/testEvalCalledFromWatchOverSetter.js +++ /dev/null @@ -1,3 +0,0 @@ -this.__defineSetter__("x", function(){}); -this.watch("x", eval); -x = 0; diff --git a/js/src/jit-test/tests/basic/testNonStubGetter.js b/js/src/jit-test/tests/basic/testNonStubGetter.js deleted file mode 100644 index 58a698eb4..000000000 --- a/js/src/jit-test/tests/basic/testNonStubGetter.js +++ /dev/null @@ -1,7 +0,0 @@ -function testNonStubGetter() { - { let [] = []; (this.watch("x", function(p, o, n) { return /a/g.exec(p, o, n); })); }; - (function () { (eval("(function(){for each (x in [1, 2, 2]);});"))(); })(); - this.unwatch("x"); - return "ok"; -} -assertEq(testNonStubGetter(), "ok"); diff --git a/js/src/jit-test/tests/basic/testSettingWatchPointOnReadOnlyProp.js b/js/src/jit-test/tests/basic/testSettingWatchPointOnReadOnlyProp.js deleted file mode 100644 index 78c281f05..000000000 --- a/js/src/jit-test/tests/basic/testSettingWatchPointOnReadOnlyProp.js +++ /dev/null @@ -1,7 +0,0 @@ -for (var i = 0; i < 5; ++i) { - var o = {} - Object.defineProperty(o, 'x', { value:"cow", writable:false }); - var r = o.watch('x', function() {}); - assertEq(r, undefined); - o.x = 4; -} diff --git a/js/src/jit-test/tests/basic/testTrueShiftTrue.js b/js/src/jit-test/tests/basic/testTrueShiftTrue.js deleted file mode 100644 index 44c1290d8..000000000 --- a/js/src/jit-test/tests/basic/testTrueShiftTrue.js +++ /dev/null @@ -1,16 +0,0 @@ -// Test no assert or crash from outer recorders (bug 465145) -function testBug465145() { - this.__defineSetter__("x", function(){}); - this.watch("x", function(){}); - y = this; - for (var z = 0; z < 2; ++z) { x = y }; - this.__defineSetter__("x", function(){}); - for (var z = 0; z < 2; ++z) { x = y }; -} - -function testTrueShiftTrue() { - var a = new Array(5); - for (var i=0;i<5;++i) a[i] = "" + (true << true); - return a.join(","); -} -assertEq(testTrueShiftTrue(), "2,2,2,2,2"); diff --git a/js/src/jit-test/tests/basic/testWatchRecursion.js b/js/src/jit-test/tests/basic/testWatchRecursion.js deleted file mode 100644 index e5d5877df..000000000 --- a/js/src/jit-test/tests/basic/testWatchRecursion.js +++ /dev/null @@ -1,63 +0,0 @@ -// Test that the watch handler is not called recursively for the same object -// and property. -(function() { - var obj1 = {}, obj2 = {}; - var handler_entry_count = 0; - var handler_exit_count = 0; - - obj1.watch('x', handler); - obj1.watch('y', handler); - obj2.watch('x', handler); - obj1.x = 1; - assertEq(handler_entry_count, 3); - assertEq(handler_exit_count, 3); - - function handler(id) { - handler_entry_count++; - assertEq(handler_exit_count, 0); - switch (true) { - case this === obj1 && id === "x": - assertEq(handler_entry_count, 1); - obj2.x = 3; - assertEq(handler_exit_count, 2); - break; - case this === obj2 && id === "x": - assertEq(handler_entry_count, 2); - obj1.y = 4; - assertEq(handler_exit_count, 1); - break; - default: - assertEq(this, obj1); - assertEq(id, "y"); - assertEq(handler_entry_count, 3); - - // We expect no more watch handler invocations - obj1.x = 5; - obj1.y = 6; - obj2.x = 7; - assertEq(handler_exit_count, 0); - break; - } - ++handler_exit_count; - assertEq(handler_entry_count, 3); - } -})(); - - -// Test that run-away recursion in watch handlers is properly handled. -(function() { - var obj = {}; - var i = 0; - try { - handler(); - throw new Error("Unreachable"); - } catch(e) { - assertEq(e instanceof InternalError, true); - } - - function handler() { - var prop = "a" + ++i; - obj.watch(prop, handler); - obj[prop] = 2; - } -})(); diff --git a/js/src/jit-test/tests/ctypes/function-definition.js b/js/src/jit-test/tests/ctypes/function-definition.js index 4df317a09..5882ba889 100644 --- a/js/src/jit-test/tests/ctypes/function-definition.js +++ b/js/src/jit-test/tests/ctypes/function-definition.js @@ -27,7 +27,7 @@ function test() { let lib; try { - lib = ctypes.open(ctypes.libraryName("c")); + lib = ctypes.open(ctypes.libraryName("m")); } catch (e) { } if (!lib) diff --git a/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-async.js b/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-async.js index d4e7e8576..0b2ee766b 100644 --- a/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-async.js +++ b/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-async.js @@ -9,28 +9,6 @@ async function f() { } `); -// To continue testing after uncaught exception, remember the exception and -// return normal completeion. -var currentFrame; -var uncaughtException; -dbg.uncaughtExceptionHook = function(e) { - uncaughtException = e; - return { - return: currentFrame.eval("({ done: true, value: 'uncaught' })").return - }; -}; -function testUncaughtException() { - uncaughtException = undefined; - var val = g.eval(` -var val; -f().then(v => { val = v }); -drainJobQueue(); -val; -`); - assertEq(val, "uncaught"); - assertEq(uncaughtException instanceof TypeError, true); -} - // Just continue dbg.onExceptionUnwind = function(frame) { return undefined; @@ -42,83 +20,10 @@ drainJobQueue(); assertEq(exc instanceof ReferenceError, true); `); -// Should return object. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: "foo" - }; -}; -testUncaughtException(); - -// The object should have `done` property and `value` property. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("({})").return - }; -}; -testUncaughtException(); - -// The object should have `done` property. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("({ value: 10 })").return - }; -}; -testUncaughtException(); - -// The object should have `value` property. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("({ done: true })").return - }; -}; -testUncaughtException(); - -// `done` property should be a boolean value. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("({ done: 10, value: 10 })").return - }; -}; -testUncaughtException(); - -// `done` property shouldn't be an accessor. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("({ get done() { return true; }, value: 10 })").return - }; -}; -testUncaughtException(); - -// `value` property shouldn't be an accessor. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("({ done: true, get value() { return 10; } })").return - }; -}; -testUncaughtException(); - -// The object shouldn't be a Proxy. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("new Proxy({ done: true, value: 10 }, {})").return - }; -}; -testUncaughtException(); - -// Correct resumption value. +// Return with resumption value. dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; return { - return: frame.eval("({ done: true, value: 10 })").return + return: 10 }; }; var val = g.eval(` diff --git a/js/src/jit-test/tests/gc/bug-900405.js b/js/src/jit-test/tests/gc/bug-900405.js deleted file mode 100644 index eeec6f25f..000000000 --- a/js/src/jit-test/tests/gc/bug-900405.js +++ /dev/null @@ -1,3 +0,0 @@ -(function() { - [{ "9": [] }.watch([], function(){})] -})() diff --git a/js/src/jit-test/tests/gc/bug-913261.js b/js/src/jit-test/tests/gc/bug-913261.js deleted file mode 100644 index 43066053f..000000000 --- a/js/src/jit-test/tests/gc/bug-913261.js +++ /dev/null @@ -1,5 +0,0 @@ -// |jit-test| error: InternalError: too much recursion -(function f() { - "".watch(2, function() {}); - f(); -})() diff --git a/js/src/jit-test/tests/gc/bug-986864.js b/js/src/jit-test/tests/gc/bug-986864.js deleted file mode 100644 index abb8de6b2..000000000 --- a/js/src/jit-test/tests/gc/bug-986864.js +++ /dev/null @@ -1,8 +0,0 @@ -// |jit-test| slow -function x() {} -for (var j = 0; j < 9999; ++j) { - (function() { - x += x.watch("endsWith", ArrayBuffer); - return 0 >> Function(x) - })() -} diff --git a/js/src/jit-test/tests/ion/bug1063182.js b/js/src/jit-test/tests/ion/bug1063182.js deleted file mode 100644 index 9cda48099..000000000 --- a/js/src/jit-test/tests/ion/bug1063182.js +++ /dev/null @@ -1,8 +0,0 @@ -// |jit-test| error: ReferenceError - -eval("(function() { " + "\ -var o = {};\ -o.watch('p', function() { });\ -for (var i = 0; i < 10; \u5ede ++)\ - o.p = 123;\ -" + " })();"); diff --git a/js/src/jit-test/tests/ion/bug772901.js b/js/src/jit-test/tests/ion/bug772901.js index eb71f6afb..164afd151 100644 --- a/js/src/jit-test/tests/ion/bug772901.js +++ b/js/src/jit-test/tests/ion/bug772901.js @@ -4,4 +4,4 @@ function f(x) { delete ((x)++); arguments[0] !== undefined; } -f(1, x = [f.ArrayBuffer,unwatch.Int32Array], this, this, this) ; +f(1, x = [f.ArrayBuffer, undefined], this, this, this) ; diff --git a/js/src/jit-test/tests/ion/bug774257-1.js b/js/src/jit-test/tests/ion/bug774257-1.js deleted file mode 100644 index 9c998a028..000000000 --- a/js/src/jit-test/tests/ion/bug774257-1.js +++ /dev/null @@ -1,8 +0,0 @@ -Object.defineProperty(Object.prototype, 'x', { - set: function() { evalcx('lazy'); } -}); -var obj = {}; -obj.watch("x", function (id, oldval, newval) {}); -for (var str in 'A') { - obj.x = 1; -} diff --git a/js/src/jit-test/tests/ion/bug774257-2.js b/js/src/jit-test/tests/ion/bug774257-2.js deleted file mode 100644 index b31043b08..000000000 --- a/js/src/jit-test/tests/ion/bug774257-2.js +++ /dev/null @@ -1,10 +0,0 @@ -Object.defineProperty(Object.prototype, 'x', { - set: function() { evalcx('lazy'); } -}); -var obj = {}; -var prot = {}; -obj.__proto__ = prot; -obj.watch("x", function (id, oldval, newval) {}); -for (var str in 'A') { - obj.x = 1; -} diff --git a/js/src/jit-test/tests/ion/bug779631.js b/js/src/jit-test/tests/ion/bug779631.js deleted file mode 100644 index 087aa01ac..000000000 --- a/js/src/jit-test/tests/ion/bug779631.js +++ /dev/null @@ -1,9 +0,0 @@ -var flag = 0; -var a = {}; -Object.defineProperty(a, "value", {set: function(x) {}}); -a.watch("value", function(){flag++;}); - -for(var i = 0; i < 100; i++) { - a.value = i; - assertEq(flag, i+1); -} diff --git a/js/src/jit-test/tests/ion/bug783590.js b/js/src/jit-test/tests/ion/bug783590.js index d48cb609e..9d277e02c 100644 --- a/js/src/jit-test/tests/ion/bug783590.js +++ b/js/src/jit-test/tests/ion/bug783590.js @@ -7,7 +7,6 @@ Object.defineProperty(arr, 0, { glob.__proto__; }) }); -this.watch("s", function() {}); try { arr.pop(); } catch (e) {} diff --git a/js/src/jit-test/tests/jaeger/bug550665.js b/js/src/jit-test/tests/jaeger/bug550665.js deleted file mode 100644 index 816888b5e..000000000 --- a/js/src/jit-test/tests/jaeger/bug550665.js +++ /dev/null @@ -1,8 +0,0 @@ -(function () { - var a; - eval("for(w in ((function(x,y){b:0})())) ;"); -})(); - -this.__defineSetter__("l", function() { gc() }); -this.watch("l", function(x) { yield {} }); -l = true; diff --git a/js/src/jit-test/tests/jaeger/bug557063.js b/js/src/jit-test/tests/jaeger/bug557063.js deleted file mode 100644 index ea77fd2c8..000000000 --- a/js/src/jit-test/tests/jaeger/bug557063.js +++ /dev/null @@ -1,7 +0,0 @@ -(function() { - for (a = 0; a < 2; a++) - ''.watch("", function() {}) -})() - -/* Don't crash or assert. */ - diff --git a/js/src/jit-test/tests/jaeger/bug588338.js b/js/src/jit-test/tests/jaeger/bug588338.js index 457be238a..c400d7687 100644 --- a/js/src/jit-test/tests/jaeger/bug588338.js +++ b/js/src/jit-test/tests/jaeger/bug588338.js @@ -9,7 +9,6 @@ function f() { } } })(/x/))) -for (z = 0; z < 100; x.unwatch(), z++) for (e in [0]) { gczeal(2) } ( [1,2,3])("") diff --git a/js/src/jit-test/tests/jaeger/bug625438.js b/js/src/jit-test/tests/jaeger/bug625438.js deleted file mode 100644 index 32586d8ab..000000000 --- a/js/src/jit-test/tests/jaeger/bug625438.js +++ /dev/null @@ -1,10 +0,0 @@ -// vim: set ts=8 sts=4 et sw=4 tw=99: - -var count = 0; -this.watch("x", function() { - count++; -}); -for(var i=0; i<10; i++) { - x = 2; -} -assertEq(count, 10); diff --git a/js/src/jit-test/tests/jaeger/bug630366.js b/js/src/jit-test/tests/jaeger/bug630366.js deleted file mode 100644 index acac8d3ef..000000000 --- a/js/src/jit-test/tests/jaeger/bug630366.js +++ /dev/null @@ -1,7 +0,0 @@ -var o = {}; -for(var i=0; i<5; i++) { - o.p = 2; - o.watch("p", function() { }); - o.p = 2; - delete o.p; -} diff --git a/js/src/jit-test/tests/jaeger/recompile/bug641225.js b/js/src/jit-test/tests/jaeger/recompile/bug641225.js index a6e3a86c7..7b7978197 100644 --- a/js/src/jit-test/tests/jaeger/recompile/bug641225.js +++ b/js/src/jit-test/tests/jaeger/recompile/bug641225.js @@ -118,7 +118,6 @@ for(var o2 in f5) { f2(o5); f2(o5); f0(o3); - o9.watch('p3', function() {}); o8[o8] = o8; f0(o5); f1(o6); diff --git a/js/src/jit-test/tests/modules/bug-1320993.js b/js/src/jit-test/tests/modules/bug-1320993.js new file mode 100644 index 000000000..bece5731a --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1320993.js @@ -0,0 +1,2 @@ +parseModule("export default (class {})"); +parseModule("export default (class A {})"); diff --git a/js/src/jit-test/tests/modules/export-circular-nonexisting-binding.js b/js/src/jit-test/tests/modules/export-circular-nonexisting-binding.js new file mode 100644 index 000000000..387c7c369 --- /dev/null +++ b/js/src/jit-test/tests/modules/export-circular-nonexisting-binding.js @@ -0,0 +1,3 @@ +// |jit-test| module; error:SyntaxError + +import "export-circular-nonexisting-binding-1.js"; diff --git a/js/src/jit-test/tests/modules/export-star-cannot-rescue-missing-export.js b/js/src/jit-test/tests/modules/export-star-cannot-rescue-missing-export.js new file mode 100644 index 000000000..f87829d89 --- /dev/null +++ b/js/src/jit-test/tests/modules/export-star-cannot-rescue-missing-export.js @@ -0,0 +1,4 @@ +// |jit-test| module; error:SyntaxError + +export { a } from "empty.js"; +export* from "module1.js"; diff --git a/js/src/jit-test/tests/modules/export-star-circular-dependencies.js b/js/src/jit-test/tests/modules/export-star-circular-dependencies.js new file mode 100644 index 000000000..9aa612f08 --- /dev/null +++ b/js/src/jit-test/tests/modules/export-star-circular-dependencies.js @@ -0,0 +1,6 @@ +// |jit-test| module + +import { x, y } from "export-star-circular-1.js"; + +assertEq(x, "pass"); +assertEq(y, "pass"); diff --git a/js/src/jit-test/tests/modules/import-namespace.js b/js/src/jit-test/tests/modules/import-namespace.js index f44d4568a..0287f7a60 100644 --- a/js/src/jit-test/tests/modules/import-namespace.js +++ b/js/src/jit-test/tests/modules/import-namespace.js @@ -19,9 +19,19 @@ function testHasNames(names, expected) { }); } +function testEqualArrays(actual, expected) { + assertEq(Array.isArray(actual), true); + assertEq(Array.isArray(expected), true); + assertEq(actual.length, expected.length); + for (let i = 0; i < expected.length; i++) { + assertEq(actual[i], expected[i]); + } +} + let a = moduleRepo['a'] = parseModule( - `export var a = 1; - export var b = 2;` + `// Reflection methods should return these exports alphabetically sorted. + export var b = 2; + export var a = 1;` ); let b = moduleRepo['b'] = parseModule( @@ -35,11 +45,16 @@ b.evaluation(); testHasNames(getModuleEnvironmentNames(b), ["ns", "x"]); let ns = getModuleEnvironmentValue(b, "ns"); testHasNames(Object.keys(ns), ["a", "b"]); +assertEq(ns.a, 1); +assertEq(ns.b, 2); +assertEq(ns.c, undefined); assertEq(getModuleEnvironmentValue(b, "x"), 3); // Test module namespace internal methods as defined in 9.4.6 assertEq(Object.getPrototypeOf(ns), null); -assertThrowsInstanceOf(() => Object.setPrototypeOf(ns, null), TypeError); +assertEq(Reflect.setPrototypeOf(ns, null), true); +assertEq(Reflect.setPrototypeOf(ns, Object.prototype), false); +assertThrowsInstanceOf(() => Object.setPrototypeOf(ns, {}), TypeError); assertThrowsInstanceOf(function() { ns.foo = 1; }, TypeError); assertEq(Object.isExtensible(ns), false); Object.preventExtensions(ns); @@ -59,29 +74,15 @@ desc = Object.getOwnPropertyDescriptor(ns, Symbol.toStringTag); assertEq(desc.value, "Module"); assertEq(desc.writable, false); assertEq(desc.enumerable, false); -assertEq(desc.configurable, true); +assertEq(desc.configurable, false); assertEq(typeof desc.get, "undefined"); assertEq(typeof desc.set, "undefined"); assertEq(Object.prototype.toString.call(ns), "[object Module]"); -// Test @@iterator method. -let iteratorFun = ns[Symbol.iterator]; -assertEq(iteratorFun.name, "[Symbol.iterator]"); - -let iterator = ns[Symbol.iterator](); -assertEq(iterator[Symbol.iterator](), iterator); -assertIteratorNext(iterator, "a"); -assertIteratorNext(iterator, "b"); -assertIteratorDone(iterator); - -// The iterator's next method can only be called on the object it was originally -// associated with. -iterator = ns[Symbol.iterator](); -let iterator2 = ns[Symbol.iterator](); -assertThrowsInstanceOf(() => iterator.next.call({}), TypeError); -assertThrowsInstanceOf(() => iterator.next.call(iterator2), TypeError); -assertEq(iterator.next.call(iterator).value, "a"); -assertEq(iterator2.next.call(iterator2).value, "a"); +// Test [[OwnPropertyKeys]] internal method. +testEqualArrays(Reflect.ownKeys(ns), ["a", "b", Symbol.toStringTag]); +testEqualArrays(Object.getOwnPropertyNames(ns), ["a", "b"]); +testEqualArrays(Object.getOwnPropertySymbols(ns), [Symbol.toStringTag]); // Test cyclic namespace import and access in module evaluation. let c = moduleRepo['c'] = diff --git a/js/src/jit-test/tests/pic/fuzz1.js b/js/src/jit-test/tests/pic/fuzz1.js deleted file mode 100644 index 2481a1314..000000000 --- a/js/src/jit-test/tests/pic/fuzz1.js +++ /dev/null @@ -1,4 +0,0 @@ -(function() {
- for (a = 0; a < 2; a++)
- ''.watch("", function() {})
-})()
diff --git a/js/src/jit-test/tests/pic/fuzz3.js b/js/src/jit-test/tests/pic/fuzz3.js deleted file mode 100644 index 17613b6f5..000000000 --- a/js/src/jit-test/tests/pic/fuzz3.js +++ /dev/null @@ -1,3 +0,0 @@ -for each(let w in [[], 0, [], 0]) {
- w.unwatch()
-}
diff --git a/js/src/jit-test/tests/pic/watch1.js b/js/src/jit-test/tests/pic/watch1.js deleted file mode 100644 index 09d6347bf..000000000 --- a/js/src/jit-test/tests/pic/watch1.js +++ /dev/null @@ -1,7 +0,0 @@ -// assignments to watched objects must not be cached -var obj = {x: 0}; -var hits = 0; -obj.watch("x", function (id, oldval, newval) { hits++; return newval; }); -for (var i = 0; i < 10; i++) - obj.x = i; -assertEq(hits, 10); diff --git a/js/src/jit-test/tests/pic/watch1a.js b/js/src/jit-test/tests/pic/watch1a.js deleted file mode 100644 index 4b404f507..000000000 --- a/js/src/jit-test/tests/pic/watch1a.js +++ /dev/null @@ -1,17 +0,0 @@ -// assignments to watched objects must not be traced -var hits = 0; -function counter(id, oldval, newval) { - hits++; - return newval; -} - -(function () { - var obj = {x: 0, y: 0}; - var a = ['x', 'y']; - obj.watch('z', counter); - for (var i = 0; i < 14; i++) { - obj.watch(a[+(i > 8)], counter); - obj.y = i; - } -})(); -assertEq(hits, 5); diff --git a/js/src/jit-test/tests/pic/watch2.js b/js/src/jit-test/tests/pic/watch2.js deleted file mode 100644 index fb3e29617..000000000 --- a/js/src/jit-test/tests/pic/watch2.js +++ /dev/null @@ -1,8 +0,0 @@ -// assignments to watched properties via ++ must not be cached -var obj = {x: 0}; -var hits = 0; -obj.watch("x", function (id, oldval, newval) { hits++; return newval; }); -for (var i = 0; i < 10; i++) - obj.x++; -assertEq(hits, 10); - diff --git a/js/src/jit-test/tests/pic/watch2a.js b/js/src/jit-test/tests/pic/watch2a.js deleted file mode 100644 index ce3294ba3..000000000 --- a/js/src/jit-test/tests/pic/watch2a.js +++ /dev/null @@ -1,18 +0,0 @@ -// assignments to watched properties via ++ must not be traced -var hits = 0; -function counter(id, oldval, newval) { - hits++; - return newval; -} - -(function () { - var obj = {x: 0, y: 0}; - var a = ['x', 'y']; - obj.watch('z', counter); - for (var i = 0; i < 14; i++) { - obj.watch(a[+(i > 8)], counter); - obj.y++; - } -})(); -assertEq(hits, 5); - diff --git a/js/src/jit-test/tests/pic/watch3.js b/js/src/jit-test/tests/pic/watch3.js deleted file mode 100644 index 4c5c93d8b..000000000 --- a/js/src/jit-test/tests/pic/watch3.js +++ /dev/null @@ -1,7 +0,0 @@ -// assignment to watched global properties must not be cached -x = 0; -var hits = 0; -this.watch("x", function (id, oldval, newval) { hits++; return newval; }); -for (var i = 0; i < 10; i++) - x = i; -assertEq(hits, 10); diff --git a/js/src/jit-test/tests/pic/watch3a.js b/js/src/jit-test/tests/pic/watch3a.js deleted file mode 100644 index 700daf6af..000000000 --- a/js/src/jit-test/tests/pic/watch3a.js +++ /dev/null @@ -1,19 +0,0 @@ -// assignment to watched global properties must not be traced -var hits = 0; -function counter(id, oldval, newval) { - hits++; - return newval; -} - -var x = 0; -var y = 0; -(function () { - var a = ['x', 'y']; - this.watch('z', counter); - for (var i = 0; i < 14; i++) { - this.watch(a[+(i > 8)], counter); - y = 1; - } -})(); -assertEq(hits, 5); - diff --git a/js/src/jit-test/tests/pic/watch3b.js b/js/src/jit-test/tests/pic/watch3b.js deleted file mode 100644 index 9b0dc8cc9..000000000 --- a/js/src/jit-test/tests/pic/watch3b.js +++ /dev/null @@ -1,20 +0,0 @@ -// assignment to watched global properties must not be traced -var hits = 0; -function counter(id, oldval, newval) { - hits++; - return newval; -} - -var x = 0; -var y = 0; -function f() { - var a = [{}, this]; - for (var i = 0; i < 14; i++) { - print(shapeOf(this)); - Object.prototype.watch.call(a[+(i > 8)], "y", counter); - y++; - } -} -f(); -assertEq(hits, 5); - diff --git a/js/src/jit-test/tests/pic/watch4.js b/js/src/jit-test/tests/pic/watch4.js deleted file mode 100644 index 4b640c4df..000000000 --- a/js/src/jit-test/tests/pic/watch4.js +++ /dev/null @@ -1,9 +0,0 @@ -// adding assignment + watchpoint vs. caching -var hits = 0; -var obj = {}; -obj.watch("x", function (id, oldval, newval) { hits++; return newval; }); -for (var i = 0; i < 10; i++) { - obj.x = 1; - delete obj.x; -} -assertEq(hits, 10); diff --git a/js/src/jit-test/tests/pic/watch5.js b/js/src/jit-test/tests/pic/watch5.js deleted file mode 100644 index 6b22951a4..000000000 --- a/js/src/jit-test/tests/pic/watch5.js +++ /dev/null @@ -1,27 +0,0 @@ -// test against future pic support for symbols - -// assignments to watched objects must not be cached -var obj = {}; -var x = Symbol.for("x"); -obj[x] = 0; -var hits = 0; -obj.watch(x, function (id, oldval, newval) { hits++; return newval; }); -for (var i = 0; i < 10; i++) - obj[x] = i; -assertEq(hits, 10); - -// assignments to watched properties via ++ must not be cached -hits = 0; -for (var i = 0; i < 10; i++) - obj[x]++; -assertEq(hits, 10); - -// adding assignment + watchpoint vs. caching -hits = 0; -obj = {}; -obj.watch(x, function (id, oldval, newval) { hits++; return newval; }); -for (var i = 0; i < 10; i++) { - obj[x] = 1; - delete obj[x]; -} -assertEq(hits, 10); diff --git a/js/src/jit-test/tests/profiler/bug1140643.js b/js/src/jit-test/tests/profiler/bug1140643.js deleted file mode 100644 index 1b171aea2..000000000 --- a/js/src/jit-test/tests/profiler/bug1140643.js +++ /dev/null @@ -1,14 +0,0 @@ -// |jit-test| allow-oom -enableSPSProfiling(); -loadFile('\ -for (var i = 0; i < 2; i++) {\ - obj = { m: function () {} };\ - obj.watch("m", function () { float32 = 0 + obj.foo; });\ - obj.m = 0;\ -}\ -'); -gcparam("maxBytes", gcparam("gcBytes") + (1)*1024); -newGlobal("same-compartment"); -function loadFile(lfVarx) { - evaluate(lfVarx, { noScriptRval : true, isRunOnce : true }); -} diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index b2f9d3b23..d254b9826 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -25,6 +25,7 @@ #include "jit/VMFunctions.h" #include "js/UniquePtr.h" #include "vm/AsyncFunction.h" +#include "vm/AsyncIteration.h" #include "vm/EnvironmentObject.h" #include "vm/Interpreter.h" #include "vm/TraceLogging.h" @@ -43,7 +44,7 @@ using mozilla::AssertedCast; BaselineCompiler::BaselineCompiler(JSContext* cx, TempAllocator& alloc, JSScript* script) : BaselineCompilerSpecific(cx, alloc, script), - yieldOffsets_(cx), + yieldAndAwaitOffsets_(cx), modifiesArguments_(false) { } @@ -210,7 +211,7 @@ BaselineCompiler::compile() pcMappingIndexEntries.length(), pcEntries.length(), bytecodeTypeMapEntries, - yieldOffsets_.length(), + yieldAndAwaitOffsets_.length(), traceLoggerToggleOffsets_.length()), JS::DeletePolicy<BaselineScript>(cx->runtime())); if (!baselineScript) { @@ -275,7 +276,7 @@ BaselineCompiler::compile() // searches for the sought entry when queries are in linear order. bytecodeMap[script->nTypeSets()] = 0; - baselineScript->copyYieldEntries(script, yieldOffsets_); + baselineScript->copyYieldAndAwaitEntries(script, yieldAndAwaitOffsets_); if (compileDebugInstrumentation_) baselineScript->setHasDebugInstrumentation(); @@ -3908,6 +3909,50 @@ BaselineCompiler::emit_JSOP_TOASYNC() return true; } +typedef JSObject* (*ToAsyncGenFn)(JSContext*, HandleFunction); +static const VMFunction ToAsyncGenInfo = + FunctionInfo<ToAsyncGenFn>(js::WrapAsyncGenerator, "ToAsyncGen"); + +bool +BaselineCompiler::emit_JSOP_TOASYNCGEN() +{ + frame.syncStack(0); + masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg()); + + prepareVMCall(); + pushArg(R0.scratchReg()); + + if (!callVM(ToAsyncGenInfo)) + return false; + + masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0); + frame.pop(); + frame.push(R0); + return true; +} + +typedef JSObject* (*ToAsyncIterFn)(JSContext*, HandleObject); +static const VMFunction ToAsyncIterInfo = + FunctionInfo<ToAsyncIterFn>(js::CreateAsyncFromSyncIterator, "ToAsyncIter"); + +bool +BaselineCompiler::emit_JSOP_TOASYNCITER() +{ + frame.syncStack(0); + masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg()); + + prepareVMCall(); + pushArg(R0.scratchReg()); + + if (!callVM(ToAsyncIterInfo)) + return false; + + masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0); + frame.pop(); + frame.push(R0); + return true; +} + typedef bool (*ThrowObjectCoercibleFn)(JSContext*, HandleValue); static const VMFunction ThrowObjectCoercibleInfo = FunctionInfo<ThrowObjectCoercibleFn>(ThrowObjectCoercible, "ThrowObjectCoercible"); @@ -4174,27 +4219,28 @@ BaselineCompiler::emit_JSOP_GENERATOR() } bool -BaselineCompiler::addYieldOffset() +BaselineCompiler::addYieldAndAwaitOffset() { - MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD); + MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD || *pc == JSOP_AWAIT); - uint32_t yieldIndex = GET_UINT24(pc); + uint32_t yieldAndAwaitIndex = GET_UINT24(pc); - while (yieldIndex >= yieldOffsets_.length()) { - if (!yieldOffsets_.append(0)) + while (yieldAndAwaitIndex >= yieldAndAwaitOffsets_.length()) { + if (!yieldAndAwaitOffsets_.append(0)) return false; } - static_assert(JSOP_INITIALYIELD_LENGTH == JSOP_YIELD_LENGTH, - "code below assumes INITIALYIELD and YIELD have same length"); - yieldOffsets_[yieldIndex] = script->pcToOffset(pc + JSOP_YIELD_LENGTH); + static_assert(JSOP_INITIALYIELD_LENGTH == JSOP_YIELD_LENGTH && + JSOP_INITIALYIELD_LENGTH == JSOP_AWAIT_LENGTH, + "code below assumes INITIALYIELD and YIELD and AWAIT have same length"); + yieldAndAwaitOffsets_[yieldAndAwaitIndex] = script->pcToOffset(pc + JSOP_YIELD_LENGTH); return true; } bool BaselineCompiler::emit_JSOP_INITIALYIELD() { - if (!addYieldOffset()) + if (!addYieldAndAwaitOffset()) return false; frame.syncStack(0); @@ -4204,7 +4250,8 @@ BaselineCompiler::emit_JSOP_INITIALYIELD() masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), genObj); MOZ_ASSERT(GET_UINT24(pc) == 0); - masm.storeValue(Int32Value(0), Address(genObj, GeneratorObject::offsetOfYieldIndexSlot())); + masm.storeValue(Int32Value(0), + Address(genObj, GeneratorObject::offsetOfYieldAndAwaitIndexSlot())); Register envObj = R0.scratchReg(); Address envChainSlot(genObj, GeneratorObject::offsetOfEnvironmentChainSlot()); @@ -4233,7 +4280,7 @@ static const VMFunction NormalSuspendInfo = bool BaselineCompiler::emit_JSOP_YIELD() { - if (!addYieldOffset()) + if (!addYieldAndAwaitOffset()) return false; // Store generator in R0. @@ -4250,7 +4297,7 @@ BaselineCompiler::emit_JSOP_YIELD() // generator is in the closing state, see GeneratorObject::suspend. masm.storeValue(Int32Value(GET_UINT24(pc)), - Address(genObj, GeneratorObject::offsetOfYieldIndexSlot())); + Address(genObj, GeneratorObject::offsetOfYieldAndAwaitIndexSlot())); Register envObj = R0.scratchReg(); Address envChainSlot(genObj, GeneratorObject::offsetOfEnvironmentChainSlot()); @@ -4282,6 +4329,12 @@ BaselineCompiler::emit_JSOP_YIELD() return emitReturn(); } +bool +BaselineCompiler::emit_JSOP_AWAIT() +{ + return emit_JSOP_YIELD(); +} + typedef bool (*DebugAfterYieldFn)(JSContext*, BaselineFrame*); static const VMFunction DebugAfterYieldInfo = FunctionInfo<DebugAfterYieldFn>(jit::DebugAfterYield, "DebugAfterYield"); @@ -4501,16 +4554,17 @@ BaselineCompiler::emit_JSOP_RESUME() masm.pushValue(retVal); if (resumeKind == GeneratorObject::NEXT) { - // Determine the resume address based on the yieldIndex and the - // yieldIndex -> native table in the BaselineScript. + // Determine the resume address based on the yieldAndAwaitIndex and the + // yieldAndAwaitIndex -> native table in the BaselineScript. masm.load32(Address(scratch1, BaselineScript::offsetOfYieldEntriesOffset()), scratch2); masm.addPtr(scratch2, scratch1); - masm.unboxInt32(Address(genObj, GeneratorObject::offsetOfYieldIndexSlot()), scratch2); + masm.unboxInt32(Address(genObj, GeneratorObject::offsetOfYieldAndAwaitIndexSlot()), + scratch2); masm.loadPtr(BaseIndex(scratch1, scratch2, ScaleFromElemWidth(sizeof(uintptr_t))), scratch1); // Mark as running and jump to the generator's JIT code. - masm.storeValue(Int32Value(GeneratorObject::YIELD_INDEX_RUNNING), - Address(genObj, GeneratorObject::offsetOfYieldIndexSlot())); + masm.storeValue(Int32Value(GeneratorObject::YIELD_AND_AWAIT_INDEX_RUNNING), + Address(genObj, GeneratorObject::offsetOfYieldAndAwaitIndexSlot())); masm.jump(scratch1); } else { MOZ_ASSERT(resumeKind == GeneratorObject::THROW || resumeKind == GeneratorObject::CLOSE); diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index 95e0c77ad..a200f7ab9 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -196,6 +196,8 @@ namespace jit { _(JSOP_RUNONCE) \ _(JSOP_REST) \ _(JSOP_TOASYNC) \ + _(JSOP_TOASYNCGEN) \ + _(JSOP_TOASYNCITER) \ _(JSOP_TOID) \ _(JSOP_TOSTRING) \ _(JSOP_TABLESWITCH) \ @@ -207,6 +209,7 @@ namespace jit { _(JSOP_GENERATOR) \ _(JSOP_INITIALYIELD) \ _(JSOP_YIELD) \ + _(JSOP_AWAIT) \ _(JSOP_DEBUGAFTERYIELD) \ _(JSOP_FINALYIELDRVAL) \ _(JSOP_RESUME) \ @@ -255,9 +258,9 @@ class BaselineCompiler : public BaselineCompilerSpecific // equivalent positions when debug mode is off. CodeOffset postDebugPrologueOffset_; - // For each INITIALYIELD or YIELD op, this Vector maps the yield index - // to the bytecode offset of the next op. - Vector<uint32_t> yieldOffsets_; + // For each INITIALYIELD or YIELD or AWAIT op, this Vector maps the yield + // index to the bytecode offset of the next op. + Vector<uint32_t> yieldAndAwaitOffsets_; // Whether any on stack arguments are modified. bool modifiesArguments_; @@ -349,7 +352,7 @@ class BaselineCompiler : public BaselineCompilerSpecific MOZ_MUST_USE bool addPCMappingEntry(bool addIndexEntry); - MOZ_MUST_USE bool addYieldOffset(); + MOZ_MUST_USE bool addYieldAndAwaitOffset(); void getEnvironmentCoordinateObject(Register reg); Address getEnvironmentCoordinateAddressFromObject(Register objReg, Register reg); diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index a001357f8..e65f10aac 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -4053,9 +4053,6 @@ TryAttachSetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, IC { MOZ_ASSERT(!*attached); - if (obj->watched()) - return true; - RootedShape shape(cx); RootedObject holder(cx); if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape)) @@ -4151,9 +4148,6 @@ TryAttachSetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecode* pc, MOZ_ASSERT(!*attached); MOZ_ASSERT(!*isTemporarilyUnoptimizable); - if (obj->watched()) - return true; - RootedShape shape(cx); RootedObject holder(cx); if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape)) @@ -6606,12 +6600,13 @@ ICCall_IsSuspendedStarGenerator::Compiler::generateStubCode(MacroAssembler& masm masm.branchTestObjClass(Assembler::NotEqual, genObj, scratch, &StarGeneratorObject::class_, &returnFalse); - // If the yield index slot holds an int32 value < YIELD_INDEX_CLOSING, + // If the yield index slot holds an int32 value < YIELD_AND_AWAIT_INDEX_CLOSING, // the generator is suspended. - masm.loadValue(Address(genObj, GeneratorObject::offsetOfYieldIndexSlot()), argVal); + masm.loadValue(Address(genObj, GeneratorObject::offsetOfYieldAndAwaitIndexSlot()), argVal); masm.branchTestInt32(Assembler::NotEqual, argVal, &returnFalse); masm.unboxInt32(argVal, scratch); - masm.branch32(Assembler::AboveOrEqual, scratch, Imm32(StarGeneratorObject::YIELD_INDEX_CLOSING), + masm.branch32(Assembler::AboveOrEqual, scratch, + Imm32(StarGeneratorObject::YIELD_AND_AWAIT_INDEX_CLOSING), &returnFalse); masm.moveValue(BooleanValue(true), R0); diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index 5c21926b5..94e018252 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -760,12 +760,12 @@ BaselineScript::icEntryFromReturnAddress(uint8_t* returnAddr) } void -BaselineScript::copyYieldEntries(JSScript* script, Vector<uint32_t>& yieldOffsets) +BaselineScript::copyYieldAndAwaitEntries(JSScript* script, Vector<uint32_t>& yieldAndAwaitOffsets) { uint8_t** entries = yieldEntryList(); - for (size_t i = 0; i < yieldOffsets.length(); i++) { - uint32_t offset = yieldOffsets[i]; + for (size_t i = 0; i < yieldAndAwaitOffsets.length(); i++) { + uint32_t offset = yieldAndAwaitOffsets[i]; entries[i] = nativeCodeForPC(script, script->offsetToPC(offset)); } } diff --git a/js/src/jit/BaselineJIT.h b/js/src/jit/BaselineJIT.h index 5e7775a61..b6ccd2ff9 100644 --- a/js/src/jit/BaselineJIT.h +++ b/js/src/jit/BaselineJIT.h @@ -397,7 +397,7 @@ struct BaselineScript void copyICEntries(JSScript* script, const BaselineICEntry* entries, MacroAssembler& masm); void adoptFallbackStubs(FallbackICStubSpace* stubSpace); - void copyYieldEntries(JSScript* script, Vector<uint32_t>& yieldOffsets); + void copyYieldAndAwaitEntries(JSScript* script, Vector<uint32_t>& yieldAndAwaitOffsets); PCMappingIndexEntry& pcMappingIndexEntry(size_t index); CompactBufferReader pcMappingReader(size_t indexEntry); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 205812942..2b1c671d1 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -41,6 +41,7 @@ #include "jit/RangeAnalysis.h" #include "jit/SharedICHelpers.h" #include "vm/AsyncFunction.h" +#include "vm/AsyncIteration.h" #include "vm/MatchPairs.h" #include "vm/RegExpObject.h" #include "vm/RegExpStatics.h" @@ -10213,6 +10214,28 @@ CodeGenerator::visitToAsync(LToAsync* lir) callVM(ToAsyncInfo, lir); } +typedef JSObject* (*ToAsyncGenFn)(JSContext*, HandleFunction); +static const VMFunction ToAsyncGenInfo = + FunctionInfo<ToAsyncGenFn>(js::WrapAsyncGenerator, "ToAsyncGen"); + +void +CodeGenerator::visitToAsyncGen(LToAsyncGen* lir) +{ + pushArg(ToRegister(lir->unwrapped())); + callVM(ToAsyncGenInfo, lir); +} + +typedef JSObject* (*ToAsyncIterFn)(JSContext*, HandleObject); +static const VMFunction ToAsyncIterInfo = + FunctionInfo<ToAsyncIterFn>(js::CreateAsyncFromSyncIterator, "ToAsyncIter"); + +void +CodeGenerator::visitToAsyncIter(LToAsyncIter* lir) +{ + pushArg(ToRegister(lir->unwrapped())); + callVM(ToAsyncIterInfo, lir); +} + typedef bool (*ToIdFn)(JSContext*, HandleScript, jsbytecode*, HandleValue, MutableHandleValue); static const VMFunction ToIdInfo = FunctionInfo<ToIdFn>(ToIdOperation, "ToIdOperation"); diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 12f1238ef..65acfe274 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -286,6 +286,8 @@ class CodeGenerator final : public CodeGeneratorSpecific void visitTypeOfV(LTypeOfV* lir); void visitOutOfLineTypeOfV(OutOfLineTypeOfV* ool); void visitToAsync(LToAsync* lir); + void visitToAsyncGen(LToAsyncGen* lir); + void visitToAsyncIter(LToAsyncIter* lir); void visitToIdV(LToIdV* lir); template<typename T> void emitLoadElementT(LLoadElementT* lir, const T& source); void visitLoadElementT(LLoadElementT* lir); diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h index 1d0506f74..9c864515d 100644 --- a/js/src/jit/InlinableNatives.h +++ b/js/src/jit/InlinableNatives.h @@ -119,7 +119,6 @@ _(IntrinsicGuardToArrayIterator) \ _(IntrinsicGuardToMapIterator) \ _(IntrinsicGuardToSetIterator) \ - _(IntrinsicIsListIterator) \ _(IntrinsicGuardToStringIterator) \ \ _(IntrinsicGuardToMapObject) \ diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index b8a2d2fba..9337f6150 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2320,7 +2320,9 @@ IonCompile(JSContext* cx, JSScript* script, static bool CheckFrame(JSContext* cx, BaselineFrame* frame) { - MOZ_ASSERT(!frame->script()->isGenerator()); + MOZ_ASSERT(!frame->script()->isStarGenerator()); + MOZ_ASSERT(!frame->script()->isLegacyGenerator()); + MOZ_ASSERT(!frame->script()->isAsync()); MOZ_ASSERT(!frame->isDebuggerEvalFrame()); MOZ_ASSERT(!frame->isEvalFrame()); @@ -2351,11 +2353,16 @@ CheckScript(JSContext* cx, JSScript* script, bool osr) return false; } - if (script->isGenerator()) { + if (script->isStarGenerator() || script->isLegacyGenerator()) { TrackAndSpewIonAbort(cx, script, "generator script"); return false; } + if (script->isAsync()) { + TrackAndSpewIonAbort(cx, script, "async script"); + return false; + } + if (script->hasNonSyntacticScope() && !script->functionNonDelazifying()) { // Support functions with a non-syntactic global scope but not other // scripts. For global scripts, IonBuilder currently uses the global diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index 5fc624fb1..a4724bca4 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -4005,7 +4005,7 @@ jit::ConvertLinearInequality(TempAllocator& alloc, MBasicBlock* block, const Lin } static bool -AnalyzePoppedThis(JSContext* cx, ObjectGroup* group, +AnalyzePoppedThis(JSContext* cx, DPAConstraintInfo& constraintInfo, ObjectGroup* group, MDefinition* thisValue, MInstruction* ins, bool definitelyExecuted, HandlePlainObject baseobj, Vector<TypeNewScript::Initializer>* initializerList, @@ -4046,7 +4046,12 @@ AnalyzePoppedThis(JSContext* cx, ObjectGroup* group, return true; RootedId id(cx, NameToId(setprop->name())); - if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, group, id)) { + bool added = false; + if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, constraintInfo, + group, id, &added)) { + return false; + } + if (!added) { // The prototype chain already contains a getter/setter for this // property, or type information is too imprecise. return true; @@ -4106,7 +4111,12 @@ AnalyzePoppedThis(JSContext* cx, ObjectGroup* group, if (!baseobj->lookup(cx, id) && !accessedProperties->append(get->name())) return false; - if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, group, id)) { + bool added = false; + if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, constraintInfo, + group, id, &added)) { + return false; + } + if (!added) { // The |this| value can escape if any property reads it does go // through a getter. return true; @@ -4132,8 +4142,11 @@ CmpInstructions(const void* a, const void* b) } bool -jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx, HandleFunction fun, - ObjectGroup* group, HandlePlainObject baseobj, +jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx, + DPAConstraintInfo& constraintInfo, + HandleFunction fun, + ObjectGroup* group, + HandlePlainObject baseobj, Vector<TypeNewScript::Initializer>* initializerList) { MOZ_ASSERT(cx->zone()->types.activeAnalysis); @@ -4293,7 +4306,7 @@ jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx, HandleFunction fun, bool handled = false; size_t slotSpan = baseobj->slotSpan(); - if (!AnalyzePoppedThis(cx, group, thisValue, ins, definitelyExecuted, + if (!AnalyzePoppedThis(cx, constraintInfo, group, thisValue, ins, definitelyExecuted, baseobj, initializerList, &accessedProperties, &handled)) { return false; @@ -4312,7 +4325,6 @@ jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx, HandleFunction fun, // contingent on the correct frames being inlined. Add constraints to // invalidate the definite properties if additional functions could be // called at the inline frame sites. - Vector<MBasicBlock*> exitBlocks(cx); for (MBasicBlockIterator block(graph.begin()); block != graph.end(); block++) { // Inlining decisions made after the last new property was added to // the object don't need to be frozen. @@ -4320,9 +4332,11 @@ jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx, HandleFunction fun, break; if (MResumePoint* rp = block->callerResumePoint()) { if (block->numPredecessors() == 1 && block->getPredecessor(0) == rp->block()) { - JSScript* script = rp->block()->info().script(); - if (!AddClearDefiniteFunctionUsesInScript(cx, group, script, block->info().script())) + JSScript* caller = rp->block()->info().script(); + JSScript* callee = block->info().script(); + if (!constraintInfo.addInliningConstraint(caller, callee)) { return false; + } } } } @@ -4388,8 +4402,14 @@ jit::AnalyzeArgumentsUsage(JSContext* cx, JSScript* scriptArg) // direct eval is present. // // FIXME: Don't build arguments for ES6 generator expressions. - if (scriptArg->isDebuggee() || script->isGenerator() || script->bindingsAccessedDynamically()) + if (scriptArg->isDebuggee() || + script->isStarGenerator() || + script->isLegacyGenerator() || + script->isAsync() || + script->bindingsAccessedDynamically()) + { return true; + } if (!jit::IsIonEnabled(cx)) return true; diff --git a/js/src/jit/IonAnalysis.h b/js/src/jit/IonAnalysis.h index efd31415b..49bc0b591 100644 --- a/js/src/jit/IonAnalysis.h +++ b/js/src/jit/IonAnalysis.h @@ -196,8 +196,11 @@ MCompare* ConvertLinearInequality(TempAllocator& alloc, MBasicBlock* block, const LinearSum& sum); MOZ_MUST_USE bool -AnalyzeNewScriptDefiniteProperties(JSContext* cx, HandleFunction fun, - ObjectGroup* group, HandlePlainObject baseobj, +AnalyzeNewScriptDefiniteProperties(JSContext* cx, + DPAConstraintInfo& constraintInfo, + HandleFunction fun, + ObjectGroup* group, + HandlePlainObject baseobj, Vector<TypeNewScript::Initializer>* initializerList); MOZ_MUST_USE bool diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 2d053de5a..1e12f5dbe 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -2130,6 +2130,12 @@ IonBuilder::inspectOpcode(JSOp op) case JSOP_TOASYNC: return jsop_toasync(); + case JSOP_TOASYNCGEN: + return jsop_toasyncgen(); + + case JSOP_TOASYNCITER: + return jsop_toasynciter(); + case JSOP_TOID: return jsop_toid(); @@ -3351,7 +3357,7 @@ IonBuilder::whileOrForInLoop(jssrcnote* sn) unsigned stackPhiCount; if (SN_TYPE(sn) == SRC_FOR_OF) - stackPhiCount = 2; + stackPhiCount = 3; else if (SN_TYPE(sn) == SRC_FOR_IN) stackPhiCount = 1; else @@ -8895,10 +8901,8 @@ IonBuilder::jsop_getimport(PropertyName* name) if (!emitted) { // This can happen if we don't have type information. - TypeSet::ObjectKey* staticKey = TypeSet::ObjectKey::get(targetEnv); TemporaryTypeSet* types = bytecodeTypes(pc); - BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), staticKey, - name, types, /* updateObserved = */ true); + BarrierKind barrier = BarrierKind::TypeSet; if (!loadStaticSlot(targetEnv, barrier, types, shape->slot())) return false; @@ -13198,6 +13202,34 @@ IonBuilder::jsop_toasync() } bool +IonBuilder::jsop_toasyncgen() +{ + MDefinition* unwrapped = current->pop(); + MOZ_ASSERT(unwrapped->type() == MIRType::Object); + + MToAsyncGen* ins = MToAsyncGen::New(alloc(), unwrapped); + + current->add(ins); + current->push(ins); + + return resumeAfter(ins); +} + +bool +IonBuilder::jsop_toasynciter() +{ + MDefinition* unwrapped = current->pop(); + MOZ_ASSERT(unwrapped->type() == MIRType::Object); + + MToAsyncIter* ins = MToAsyncIter::New(alloc(), unwrapped); + + current->add(ins); + current->push(ins); + + return resumeAfter(ins); +} + +bool IonBuilder::jsop_toid() { // No-op if the index is an integer. diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 1f763a4f2..6a3b61232 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -762,6 +762,8 @@ class IonBuilder MOZ_MUST_USE bool jsop_globalthis(); MOZ_MUST_USE bool jsop_typeof(); MOZ_MUST_USE bool jsop_toasync(); + MOZ_MUST_USE bool jsop_toasyncgen(); + MOZ_MUST_USE bool jsop_toasynciter(); MOZ_MUST_USE bool jsop_toid(); MOZ_MUST_USE bool jsop_iter(uint8_t flags); MOZ_MUST_USE bool jsop_itermore(); diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index 48e0792bb..fb4291188 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -3232,7 +3232,7 @@ SetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* MOZ_ASSERT(!*emitted); MOZ_ASSERT(!*tryNativeAddSlot); - if (!canAttachStub() || obj->watched()) + if (!canAttachStub()) return true; // Fail cache emission if the object is frozen @@ -3897,9 +3897,6 @@ IsDenseElementSetInlineable(JSObject* obj, const Value& idval, const ConstantOrR if (!obj->is<ArrayObject>()) return false; - if (obj->watched()) - return false; - if (!idval.isInt32()) return false; diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 22f1d5f70..108450983 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -1176,6 +1176,22 @@ LIRGenerator::visitToAsync(MToAsync* ins) } void +LIRGenerator::visitToAsyncGen(MToAsyncGen* ins) +{ + LToAsyncGen* lir = new(alloc()) LToAsyncGen(useRegisterAtStart(ins->input())); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void +LIRGenerator::visitToAsyncIter(MToAsyncIter* ins) +{ + LToAsyncIter* lir = new(alloc()) LToAsyncIter(useRegisterAtStart(ins->input())); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + +void LIRGenerator::visitToId(MToId* ins) { LToIdV* lir = new(alloc()) LToIdV(useBox(ins->input()), tempDouble()); diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 9342ef471..81e6abbbb 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -121,6 +121,8 @@ class LIRGenerator : public LIRGeneratorSpecific void visitCompare(MCompare* comp); void visitTypeOf(MTypeOf* ins); void visitToAsync(MToAsync* ins); + void visitToAsyncGen(MToAsyncGen* ins); + void visitToAsyncIter(MToAsyncIter* ins); void visitToId(MToId* ins); void visitBitNot(MBitNot* ins); void visitBitAnd(MBitAnd* ins); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 5eee30e49..236354530 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -285,8 +285,6 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) return inlineGuardToClass(callInfo, &SetIteratorObject::class_); case InlinableNative::IntrinsicGuardToStringIterator: return inlineGuardToClass(callInfo, &StringIteratorObject::class_); - case InlinableNative::IntrinsicIsListIterator: - return inlineHasClass(callInfo, &ListIteratorObject::class_); case InlinableNative::IntrinsicDefineDataProperty: return inlineDefineDataProperty(callInfo); case InlinableNative::IntrinsicObjectHasPrototype: diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 6c376d528..e7186ed30 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -5784,6 +5784,36 @@ class MToAsync TRIVIAL_NEW_WRAPPERS }; +class MToAsyncGen + : public MUnaryInstruction, + public SingleObjectPolicy::Data +{ + explicit MToAsyncGen(MDefinition* unwrapped) + : MUnaryInstruction(unwrapped) + { + setResultType(MIRType::Object); + } + + public: + INSTRUCTION_HEADER(ToAsyncGen) + TRIVIAL_NEW_WRAPPERS +}; + +class MToAsyncIter + : public MUnaryInstruction, + public SingleObjectPolicy::Data +{ + explicit MToAsyncIter(MDefinition* unwrapped) + : MUnaryInstruction(unwrapped) + { + setResultType(MIRType::Object); + } + + public: + INSTRUCTION_HEADER(ToAsyncIter) + TRIVIAL_NEW_WRAPPERS +}; + class MToId : public MUnaryInstruction, public BoxInputsPolicy::Data diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 9e460a2ba..589dde077 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -79,6 +79,8 @@ namespace jit { _(BitNot) \ _(TypeOf) \ _(ToAsync) \ + _(ToAsyncGen) \ + _(ToAsyncIter) \ _(ToId) \ _(BitAnd) \ _(BitOr) \ diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 1ff7adfd1..652c23bf1 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -735,7 +735,7 @@ bool NormalSuspend(JSContext* cx, HandleObject obj, BaselineFrame* frame, jsbytecode* pc, uint32_t stackDepth) { - MOZ_ASSERT(*pc == JSOP_YIELD); + MOZ_ASSERT(*pc == JSOP_YIELD || *pc == JSOP_AWAIT); // Return value is still on the stack. MOZ_ASSERT(stackDepth >= 1); @@ -816,7 +816,7 @@ GeneratorThrowOrClose(JSContext* cx, BaselineFrame* frame, Handle<GeneratorObjec // work. This function always returns false, so we're guaranteed to enter // the exception handler where we will clear the pc. JSScript* script = frame->script(); - uint32_t offset = script->yieldOffsets()[genObj->yieldIndex()]; + uint32_t offset = script->yieldAndAwaitOffsets()[genObj->yieldAndAwaitIndex()]; frame->setOverridePc(script->offsetToPC(offset)); MOZ_ALWAYS_TRUE(DebugAfterYield(cx, frame)); diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index a4161ab00..3421001f7 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -3462,8 +3462,8 @@ MacroAssemblerARMCompat::storePayload(const Value& val, const Address& dest) ScratchRegisterScope scratch(asMasm()); SecondScratchRegisterScope scratch2(asMasm()); - if (val.isMarkable()) - ma_mov(ImmGCPtr(val.toMarkablePointer()), scratch); + if (val.isGCThing()) + ma_mov(ImmGCPtr(val.toGCThing()), scratch); else ma_mov(Imm32(val.toNunboxPayload()), scratch); ma_str(scratch, ToPayload(dest), scratch2); diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index ecf02816e..ff4915d1a 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -1606,6 +1606,32 @@ class LToAsync : public LCallInstructionHelper<1, 1, 0> } }; +class LToAsyncGen : public LCallInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(ToAsyncGen) + explicit LToAsyncGen(const LAllocation& input) { + setOperand(0, input); + } + + const LAllocation* unwrapped() { + return getOperand(0); + } +}; + +class LToAsyncIter : public LCallInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(ToAsyncIter) + explicit LToAsyncIter(const LAllocation& input) { + setOperand(0, input); + } + + const LAllocation* unwrapped() { + return getOperand(0); + } +}; + class LToIdV : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 1> { public: diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index e260d7e94..56b98940a 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -353,6 +353,8 @@ _(Rest) \ _(TypeOfV) \ _(ToAsync) \ + _(ToAsyncGen) \ + _(ToAsyncIter) \ _(ToIdV) \ _(Floor) \ _(FloorF) \ diff --git a/js/src/js.msg b/js/src/js.msg index f57b36a90..9dc5f4e9f 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -47,7 +47,6 @@ MSG_DEF(JSMSG_MORE_ARGS_NEEDED, 3, JSEXN_TYPEERR, "{0} requires more than MSG_DEF(JSMSG_INCOMPATIBLE_PROTO, 3, JSEXN_TYPEERR, "{0}.prototype.{1} called on incompatible {2}") MSG_DEF(JSMSG_NO_CONSTRUCTOR, 1, JSEXN_TYPEERR, "{0} has no constructor") MSG_DEF(JSMSG_BAD_SORT_ARG, 0, JSEXN_TYPEERR, "invalid Array.prototype.sort argument") -MSG_DEF(JSMSG_CANT_WATCH, 1, JSEXN_TYPEERR, "can't watch non-native objects of class {0}") MSG_DEF(JSMSG_READ_ONLY, 1, JSEXN_TYPEERR, "{0} is read-only") MSG_DEF(JSMSG_CANT_DELETE, 1, JSEXN_TYPEERR, "property {0} is non-configurable and can't be deleted") MSG_DEF(JSMSG_CANT_TRUNCATE_ARRAY, 0, JSEXN_TYPEERR, "can't delete non-configurable array element") @@ -72,7 +71,6 @@ MSG_DEF(JSMSG_UNDEFINED_PROP, 1, JSEXN_REFERENCEERR, "reference to unde MSG_DEF(JSMSG_INVALID_MAP_ITERABLE, 1, JSEXN_TYPEERR, "iterable for {0} should have array-like objects") MSG_DEF(JSMSG_NESTING_GENERATOR, 0, JSEXN_TYPEERR, "already executing generator") MSG_DEF(JSMSG_INCOMPATIBLE_METHOD, 3, JSEXN_TYPEERR, "{0} {1} called on incompatible {2}") -MSG_DEF(JSMSG_OBJECT_WATCH_DEPRECATED, 0, JSEXN_WARN, "Object.prototype.watch and unwatch are very slow, non-standard, and deprecated; use a getter/setter instead") MSG_DEF(JSMSG_ARRAYBUFFER_SLICE_DEPRECATED, 0, JSEXN_WARN, "ArrayBuffer.slice is deprecated; use ArrayBuffer.prototype.slice instead") MSG_DEF(JSMSG_BAD_SURROGATE_CHAR, 1, JSEXN_TYPEERR, "bad surrogate character {0}") MSG_DEF(JSMSG_UTF8_CHAR_TOO_LARGE, 1, JSEXN_TYPEERR, "UTF-8 character {0} too large") @@ -134,6 +132,8 @@ MSG_DEF(JSMSG_INVALID_NORMALIZE_FORM, 0, JSEXN_RANGEERR, "form must be one of ' MSG_DEF(JSMSG_NEGATIVE_REPETITION_COUNT, 0, JSEXN_RANGEERR, "repeat count must be non-negative") MSG_DEF(JSMSG_NOT_A_CODEPOINT, 1, JSEXN_RANGEERR, "{0} is not a valid code point") MSG_DEF(JSMSG_RESULTING_STRING_TOO_LARGE, 0, JSEXN_RANGEERR, "repeat count must be less than infinity and not overflow maximum string size") +MSG_DEF(JSMSG_FLAGS_UNDEFINED_OR_NULL, 0, JSEXN_TYPEERR, "'flags' property must neither be undefined nor null") +MSG_DEF(JSMSG_REQUIRES_GLOBAL_REGEXP, 1, JSEXN_TYPEERR, "{0} must be called with a global RegExp") // Number MSG_DEF(JSMSG_BAD_RADIX, 0, JSEXN_RANGEERR, "radix must be an integer at least 2 and no greater than 36") @@ -185,7 +185,6 @@ MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 0, JSEXN_SYNTAXERR, "invalid array compre MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG, 0, JSEXN_INTERNALERR, "array initializer too large") MSG_DEF(JSMSG_AS_AFTER_IMPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'as' after import *") MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD, 1, JSEXN_SYNTAXERR, "missing keyword 'as' after reserved word '{0}'") -MSG_DEF(JSMSG_ASYNC_GENERATOR, 0, JSEXN_SYNTAXERR, "generator function or method can't be async") MSG_DEF(JSMSG_AWAIT_IN_DEFAULT, 0, JSEXN_SYNTAXERR, "await can't be used in default expression") MSG_DEF(JSMSG_AWAIT_OUTSIDE_ASYNC, 0, JSEXN_SYNTAXERR, "await is only valid in async functions") MSG_DEF(JSMSG_BAD_ARROW_ARGS, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)") @@ -438,7 +437,6 @@ MSG_DEF(JSMSG_SC_SAB_DISABLED, 0, JSEXN_TYPEERR, "SharedArrayBuffer not // Debugger MSG_DEF(JSMSG_ASSIGN_FUNCTION_OR_NULL, 1, JSEXN_TYPEERR, "value assigned to {0} must be a function or null") -MSG_DEF(JSMSG_DEBUG_BAD_AWAIT, 0, JSEXN_TYPEERR, "await expression received invalid value") MSG_DEF(JSMSG_DEBUG_BAD_LINE, 0, JSEXN_TYPEERR, "invalid line number") MSG_DEF(JSMSG_DEBUG_BAD_OFFSET, 0, JSEXN_TYPEERR, "invalid script offset") MSG_DEF(JSMSG_DEBUG_BAD_REFERENT, 2, JSEXN_TYPEERR, "{0} does not refer to {1}") @@ -594,3 +592,9 @@ MSG_DEF(JSMSG_PROMISE_ERROR_IN_WRAPPED_REJECTION_REASON,0, JSEXN_INTERNALERR, "P // Iterator MSG_DEF(JSMSG_RETURN_NOT_CALLABLE, 0, JSEXN_TYPEERR, "property 'return' of iterator is not callable") MSG_DEF(JSMSG_ITERATOR_NO_THROW, 0, JSEXN_TYPEERR, "iterator does not have a 'throw' method") + +// Async Iteration +MSG_DEF(JSMSG_FOR_AWAIT_NOT_OF, 0, JSEXN_TYPEERR, "'for await' loop should be used with 'of'") +MSG_DEF(JSMSG_NOT_AN_ASYNC_GENERATOR, 0, JSEXN_TYPEERR, "Not an async generator") +MSG_DEF(JSMSG_NOT_AN_ASYNC_ITERATOR, 0, JSEXN_TYPEERR, "Not an async from sync iterator") +MSG_DEF(JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.asyncIterator]() returned a non-object value") diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build index c176fbf0a..f3757f5a4 100644 --- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build @@ -77,6 +77,7 @@ UNIFIED_SOURCES += [ 'testPrintf.cpp', 'testPrivateGCThingValue.cpp', 'testProfileStrings.cpp', + 'testPromise.cpp', 'testPropCache.cpp', 'testRegExp.cpp', 'testResolveRecursion.cpp', @@ -124,11 +125,6 @@ if CONFIG['ENABLE_ION']: 'testJitRValueAlloc.cpp', ] -if CONFIG['SPIDERMONKEY_PROMISE']: - UNIFIED_SOURCES += [ - 'testPromise.cpp', - ] - DEFINES['EXPORT_JS_API'] = True LOCAL_INCLUDES += [ diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 84a315587..6483641f3 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -39,7 +39,6 @@ #include "jsstr.h" #include "jstypes.h" #include "jsutil.h" -#include "jswatchpoint.h" #include "jsweakmap.h" #include "jswrapper.h" @@ -71,6 +70,7 @@ #include "js/StructuredClone.h" #include "js/Utility.h" #include "vm/AsyncFunction.h" +#include "vm/AsyncIteration.h" #include "vm/DateObject.h" #include "vm/Debugger.h" #include "vm/EnvironmentObject.h" @@ -2011,6 +2011,28 @@ JS_GetOwnUCPropertyDescriptor(JSContext* cx, HandleObject obj, const char16_t* n } JS_PUBLIC_API(bool) +JS_GetOwnElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::MutableHandleValue vp) +{ + RootedId id(cx); + if (!IndexToId(cx, index, &id)) { + return false; + } + + Rooted<PropertyDescriptor> desc(cx); + if (!JS_GetOwnPropertyDescriptorById(cx, obj, id, &desc)) { + return false; + } + + if (desc.object() && desc.isDataDescriptor()) { + vp.set(desc.value()); + } else { + vp.setUndefined(); + } + + return true; +} + +JS_PUBLIC_API(bool) JS_GetPropertyDescriptorById(JSContext* cx, HandleObject obj, HandleId id, MutableHandle<PropertyDescriptor> desc) { @@ -3570,6 +3592,11 @@ CloneFunctionObject(JSContext* cx, HandleObject funobj, HandleObject env, Handle return nullptr; } + if (IsWrappedAsyncGenerator(fun)) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT); + return nullptr; + } + if (CanReuseScriptForClone(cx->compartment(), fun, env)) { // If the script is to be reused, either the script can already handle // non-syntactic scopes, or there is only the standard global lexical diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 67b3d4267..7795c5e4c 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2922,6 +2922,9 @@ extern JS_PUBLIC_API(bool) JS_GetOwnUCPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen, JS::MutableHandle<JS::PropertyDescriptor> desc); +extern JS_PUBLIC_API(bool) +JS_GetOwnElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::MutableHandleValue vp); + /** * Like JS_GetOwnPropertyDescriptorById, but also searches the prototype chain * if no own property is found directly on obj. The object on which the @@ -5084,7 +5087,9 @@ GetSymbolDescription(HandleSymbol symbol); macro(split) \ macro(toPrimitive) \ macro(toStringTag) \ - macro(unscopables) + macro(unscopables) \ + macro(asyncIterator) \ + macro(matchAll) enum class SymbolCode : uint32_t { // There is one SymbolCode for each well-known symbol. @@ -5701,6 +5706,7 @@ JS_ObjectIsDate(JSContext* cx, JS::HandleObject obj, bool* isDate); #define JSREG_MULTILINE 0x04u /* treat ^ and $ as begin and end of line */ #define JSREG_STICKY 0x08u /* only match starting at lastIndex */ #define JSREG_UNICODE 0x10u /* unicode */ +#define JSREG_DOTALL 0x20u /* match . to everything including newlines */ extern JS_PUBLIC_API(JSObject*) JS_NewRegExpObject(JSContext* cx, const char* bytes, size_t length, unsigned flags); diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index e505a4b34..23b9d27ae 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -37,7 +37,6 @@ #include "jsscript.h" #include "jsstr.h" #include "jstypes.h" -#include "jswatchpoint.h" #include "gc/Marking.h" #include "jit/Ion.h" diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index d0caeb558..8ba186b08 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -13,7 +13,6 @@ #include "jsfriendapi.h" #include "jsgc.h" #include "jsiter.h" -#include "jswatchpoint.h" #include "jswrapper.h" #include "gc/Marking.h" @@ -76,7 +75,6 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options = gcIncomingGrayPointers(nullptr), debugModeBits(0), randomKeyGenerator_(runtime_->forkRandomKeyGenerator()), - watchpointMap(nullptr), scriptCountsMap(nullptr), debugScriptMap(nullptr), debugEnvs(nullptr), @@ -103,7 +101,6 @@ JSCompartment::~JSCompartment() rt->lcovOutput.writeLCovResult(lcovOutput); js_delete(jitCompartment_); - js_delete(watchpointMap); js_delete(scriptCountsMap); js_delete(debugScriptMap); js_delete(debugEnvs); @@ -662,12 +659,6 @@ JSCompartment::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime t if (traceOrMark == js::gc::GCRuntime::MarkRuntime && !zone()->isCollecting()) return; - // During a GC, these are treated as weak pointers. - if (traceOrMark == js::gc::GCRuntime::TraceRuntime) { - if (watchpointMap) - watchpointMap->markAll(trc); - } - /* Mark debug scopes, if present */ if (debugEnvs) debugEnvs->mark(trc); @@ -712,9 +703,6 @@ JSCompartment::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime t void JSCompartment::finishRoots() { - if (watchpointMap) - watchpointMap->clear(); - if (debugEnvs) debugEnvs->finish(); diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 83c15da3b..05ff40b43 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -282,7 +282,6 @@ class MOZ_RAII AutoSetNewObjectMetadata : private JS::CustomAutoRooter namespace js { class DebugEnvironments; class ObjectWeakMap; -class WatchpointMap; class WeakMapBase; } // namespace js @@ -811,8 +810,6 @@ struct JSCompartment void sweepBreakpoints(js::FreeOp* fop); public: - js::WatchpointMap* watchpointMap; - js::ScriptCountsMap* scriptCountsMap; js::DebugScriptMap* debugScriptMap; diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index 65cc81a1a..72f03ea55 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -32,6 +32,7 @@ #include "vm/ErrorObject.h" #include "vm/GlobalObject.h" #include "vm/SavedStacks.h" +#include "vm/SelfHosting.h" #include "vm/StringBuffer.h" #include "jsobjinlines.h" @@ -205,7 +206,12 @@ size_t ExtraMallocSize(JSErrorReport* report) { if (report->linebuf()) - return (report->linebufLength() + 1) * sizeof(char16_t); + /* + * Mozilla bug 1352449. Count with null + * terminator and alignment. See CopyExtraData for + * the details about alignment. + */ + return (report->linebufLength() + 1) * sizeof(char16_t) + 1; return 0; } @@ -220,10 +226,20 @@ bool CopyExtraData(JSContext* cx, uint8_t** cursor, JSErrorReport* copy, JSErrorReport* report) { if (report->linebuf()) { + /* + * Make sure cursor is properly aligned for char16_t for platforms + * which need it and it's at the end of the buffer on exit. + */ + size_t alignment_backlog = 0; + if (size_t(*cursor) % 2) + (*cursor)++; + else + alignment_backlog = 1; + size_t linebufSize = (report->linebufLength() + 1) * sizeof(char16_t); const char16_t* linebufCopy = (const char16_t*)(*cursor); js_memcpy(*cursor, report->linebuf(), linebufSize); - *cursor += linebufSize; + *cursor += linebufSize + alignment_backlog; copy->initBorrowedLinebuf(linebufCopy, report->linebufLength(), report->tokenOffset()); } @@ -1076,3 +1092,19 @@ js::ValueToSourceForError(JSContext* cx, HandleValue val, JSAutoByteString& byte return "<<error converting value to string>>"; return bytes.encodeLatin1(cx, str); } + +bool +js::GetInternalError(JSContext* cx, unsigned errorNumber, MutableHandleValue error) +{ + FixedInvokeArgs<1> args(cx); + args[0].set(Int32Value(errorNumber)); + return CallSelfHostedFunction(cx, "GetInternalError", NullHandleValue, args, error); +} + +bool +js::GetTypeError(JSContext* cx, unsigned errorNumber, MutableHandleValue error) +{ + FixedInvokeArgs<1> args(cx); + args[0].set(Int32Value(errorNumber)); + return CallSelfHostedFunction(cx, "GetTypeError", NullHandleValue, args, error); +} diff --git a/js/src/jsexn.h b/js/src/jsexn.h index 00120d89c..ee69e3c50 100644 --- a/js/src/jsexn.h +++ b/js/src/jsexn.h @@ -131,6 +131,11 @@ class AutoAssertNoPendingException extern const char* ValueToSourceForError(JSContext* cx, HandleValue val, JSAutoByteString& bytes); +bool +GetInternalError(JSContext* cx, unsigned errorNumber, MutableHandleValue error); +bool +GetTypeError(JSContext* cx, unsigned errorNumber, MutableHandleValue error); + } // namespace js #endif /* jsexn_h */ diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index f7622cb44..bdb3c0a4d 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -15,7 +15,6 @@ #include "jsgc.h" #include "jsobj.h" #include "jsprf.h" -#include "jswatchpoint.h" #include "jsweakmap.h" #include "jswrapper.h" @@ -579,7 +578,6 @@ void js::TraceWeakMaps(WeakMapTracer* trc) { WeakMapBase::traceAllMappings(trc); - WatchpointMap::traceAll(trc); } extern JS_FRIEND_API(bool) diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 351667fb3..7b59689ff 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -948,6 +948,7 @@ IsObjectInContextCompartment(JSObject* obj, const JSContext* cx); #define JSITER_HIDDEN 0x10 /* also enumerate non-enumerable properties */ #define JSITER_SYMBOLS 0x20 /* also include symbol property keys */ #define JSITER_SYMBOLSONLY 0x40 /* exclude string property keys */ +#define JSITER_FORAWAITOF 0x80 /* for-await-of */ JS_FRIEND_API(bool) RunningWithTrustedPrincipals(JSContext* cx); @@ -2110,30 +2111,6 @@ JS_FRIEND_API(void*) JS_GetDataViewData(JSObject* obj, const JS::AutoCheckCannotGC&); namespace js { - -/** - * Add a watchpoint -- in the Object.prototype.watch sense -- to |obj| for the - * property |id|, using the callable object |callable| as the function to be - * called for notifications. - * - * This is an internal function exposed -- temporarily -- only so that DOM - * proxies can be watchable. Don't use it! We'll soon kill off the - * Object.prototype.{,un}watch functions, at which point this will go too. - */ -extern JS_FRIEND_API(bool) -WatchGuts(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable); - -/** - * Remove a watchpoint -- in the Object.prototype.watch sense -- from |obj| for - * the property |id|. - * - * This is an internal function exposed -- temporarily -- only so that DOM - * proxies can be watchable. Don't use it! We'll soon kill off the - * Object.prototype.{,un}watch functions, at which point this will go too. - */ -extern JS_FRIEND_API(bool) -UnwatchGuts(JSContext* cx, JS::HandleObject obj, JS::HandleId id); - namespace jit { enum class InlinableNative : uint16_t; diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 9edf238ef..ce1ca33fe 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -41,6 +41,7 @@ #include "js/CallNonGenericMethod.h" #include "js/Proxy.h" #include "vm/AsyncFunction.h" +#include "vm/AsyncIteration.h" #include "vm/Debugger.h" #include "vm/GlobalObject.h" #include "vm/Interpreter.h" @@ -131,7 +132,11 @@ IsFunctionInStrictMode(JSFunction* fun) static bool IsNewerTypeFunction(JSFunction* fun) { - return fun->isArrow() || fun->isGenerator() || fun->isAsync() || fun->isMethod(); + return fun->isArrow() || + fun->isStarGenerator() || + fun->isLegacyGenerator() || + fun->isAsync() || + fun->isMethod(); } // Beware: this function can be invoked on *any* function! That includes @@ -308,6 +313,8 @@ CallerGetterImpl(JSContext* cx, const CallArgs& args) JSFunction* callerFun = &callerObj->as<JSFunction>(); if (IsWrappedAsyncFunction(callerFun)) callerFun = GetUnwrappedAsyncFunction(callerFun); + else if (IsWrappedAsyncGenerator(callerFun)) + callerFun = GetUnwrappedAsyncGenerator(callerFun); MOZ_ASSERT(!callerFun->isBuiltin(), "non-builtin iterator returned a builtin?"); if (callerFun->strict()) { @@ -360,13 +367,15 @@ static const JSPropertySpec function_properties[] = { static bool ResolveInterpretedFunctionPrototype(JSContext* cx, HandleFunction fun, HandleId id) { - MOZ_ASSERT(fun->isInterpreted() || fun->isAsmJSNative()); + bool isAsyncGenerator = IsWrappedAsyncGenerator(fun); + + MOZ_ASSERT_IF(!isAsyncGenerator, fun->isInterpreted() || fun->isAsmJSNative()); MOZ_ASSERT(id == NameToId(cx->names().prototype)); // Assert that fun is not a compiler-created function object, which // must never leak to script or embedding code and then be mutated. // Also assert that fun is not bound, per the ES5 15.3.4.5 ref above. - MOZ_ASSERT(!IsInternalFunctionObject(*fun)); + MOZ_ASSERT_IF(!isAsyncGenerator, !IsInternalFunctionObject(*fun)); MOZ_ASSERT(!fun->isBoundFunction()); // Make the prototype object an instance of Object with the same parent as @@ -376,7 +385,9 @@ ResolveInterpretedFunctionPrototype(JSContext* cx, HandleFunction fun, HandleId bool isStarGenerator = fun->isStarGenerator(); Rooted<GlobalObject*> global(cx, &fun->global()); RootedObject objProto(cx); - if (isStarGenerator) + if (isAsyncGenerator) + objProto = GlobalObject::getOrCreateAsyncGeneratorPrototype(cx, global); + else if (isStarGenerator) objProto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global); else objProto = GlobalObject::getOrCreateObjectPrototype(cx, global); @@ -392,7 +403,7 @@ ResolveInterpretedFunctionPrototype(JSContext* cx, HandleFunction fun, HandleId // non-enumerable, and writable. However, per the 15 July 2013 ES6 draft, // section 15.19.3, the .prototype of a generator function does not link // back with a .constructor. - if (!isStarGenerator) { + if (!isStarGenerator && !isAsyncGenerator) { RootedValue objVal(cx, ObjectValue(*fun)); if (!DefineProperty(cx, proto, cx->names().constructor, objVal, nullptr, nullptr, 0)) return false; @@ -442,8 +453,14 @@ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp) * - Arrow functions * - Function.prototype */ - if (fun->isBuiltin() || (!fun->isConstructor() && !fun->isGenerator())) - return true; + if (!IsWrappedAsyncGenerator(fun)) { + if (fun->isBuiltin()) + return true; + if (!fun->isConstructor()) { + if (!fun->isStarGenerator() && !fun->isLegacyGenerator() && !fun->isAsync()) + return true; + } + } if (!ResolveInterpretedFunctionPrototype(cx, fun, id)) return false; @@ -515,7 +532,7 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope, { enum FirstWordFlag { HasAtom = 0x1, - IsStarGenerator = 0x2, + HasStarGeneratorProto = 0x2, IsLazy = 0x4, HasSingletonType = 0x8 }; @@ -544,8 +561,8 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope, if (fun->explicitName() || fun->hasCompileTimeName() || fun->hasGuessedAtom()) firstword |= HasAtom; - if (fun->isStarGenerator()) - firstword |= IsStarGenerator; + if (fun->isStarGenerator() || fun->isAsync()) + firstword |= HasStarGeneratorProto; if (fun->isInterpretedLazy()) { // Encode a lazy script. @@ -581,7 +598,7 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope, if (mode == XDR_DECODE) { RootedObject proto(cx); - if (firstword & IsStarGenerator) { + if (firstword & HasStarGeneratorProto) { proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global()); if (!proto) return false; @@ -938,6 +955,11 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint) return FunctionToString(cx, unwrapped, prettyPrint); } + if (IsWrappedAsyncGenerator(fun)) { + RootedFunction unwrapped(cx, GetUnwrappedAsyncGenerator(fun)); + return FunctionToString(cx, unwrapped, prettyPrint); + } + StringBuffer out(cx); RootedScript script(cx); @@ -1556,7 +1578,7 @@ fun_isGenerator(JSContext* cx, unsigned argc, Value* vp) return true; } - args.rval().setBoolean(fun->isGenerator()); + args.rval().setBoolean(fun->isStarGenerator() || fun->isLegacyGenerator()); return true; } @@ -1588,8 +1610,6 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator bool isStarGenerator = generatorKind == StarGenerator; bool isAsync = asyncKind == AsyncFunction; MOZ_ASSERT(generatorKind != LegacyGenerator); - MOZ_ASSERT_IF(isAsync, isStarGenerator); - MOZ_ASSERT_IF(!isStarGenerator, !isAsync); RootedScript maybeScript(cx); const char* filename; @@ -1600,10 +1620,14 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator &mutedErrors); const char* introductionType = "Function"; - if (isAsync) - introductionType = "AsyncFunction"; - else if (generatorKind != NotGenerator) + if (isAsync) { + if (isStarGenerator) + introductionType = "AsyncGenerator"; + else + introductionType = "AsyncFunction"; + } else if (generatorKind != NotGenerator) { introductionType = "GeneratorFunction"; + } const char* introducerFilename = filename; if (maybeScript && maybeScript->scriptSource()->introducerFilename()) @@ -1701,7 +1725,7 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator // Step 4.d, use %Generator% as the fallback prototype. // Also use %Generator% for the unwrapped function of async functions. - if (!proto && isStarGenerator) { + if (!proto && (isStarGenerator || isAsync)) { proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, global); if (!proto) return false; @@ -1731,12 +1755,20 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator : SourceBufferHolder::NoOwnership; bool ok; SourceBufferHolder srcBuf(chars.begin().get(), chars.length(), ownership); - if (isAsync) - ok = frontend::CompileStandaloneAsyncFunction(cx, &fun, options, srcBuf, parameterListEnd); - else if (isStarGenerator) - ok = frontend::CompileStandaloneGenerator(cx, &fun, options, srcBuf, parameterListEnd); - else - ok = frontend::CompileStandaloneFunction(cx, &fun, options, srcBuf, parameterListEnd); + if (isAsync) { + if (isStarGenerator) { + ok = frontend::CompileStandaloneAsyncGenerator(cx, &fun, options, srcBuf, + parameterListEnd); + } else { + ok = frontend::CompileStandaloneAsyncFunction(cx, &fun, options, srcBuf, + parameterListEnd); + } + } else { + if (isStarGenerator) + ok = frontend::CompileStandaloneGenerator(cx, &fun, options, srcBuf, parameterListEnd); + else + ok = frontend::CompileStandaloneFunction(cx, &fun, options, srcBuf, parameterListEnd); + } // Step 33. args.rval().setObject(*fun); @@ -1762,14 +1794,14 @@ js::AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - // Save the callee before its reset in FunctionConstructor(). + // Save the callee before it's reset in FunctionConstructor(). RootedObject newTarget(cx); if (args.isConstructing()) newTarget = &args.newTarget().toObject(); else newTarget = &args.callee(); - if (!FunctionConstructor(cx, args, StarGenerator, AsyncFunction)) + if (!FunctionConstructor(cx, args, NotGenerator, AsyncFunction)) return false; // ES2017, draft rev 0f10dba4ad18de92d47d421f378233a2eae8f077 @@ -1795,6 +1827,40 @@ js::AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp) } bool +js::AsyncGeneratorConstructor(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // Save the callee before its reset in FunctionConstructor(). + RootedObject newTarget(cx); + if (args.isConstructing()) + newTarget = &args.newTarget().toObject(); + else + newTarget = &args.callee(); + + if (!FunctionConstructor(cx, args, StarGenerator, AsyncFunction)) + return false; + + RootedObject proto(cx); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + if (!proto) { + proto = GlobalObject::getOrCreateAsyncGenerator(cx, cx->global()); + if (!proto) + return false; + } + + RootedFunction unwrapped(cx, &args.rval().toObject().as<JSFunction>()); + RootedObject wrapped(cx, WrapAsyncGeneratorWithProto(cx, unwrapped, proto)); + if (!wrapped) + return false; + + args.rval().setObject(*wrapped); + return true; +} + +bool JSFunction::isBuiltinFunctionConstructor() { return maybeNative() == Function || maybeNative() == Generator; @@ -1965,7 +2031,7 @@ NewFunctionClone(JSContext* cx, HandleFunction fun, NewObjectKind newKind, gc::AllocKind allocKind, HandleObject proto) { RootedObject cloneProto(cx, proto); - if (!proto && fun->isStarGenerator()) { + if (!proto && (fun->isStarGenerator() || fun->isAsync())) { cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global()); if (!cloneProto) return nullptr; diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 234169507..ea9e7af9b 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -89,15 +89,15 @@ class JSFunction : public js::NativeObject ASMJS_CTOR = ASMJS_KIND | NATIVE_CTOR, ASMJS_LAMBDA_CTOR = ASMJS_KIND | NATIVE_CTOR | LAMBDA, INTERPRETED_METHOD = INTERPRETED | METHOD_KIND, - INTERPRETED_METHOD_GENERATOR = INTERPRETED | METHOD_KIND, + INTERPRETED_METHOD_GENERATOR_OR_ASYNC = INTERPRETED | METHOD_KIND, INTERPRETED_CLASS_CONSTRUCTOR = INTERPRETED | CLASSCONSTRUCTOR_KIND | CONSTRUCTOR, INTERPRETED_GETTER = INTERPRETED | GETTER_KIND, INTERPRETED_SETTER = INTERPRETED | SETTER_KIND, INTERPRETED_LAMBDA = INTERPRETED | LAMBDA | CONSTRUCTOR, INTERPRETED_LAMBDA_ARROW = INTERPRETED | LAMBDA | ARROW_KIND, - INTERPRETED_LAMBDA_GENERATOR = INTERPRETED | LAMBDA, + INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC = INTERPRETED | LAMBDA, INTERPRETED_NORMAL = INTERPRETED | CONSTRUCTOR, - INTERPRETED_GENERATOR = INTERPRETED, + INTERPRETED_GENERATOR_OR_ASYNC = INTERPRETED, NO_XDR_FLAGS = RESOLVED_LENGTH | RESOLVED_NAME, STABLE_ACROSS_CLONES = CONSTRUCTOR | HAS_GUESSED_ATOM | LAMBDA | @@ -146,7 +146,9 @@ class JSFunction : public js::NativeObject MOZ_ASSERT_IF(nonLazyScript()->funHasExtensibleScope() || nonLazyScript()->needsHomeObject() || nonLazyScript()->isDerivedClassConstructor() || - isGenerator(), + isStarGenerator() || + isLegacyGenerator() || + isAsync(), nonLazyScript()->bodyScope()->hasEnvironment()); return nonLazyScript()->bodyScope()->hasEnvironment(); @@ -501,8 +503,6 @@ class JSFunction : public js::NativeObject return js::NotGenerator; } - bool isGenerator() const { return generatorKind() != js::NotGenerator; } - bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; } bool isStarGenerator() const { return generatorKind() == js::StarGenerator; } @@ -513,9 +513,9 @@ class JSFunction : public js::NativeObject bool isAsync() const { if (isInterpretedLazy()) - return lazyScript()->asyncKind() == js::AsyncFunction; + return lazyScript()->isAsync(); if (hasScript()) - return nonLazyScript()->asyncKind() == js::AsyncFunction; + return nonLazyScript()->isAsync(); return false; } @@ -660,6 +660,9 @@ Generator(JSContext* cx, unsigned argc, Value* vp); extern bool AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp); +extern bool +AsyncGeneratorConstructor(JSContext* cx, unsigned argc, Value* vp); + // Allocate a new function backed by a JSNative. Note that by default this // creates a singleton object. extern JSFunction* diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index b2ee8d67b..5a9d732b6 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -207,7 +207,6 @@ #include "jsscript.h" #include "jstypes.h" #include "jsutil.h" -#include "jswatchpoint.h" #include "jsweakmap.h" #ifdef XP_WIN # include "jswin.h" @@ -2392,11 +2391,6 @@ GCRuntime::updatePointersToRelocatedCells(Zone* zone, AutoLockForExclusiveAccess Debugger::markIncomingCrossCompartmentEdges(&trc); WeakMapBase::markAll(zone, &trc); - for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) { - c->trace(&trc); - if (c->watchpointMap) - c->watchpointMap->markAll(&trc); - } // Mark all gray roots, making sure we call the trace callback to get the // current set. @@ -2405,7 +2399,6 @@ GCRuntime::updatePointersToRelocatedCells(Zone* zone, AutoLockForExclusiveAccess } // Sweep everything to fix up weak pointers - WatchpointMap::sweepAll(rt); Debugger::sweepAll(rt->defaultFreeOp()); jit::JitRuntime::SweepJitcodeGlobalTable(rt); rt->gc.sweepZoneAfterCompacting(zone); @@ -3850,10 +3843,6 @@ GCRuntime::markWeakReferences(gcstats::Phase phase) for (ZoneIterT zone(rt); !zone.done(); zone.next()) markedAny |= WeakMapBase::markZoneIteratively(zone, &marker); } - for (CompartmentsIterT<ZoneIterT> c(rt); !c.done(); c.next()) { - if (c->watchpointMap) - markedAny |= c->watchpointMap->markIteratively(&marker); - } markedAny |= Debugger::markAllIteratively(&marker); markedAny |= jit::JitRuntime::MarkJitcodeGlobalTableIteratively(&marker); @@ -4625,9 +4614,6 @@ GCRuntime::beginSweepingZoneGroup(AutoLockForExclusiveAccess& lock) // Bug 1071218: the following two methods have not yet been // refactored to work on a single zone-group at once. - // Collect watch points associated with unreachable objects. - WatchpointMap::sweepAll(rt); - // Detach unreachable debuggers and global objects from each other. Debugger::sweepAll(&fop); diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 749e15d27..c58f32382 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -24,6 +24,7 @@ #include "jstypes.h" #include "jsutil.h" +#include "builtin/SelfHostingDefines.h" #include "ds/Sort.h" #include "gc/Marking.h" #include "js/Proxy.h" @@ -915,28 +916,30 @@ js::GetIteratorObject(JSContext* cx, HandleObject obj, uint32_t flags) return iterator; } +// ES 2017 draft 7.4.7. JSObject* -js::CreateItrResultObject(JSContext* cx, HandleValue value, bool done) +js::CreateIterResultObject(JSContext* cx, HandleValue value, bool done) { - // FIXME: We can cache the iterator result object shape somewhere. - AssertHeapIsIdle(cx); + // Step 1 (implicit). - RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, cx->global())); - if (!proto) + // Step 2. + RootedObject resultObj(cx, NewBuiltinClassInstance<PlainObject>(cx)); + if (!resultObj) return nullptr; - RootedPlainObject obj(cx, NewObjectWithGivenProto<PlainObject>(cx, proto)); - if (!obj) - return nullptr; - - if (!DefineProperty(cx, obj, cx->names().value, value)) + // Step 3. + if (!DefineProperty(cx, resultObj, cx->names().value, value)) return nullptr; - RootedValue doneBool(cx, BooleanValue(done)); - if (!DefineProperty(cx, obj, cx->names().done, doneBool)) + // Step 4. + if (!DefineProperty(cx, resultObj, cx->names().done, + done ? TrueHandleValue : FalseHandleValue)) + { return nullptr; + } - return obj; + // Step 5. + return resultObj; } bool @@ -1135,16 +1138,36 @@ static const JSFunctionSpec string_iterator_methods[] = { JS_FS_END }; +static const Class RegExpStringIteratorPrototypeClass = { + "RegExp String Iterator", + 0 +}; + enum { - ListIteratorSlotIteratedObject, - ListIteratorSlotNextIndex, - ListIteratorSlotNextMethod, - ListIteratorSlotCount + RegExpStringIteratorSlotRegExp, + RegExpStringIteratorSlotString, + RegExpStringIteratorSlotFlags, + RegExpStringIteratorSlotDone, + RegExpStringIteratorSlotCount +}; + +static_assert(RegExpStringIteratorSlotRegExp == REGEXP_STRING_ITERATOR_REGEXP_SLOT, + "RegExpStringIteratorSlotRegExp must match self-hosting define for regexp slot."); +static_assert(RegExpStringIteratorSlotString == REGEXP_STRING_ITERATOR_STRING_SLOT, + "RegExpStringIteratorSlotString must match self-hosting define for string slot."); +static_assert(RegExpStringIteratorSlotFlags == REGEXP_STRING_ITERATOR_FLAGS_SLOT, + "RegExpStringIteratorSlotFlags must match self-hosting define for flags slot."); +static_assert(RegExpStringIteratorSlotDone == REGEXP_STRING_ITERATOR_DONE_SLOT, + "RegExpStringIteratorSlotDone must match self-hosting define for done slot."); + +const Class RegExpStringIteratorObject::class_ = { + "RegExp String Iterator", + JSCLASS_HAS_RESERVED_SLOTS(RegExpStringIteratorSlotCount) }; -const Class ListIteratorObject::class_ = { - "List Iterator", - JSCLASS_HAS_RESERVED_SLOTS(ListIteratorSlotCount) +static const JSFunctionSpec regexp_string_iterator_methods[] = { + JS_SELF_HOSTED_FN("next", "RegExpStringIteratorNext", 0, 0), + JS_FS_END }; JSObject* @@ -1553,6 +1576,30 @@ GlobalObject::initStringIteratorProto(JSContext* cx, Handle<GlobalObject*> globa return true; } +/* static */ bool +GlobalObject::initRegExpStringIteratorProto(JSContext* cx, Handle<GlobalObject*> global) +{ + if (global->getReservedSlot(REGEXP_STRING_ITERATOR_PROTO).isObject()) + return true; + + RootedObject iteratorProto(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global)); + if (!iteratorProto) + return false; + + const Class* cls = &RegExpStringIteratorPrototypeClass; + RootedObject proto(cx, GlobalObject::createBlankPrototypeInheriting(cx, global, cls, + iteratorProto)); + if (!proto || + !DefinePropertiesAndFunctions(cx, proto, nullptr, regexp_string_iterator_methods) || + !DefineToStringTag(cx, proto, cx->names().RegExpStringIterator)) + { + return false; + } + + global->setReservedSlot(REGEXP_STRING_ITERATOR_PROTO, ObjectValue(*proto)); + return true; +} + JSObject* js::InitLegacyIteratorClass(JSContext* cx, HandleObject obj) { diff --git a/js/src/jsiter.h b/js/src/jsiter.h index f11f09b55..0b324a014 100644 --- a/js/src/jsiter.h +++ b/js/src/jsiter.h @@ -151,7 +151,7 @@ class StringIteratorObject : public JSObject static const Class class_; }; -class ListIteratorObject : public JSObject +class RegExpStringIteratorObject : public JSObject { public: static const Class class_; @@ -216,10 +216,10 @@ ThrowStopIteration(JSContext* cx); /* * Create an object of the form { value: VALUE, done: DONE }. - * ES6 draft from 2013-09-05, section 25.4.3.4. + * ES 2017 draft 7.4.7. */ extern JSObject* -CreateItrResultObject(JSContext* cx, HandleValue value, bool done); +CreateIterResultObject(JSContext* cx, HandleValue value, bool done); extern JSObject* InitLegacyIteratorClass(JSContext* cx, HandleObject obj); @@ -227,6 +227,8 @@ InitLegacyIteratorClass(JSContext* cx, HandleObject obj); extern JSObject* InitStopIterationClass(JSContext* cx, HandleObject obj); +enum class IteratorKind { Sync, Async }; + } /* namespace js */ #endif /* jsiter_h */ diff --git a/js/src/jsnativestack.cpp b/js/src/jsnativestack.cpp index 98f8fc741..4e96e01e8 100644 --- a/js/src/jsnativestack.cpp +++ b/js/src/jsnativestack.cpp @@ -26,11 +26,7 @@ # include <sys/syscall.h> # include <sys/types.h> # include <unistd.h> -static pid_t -gettid() -{ - return syscall(__NR_gettid); -} +# define gettid() static_cast<pid_t>(syscall(SYS_gettid)) # endif #else @@ -71,6 +67,20 @@ js::GetNativeStackBaseImpl() # endif } +#elif defined(XP_SOLARIS) + +#include <ucontext.h> + +JS_STATIC_ASSERT(JS_STACK_GROWTH_DIRECTION < 0); + +void* +js::GetNativeStackBaseImpl() +{ + stack_t st; + stack_getbounds(&st); + return static_cast<char*>(st.ss_sp) + st.ss_size; +} + #elif defined(XP_LINUX) && !defined(ANDROID) && defined(__GLIBC__) void* js::GetNativeStackBaseImpl() diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 6f9596924..ef1291079 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -33,7 +33,6 @@ #include "jsstr.h" #include "jstypes.h" #include "jsutil.h" -#include "jswatchpoint.h" #include "jswin.h" #include "jswrapper.h" @@ -1011,13 +1010,7 @@ js::CreateThisForFunction(JSContext* cx, HandleObject callee, HandleObject newTa JSObject::nonNativeSetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, HandleValue receiver, ObjectOpResult& result) { - RootedValue value(cx, v); - if (MOZ_UNLIKELY(obj->watched())) { - WatchpointMap* wpmap = cx->compartment()->watchpointMap; - if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, &value)) - return false; - } - return obj->getOpsSetProperty()(cx, obj, id, value, receiver, result); + return obj->getOpsSetProperty()(cx, obj, id, v, receiver, result); } /* static */ bool @@ -2795,68 +2788,6 @@ js::GetPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, return true; } -bool -js::WatchGuts(JSContext* cx, JS::HandleObject origObj, JS::HandleId id, JS::HandleObject callable) -{ - RootedObject obj(cx, ToWindowIfWindowProxy(origObj)); - if (obj->isNative()) { - // Use sparse indexes for watched objects, as dense elements can be - // written to without checking the watchpoint map. - if (!NativeObject::sparsifyDenseElements(cx, obj.as<NativeObject>())) - return false; - - MarkTypePropertyNonData(cx, obj, id); - } - - WatchpointMap* wpmap = cx->compartment()->watchpointMap; - if (!wpmap) { - wpmap = cx->runtime()->new_<WatchpointMap>(); - if (!wpmap || !wpmap->init()) { - ReportOutOfMemory(cx); - js_delete(wpmap); - return false; - } - cx->compartment()->watchpointMap = wpmap; - } - - return wpmap->watch(cx, obj, id, js::WatchHandler, callable); -} - -bool -js::UnwatchGuts(JSContext* cx, JS::HandleObject origObj, JS::HandleId id) -{ - // Looking in the map for an unsupported object will never hit, so we don't - // need to check for nativeness or watchable-ness here. - RootedObject obj(cx, ToWindowIfWindowProxy(origObj)); - if (WatchpointMap* wpmap = cx->compartment()->watchpointMap) - wpmap->unwatch(obj, id, nullptr, nullptr); - return true; -} - -bool -js::WatchProperty(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable) -{ - if (WatchOp op = obj->getOpsWatch()) - return op(cx, obj, id, callable); - - if (!obj->isNative() || obj->is<TypedArrayObject>()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_WATCH, - obj->getClass()->name); - return false; - } - - return WatchGuts(cx, obj, id, callable); -} - -bool -js::UnwatchProperty(JSContext* cx, HandleObject obj, HandleId id) -{ - if (UnwatchOp op = obj->getOpsUnwatch()) - return op(cx, obj, id); - - return UnwatchGuts(cx, obj, id); -} - const char* js::GetObjectClassName(JSContext* cx, HandleObject obj) { @@ -3416,7 +3347,6 @@ JSObject::dump(FILE* fp) const if (obj->isBoundFunction()) fprintf(fp, " bound_function"); if (obj->isQualifiedVarObj()) fprintf(fp, " varobj"); if (obj->isUnqualifiedVarObj()) fprintf(fp, " unqualified_varobj"); - if (obj->watched()) fprintf(fp, " watched"); if (obj->isIteratedSingleton()) fprintf(fp, " iterated_singleton"); if (obj->isNewGroupUnknown()) fprintf(fp, " new_type_unknown"); if (obj->hasUncacheableProto()) fprintf(fp, " has_uncacheable_proto"); diff --git a/js/src/jsobj.h b/js/src/jsobj.h index db2c22b76..01845d7e6 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -141,8 +141,6 @@ class JSObject : public js::gc::Cell js::GetOwnPropertyOp getOpsGetOwnPropertyDescriptor() const { return getClass()->getOpsGetOwnPropertyDescriptor(); } js::DeletePropertyOp getOpsDeleteProperty() const { return getClass()->getOpsDeleteProperty(); } - js::WatchOp getOpsWatch() const { return getClass()->getOpsWatch(); } - js::UnwatchOp getOpsUnwatch() const { return getClass()->getOpsUnwatch(); } js::GetElementsOp getOpsGetElements() const { return getClass()->getOpsGetElements(); } JSNewEnumerateOp getOpsEnumerate() const { return getClass()->getOpsEnumerate(); } JSFunToStringOp getOpsFunToString() const { return getClass()->getOpsFunToString(); } @@ -221,11 +219,6 @@ class JSObject : public js::gc::Cell inline bool isBoundFunction() const; inline bool hasSpecialEquality() const; - inline bool watched() const; - static bool setWatched(js::ExclusiveContext* cx, JS::HandleObject obj) { - return setFlags(cx, obj, js::BaseShape::WATCHED, GENERATE_SHAPE); - } - // A "qualified" varobj is the object on which "qualified" variable // declarations (i.e., those defined with "var") are kept. // @@ -1032,21 +1025,6 @@ extern bool DefineFunctions(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs, DefineAsIntrinsic intrinsic); -/* - * Set a watchpoint: a synchronous callback when the given property of the - * given object is set. - * - * Watchpoints are nonstandard and do not fit in well with the way ES6 - * specifies [[Set]]. They are also insufficient for implementing - * Object.observe. - */ -extern bool -WatchProperty(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable); - -/* Clear a watchpoint. */ -extern bool -UnwatchProperty(JSContext* cx, HandleObject obj, HandleId id); - /* ES6 draft rev 36 (2015 March 17) 7.1.1 ToPrimitive(vp[, preferredType]) */ extern bool ToPrimitiveSlow(JSContext* cx, JSType hint, MutableHandleValue vp); diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index c132ee6b2..98e740142 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -464,12 +464,6 @@ JSObject::isBoundFunction() const } inline bool -JSObject::watched() const -{ - return hasAllFlags(js::BaseShape::WATCHED); -} - -inline bool JSObject::isDelegate() const { return hasAllFlags(js::BaseShape::DELEGATE); diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index e56eebb2d..e417e3a64 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -655,7 +655,8 @@ IsValidBytecodeOffset(JSContext* cx, JSScript* script, size_t offset); inline bool FlowsIntoNext(JSOp op) { - /* JSOP_YIELD is considered to flow into the next instruction, like JSOP_CALL. */ + // JSOP_YIELD/JSOP_AWAIT is considered to flow into the next instruction, + // like JSOP_CALL. switch (op) { case JSOP_RETRVAL: case JSOP_RETURN: diff --git a/js/src/jsprototypes.h b/js/src/jsprototypes.h index dc7cdb85a..3cf8621cc 100644 --- a/js/src/jsprototypes.h +++ b/js/src/jsprototypes.h @@ -57,12 +57,6 @@ #define IF_SAB(real,imaginary) imaginary #endif -#ifdef SPIDERMONKEY_PROMISE -#define IF_PROMISE(real,imaginary) real -#else -#define IF_PROMISE(real,imaginary) imaginary -#endif - #define JS_FOR_PROTOTYPES(real,imaginary) \ imaginary(Null, 0, InitNullClass, dummy) \ real(Object, 1, InitViaClassSpec, OCLASP(Plain)) \ @@ -118,7 +112,7 @@ IF_SAB(real,imaginary)(Atomics, 47, InitAtomicsClass, OCLASP(Atomics)) \ imaginary(WasmInstance, 51, dummy, dummy) \ imaginary(WasmMemory, 52, dummy, dummy) \ imaginary(WasmTable, 53, dummy, dummy) \ -IF_PROMISE(real,imaginary)(Promise, 54, InitViaClassSpec, OCLASP(Promise)) \ + real(Promise, 54, InitViaClassSpec, OCLASP(Promise)) \ #define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro) diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index fc7438e3b..bdd411d04 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -402,8 +402,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip ntrynotes = script->trynotes()->length; if (script->hasScopeNotes()) nscopenotes = script->scopeNotes()->length; - if (script->hasYieldOffsets()) - nyieldoffsets = script->yieldOffsets().length(); + if (script->hasYieldAndAwaitOffsets()) + nyieldoffsets = script->yieldAndAwaitOffsets().length(); nTypeSets = script->nTypeSets(); funLength = script->funLength(); @@ -902,7 +902,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip } for (i = 0; i < nyieldoffsets; ++i) { - uint32_t* offset = &script->yieldOffsets()[i]; + uint32_t* offset = &script->yieldAndAwaitOffsets()[i]; if (!xdr->codeUint32(offset)) return false; } @@ -2456,7 +2456,7 @@ ScriptDataSize(uint32_t nscopes, uint32_t nconsts, uint32_t nobjects, if (nscopenotes != 0) size += sizeof(ScopeNoteArray) + nscopenotes * sizeof(ScopeNote); if (nyieldoffsets != 0) - size += sizeof(YieldOffsetArray) + nyieldoffsets * sizeof(uint32_t); + size += sizeof(YieldAndAwaitOffsetArray) + nyieldoffsets * sizeof(uint32_t); return size; } @@ -2558,10 +2558,10 @@ JSScript::partiallyInit(ExclusiveContext* cx, HandleScript script, uint32_t nsco cursor += sizeof(ScopeNoteArray); } - YieldOffsetArray* yieldOffsets = nullptr; + YieldAndAwaitOffsetArray* yieldAndAwaitOffsets = nullptr; if (nyieldoffsets != 0) { - yieldOffsets = reinterpret_cast<YieldOffsetArray*>(cursor); - cursor += sizeof(YieldOffsetArray); + yieldAndAwaitOffsets = reinterpret_cast<YieldAndAwaitOffsetArray*>(cursor); + cursor += sizeof(YieldAndAwaitOffsetArray); } if (nconsts != 0) { @@ -2602,8 +2602,8 @@ JSScript::partiallyInit(ExclusiveContext* cx, HandleScript script, uint32_t nsco } if (nyieldoffsets != 0) { - yieldOffsets->init(reinterpret_cast<uint32_t*>(cursor), nyieldoffsets); - size_t vectorSize = nyieldoffsets * sizeof(script->yieldOffsets()[0]); + yieldAndAwaitOffsets->init(reinterpret_cast<uint32_t*>(cursor), nyieldoffsets); + size_t vectorSize = nyieldoffsets * sizeof(script->yieldAndAwaitOffsets()[0]); #ifdef DEBUG memset(cursor, 0, vectorSize); #endif @@ -2623,10 +2623,10 @@ JSScript::initFunctionPrototype(ExclusiveContext* cx, Handle<JSScript*> script, uint32_t numObjects = 0; uint32_t numTryNotes = 0; uint32_t numScopeNotes = 0; - uint32_t numYieldOffsets = 0; + uint32_t numYieldAndAwaitOffsets = 0; uint32_t numTypeSets = 0; if (!partiallyInit(cx, script, numScopes, numConsts, numObjects, numTryNotes, - numScopeNotes, numYieldOffsets, numTypeSets)) + numScopeNotes, numYieldAndAwaitOffsets, numTypeSets)) { return false; } @@ -2739,7 +2739,7 @@ JSScript::fullyInitFromEmitter(ExclusiveContext* cx, HandleScript script, Byteco if (!partiallyInit(cx, script, bce->scopeList.length(), bce->constList.length(), bce->objectList.length, bce->tryNoteList.length(), bce->scopeNoteList.length(), - bce->yieldOffsetList.length(), bce->typesetCount)) + bce->yieldAndAwaitOffsetList.length(), bce->typesetCount)) { return false; } @@ -2794,8 +2794,8 @@ JSScript::fullyInitFromEmitter(ExclusiveContext* cx, HandleScript script, Byteco // Copy yield offsets last, as the generator kind is set in // initFromFunctionBox. - if (bce->yieldOffsetList.length() != 0) - bce->yieldOffsetList.finish(script->yieldOffsets(), prologueLength); + if (bce->yieldAndAwaitOffsetList.length() != 0) + bce->yieldAndAwaitOffsetList.finish(script->yieldAndAwaitOffsets(), prologueLength); #ifdef DEBUG script->assertValidJumpTargets(); @@ -3188,7 +3188,7 @@ CloneInnerInterpretedFunction(JSContext* cx, HandleScope enclosingScope, HandleF { /* NB: Keep this in sync with XDRInterpretedFunction. */ RootedObject cloneProto(cx); - if (srcFun->isStarGenerator()) { + if (srcFun->isStarGenerator() || srcFun->isAsync()) { cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global()); if (!cloneProto) return nullptr; @@ -3242,7 +3242,7 @@ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst, uint32_t nscopes = src->scopes()->length; uint32_t ntrynotes = src->hasTrynotes() ? src->trynotes()->length : 0; uint32_t nscopenotes = src->hasScopeNotes() ? src->scopeNotes()->length : 0; - uint32_t nyieldoffsets = src->hasYieldOffsets() ? src->yieldOffsets().length() : 0; + uint32_t nyieldoffsets = src->hasYieldAndAwaitOffsets() ? src->yieldAndAwaitOffsets().length() : 0; /* Script data */ @@ -3380,8 +3380,10 @@ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst, dst->trynotes()->vector = Rebase<JSTryNote>(dst, src, src->trynotes()->vector); if (nscopenotes != 0) dst->scopeNotes()->vector = Rebase<ScopeNote>(dst, src, src->scopeNotes()->vector); - if (nyieldoffsets != 0) - dst->yieldOffsets().vector_ = Rebase<uint32_t>(dst, src, src->yieldOffsets().vector_); + if (nyieldoffsets != 0) { + dst->yieldAndAwaitOffsets().vector_ = + Rebase<uint32_t>(dst, src, src->yieldAndAwaitOffsets().vector_); + } /* * Function delazification assumes that their script does not have a @@ -3917,7 +3919,9 @@ JSScript::argumentsOptimizationFailed(JSContext* cx, HandleScript script) if (script->needsArgsObj()) return true; - MOZ_ASSERT(!script->isGenerator()); + MOZ_ASSERT(!script->isStarGenerator()); + MOZ_ASSERT(!script->isLegacyGenerator()); + MOZ_ASSERT(!script->isAsync()); script->needsArgsObj_ = true; diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 85eb2938d..c19fbfc71 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -156,7 +156,7 @@ struct ScopeNoteArray { uint32_t length; // Count of indexed try notes. }; -class YieldOffsetArray { +class YieldAndAwaitOffsetArray { friend bool detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst, MutableHandle<GCVector<Scope*>> scopes); @@ -1327,13 +1327,12 @@ class JSScript : public js::gc::TenuredCell js::GeneratorKind generatorKind() const { return js::GeneratorKindFromBits(generatorKindBits_); } - bool isGenerator() const { return generatorKind() != js::NotGenerator; } bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; } bool isStarGenerator() const { return generatorKind() == js::StarGenerator; } void setGeneratorKind(js::GeneratorKind kind) { // A script only gets its generator kind set as part of initialization, // so it can only transition from not being a generator. - MOZ_ASSERT(!isGenerator()); + MOZ_ASSERT(!isStarGenerator() && !isLegacyGenerator()); generatorKindBits_ = GeneratorKindAsBits(kind); } @@ -1341,6 +1340,10 @@ class JSScript : public js::gc::TenuredCell return isAsync_ ? js::AsyncFunction : js::SyncFunction; } + bool isAsync() const { + return isAsync_; + } + void setAsyncKind(js::FunctionAsyncKind kind) { isAsync_ = kind == js::AsyncFunction; } @@ -1493,7 +1496,8 @@ class JSScript : public js::gc::TenuredCell bool isRelazifiable() const { return (selfHosted() || lazyScript) && !hasInnerFunctions_ && !types_ && - !isGenerator() && !hasBaselineScript() && !hasAnyIonScript() && + !isStarGenerator() && !isLegacyGenerator() && !isAsync() && + !hasBaselineScript() && !hasAnyIonScript() && !isDefaultClassConstructor() && !doNotRelazify_; } @@ -1695,7 +1699,9 @@ class JSScript : public js::gc::TenuredCell bool hasObjects() const { return hasArray(OBJECTS); } bool hasTrynotes() const { return hasArray(TRYNOTES); } bool hasScopeNotes() const { return hasArray(SCOPENOTES); } - bool hasYieldOffsets() const { return isGenerator(); } + bool hasYieldAndAwaitOffsets() const { + return isStarGenerator() || isLegacyGenerator() || isAsync(); + } #define OFF(fooOff, hasFoo, t) (fooOff() + (hasFoo() ? sizeof(t) : 0)) @@ -1704,7 +1710,9 @@ class JSScript : public js::gc::TenuredCell size_t objectsOffset() const { return OFF(constsOffset, hasConsts, js::ConstArray); } size_t trynotesOffset() const { return OFF(objectsOffset, hasObjects, js::ObjectArray); } size_t scopeNotesOffset() const { return OFF(trynotesOffset, hasTrynotes, js::TryNoteArray); } - size_t yieldOffsetsOffset() const { return OFF(scopeNotesOffset, hasScopeNotes, js::ScopeNoteArray); } + size_t yieldAndAwaitOffsetsOffset() const { + return OFF(scopeNotesOffset, hasScopeNotes, js::ScopeNoteArray); + } #undef OFF @@ -1734,9 +1742,10 @@ class JSScript : public js::gc::TenuredCell return reinterpret_cast<js::ScopeNoteArray*>(data + scopeNotesOffset()); } - js::YieldOffsetArray& yieldOffsets() { - MOZ_ASSERT(hasYieldOffsets()); - return *reinterpret_cast<js::YieldOffsetArray*>(data + yieldOffsetsOffset()); + js::YieldAndAwaitOffsetArray& yieldAndAwaitOffsets() { + MOZ_ASSERT(hasYieldAndAwaitOffsets()); + return *reinterpret_cast<js::YieldAndAwaitOffsetArray*>(data + + yieldAndAwaitOffsetsOffset()); } bool hasLoops(); @@ -2112,8 +2121,6 @@ class LazyScript : public gc::TenuredCell GeneratorKind generatorKind() const { return GeneratorKindFromBits(p_.generatorKindBits); } - bool isGenerator() const { return generatorKind() != NotGenerator; } - bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; } bool isStarGenerator() const { return generatorKind() == StarGenerator; } @@ -2121,7 +2128,7 @@ class LazyScript : public gc::TenuredCell void setGeneratorKind(GeneratorKind kind) { // A script only gets its generator kind set as part of initialization, // so it can only transition from NotGenerator. - MOZ_ASSERT(!isGenerator()); + MOZ_ASSERT(!isStarGenerator() && !isLegacyGenerator()); // Legacy generators cannot currently be lazy. MOZ_ASSERT(kind != LegacyGenerator); p_.generatorKindBits = GeneratorKindAsBits(kind); @@ -2131,6 +2138,10 @@ class LazyScript : public gc::TenuredCell return p_.isAsync ? AsyncFunction : SyncFunction; } + bool isAsync() const { + return p_.isAsync; + } + void setAsyncKind(FunctionAsyncKind kind) { p_.isAsync = kind == AsyncFunction; } diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 74f61b87d..3964ab84e 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -2584,6 +2584,7 @@ static const JSFunctionSpec string_methods[] = { /* Perl-ish methods (search is actually Python-esque). */ JS_SELF_HOSTED_FN("match", "String_match", 1,0), + JS_SELF_HOSTED_FN("matchAll", "String_matchAll", 1,0), JS_SELF_HOSTED_FN("search", "String_search", 1,0), JS_SELF_HOSTED_FN("replace", "String_replace", 2,0), JS_SELF_HOSTED_FN("split", "String_split", 2,0), diff --git a/js/src/jsversion.h b/js/src/jsversion.h index 8bdfe47b6..cf4c6e73a 100644 --- a/js/src/jsversion.h +++ b/js/src/jsversion.h @@ -12,7 +12,6 @@ */ #define JS_HAS_STR_HTML_HELPERS 1 /* (no longer used) */ #define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ -#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ #define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ #define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ diff --git a/js/src/jswatchpoint.cpp b/js/src/jswatchpoint.cpp deleted file mode 100644 index 34479a990..000000000 --- a/js/src/jswatchpoint.cpp +++ /dev/null @@ -1,246 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "jswatchpoint.h" - -#include "jsatom.h" -#include "jscompartment.h" -#include "jsfriendapi.h" - -#include "gc/Marking.h" -#include "vm/Shape.h" - -#include "jsgcinlines.h" - -using namespace js; -using namespace js::gc; - -inline HashNumber -WatchKeyHasher::hash(const Lookup& key) -{ - return MovableCellHasher<PreBarrieredObject>::hash(key.object) ^ HashId(key.id); -} - -namespace { - -class AutoEntryHolder { - typedef WatchpointMap::Map Map; - Generation gen; - Map& map; - Map::Ptr p; - RootedObject obj; - RootedId id; - - public: - AutoEntryHolder(JSContext* cx, Map& map, Map::Ptr p) - : gen(map.generation()), map(map), p(p), obj(cx, p->key().object), id(cx, p->key().id) - { - MOZ_ASSERT(!p->value().held); - p->value().held = true; - } - - ~AutoEntryHolder() { - if (gen != map.generation()) - p = map.lookup(WatchKey(obj, id)); - if (p) - p->value().held = false; - } -}; - -} /* anonymous namespace */ - -bool -WatchpointMap::init() -{ - return map.init(); -} - -bool -WatchpointMap::watch(JSContext* cx, HandleObject obj, HandleId id, - JSWatchPointHandler handler, HandleObject closure) -{ - MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id) || JSID_IS_SYMBOL(id)); - - if (!JSObject::setWatched(cx, obj)) - return false; - - Watchpoint w(handler, closure, false); - if (!map.put(WatchKey(obj, id), w)) { - ReportOutOfMemory(cx); - return false; - } - /* - * For generational GC, we don't need to post-barrier writes to the - * hashtable here because we mark all watchpoints as part of root marking in - * markAll(). - */ - return true; -} - -void -WatchpointMap::unwatch(JSObject* obj, jsid id, - JSWatchPointHandler* handlerp, JSObject** closurep) -{ - if (Map::Ptr p = map.lookup(WatchKey(obj, id))) { - if (handlerp) - *handlerp = p->value().handler; - if (closurep) { - // Read barrier to prevent an incorrectly gray closure from escaping the - // watchpoint. See the comment before UnmarkGrayChildren in gc/Marking.cpp - JS::ExposeObjectToActiveJS(p->value().closure); - *closurep = p->value().closure; - } - map.remove(p); - } -} - -void -WatchpointMap::unwatchObject(JSObject* obj) -{ - for (Map::Enum e(map); !e.empty(); e.popFront()) { - Map::Entry& entry = e.front(); - if (entry.key().object == obj) - e.removeFront(); - } -} - -void -WatchpointMap::clear() -{ - map.clear(); -} - -bool -WatchpointMap::triggerWatchpoint(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp) -{ - Map::Ptr p = map.lookup(WatchKey(obj, id)); - if (!p || p->value().held) - return true; - - AutoEntryHolder holder(cx, map, p); - - /* Copy the entry, since GC would invalidate p. */ - JSWatchPointHandler handler = p->value().handler; - RootedObject closure(cx, p->value().closure); - - /* Determine the property's old value. */ - Value old; - old.setUndefined(); - if (obj->isNative()) { - NativeObject* nobj = &obj->as<NativeObject>(); - if (Shape* shape = nobj->lookup(cx, id)) { - if (shape->hasSlot()) - old = nobj->getSlot(shape->slot()); - } - } - - // Read barrier to prevent an incorrectly gray closure from escaping the - // watchpoint. See the comment before UnmarkGrayChildren in gc/Marking.cpp - JS::ExposeObjectToActiveJS(closure); - - /* Call the handler. */ - return handler(cx, obj, id, old, vp.address(), closure); -} - -bool -WatchpointMap::markIteratively(JSTracer* trc) -{ - bool marked = false; - for (Map::Enum e(map); !e.empty(); e.popFront()) { - Map::Entry& entry = e.front(); - JSObject* priorKeyObj = entry.key().object; - jsid priorKeyId(entry.key().id.get()); - bool objectIsLive = - IsMarked(trc->runtime(), const_cast<PreBarrieredObject*>(&entry.key().object)); - if (objectIsLive || entry.value().held) { - if (!objectIsLive) { - TraceEdge(trc, const_cast<PreBarrieredObject*>(&entry.key().object), - "held Watchpoint object"); - marked = true; - } - - MOZ_ASSERT(JSID_IS_STRING(priorKeyId) || - JSID_IS_INT(priorKeyId) || - JSID_IS_SYMBOL(priorKeyId)); - TraceEdge(trc, const_cast<PreBarrieredId*>(&entry.key().id), "WatchKey::id"); - - if (entry.value().closure && !IsMarked(trc->runtime(), &entry.value().closure)) { - TraceEdge(trc, &entry.value().closure, "Watchpoint::closure"); - marked = true; - } - - /* We will sweep this entry in sweepAll if !objectIsLive. */ - if (priorKeyObj != entry.key().object || priorKeyId != entry.key().id) - e.rekeyFront(WatchKey(entry.key().object, entry.key().id)); - } - } - return marked; -} - -void -WatchpointMap::markAll(JSTracer* trc) -{ - for (Map::Enum e(map); !e.empty(); e.popFront()) { - Map::Entry& entry = e.front(); - JSObject* object = entry.key().object; - jsid id = entry.key().id; - JSObject* priorObject = object; - jsid priorId = id; - MOZ_ASSERT(JSID_IS_STRING(priorId) || JSID_IS_INT(priorId) || JSID_IS_SYMBOL(priorId)); - - TraceManuallyBarrieredEdge(trc, &object, "held Watchpoint object"); - TraceManuallyBarrieredEdge(trc, &id, "WatchKey::id"); - TraceEdge(trc, &entry.value().closure, "Watchpoint::closure"); - - if (priorObject != object || priorId != id) - e.rekeyFront(WatchKey(object, id)); - } -} - -void -WatchpointMap::sweepAll(JSRuntime* rt) -{ - for (GCCompartmentsIter c(rt); !c.done(); c.next()) { - if (WatchpointMap* wpmap = c->watchpointMap) - wpmap->sweep(); - } -} - -void -WatchpointMap::sweep() -{ - for (Map::Enum e(map); !e.empty(); e.popFront()) { - Map::Entry& entry = e.front(); - JSObject* obj(entry.key().object); - if (IsAboutToBeFinalizedUnbarriered(&obj)) { - MOZ_ASSERT(!entry.value().held); - e.removeFront(); - } else if (obj != entry.key().object) { - e.rekeyFront(WatchKey(obj, entry.key().id)); - } - } -} - -void -WatchpointMap::traceAll(WeakMapTracer* trc) -{ - JSRuntime* rt = trc->context; - for (CompartmentsIter comp(rt, SkipAtoms); !comp.done(); comp.next()) { - if (WatchpointMap* wpmap = comp->watchpointMap) - wpmap->trace(trc); - } -} - -void -WatchpointMap::trace(WeakMapTracer* trc) -{ - for (Map::Range r = map.all(); !r.empty(); r.popFront()) { - Map::Entry& entry = r.front(); - trc->trace(nullptr, - JS::GCCellPtr(entry.key().object.get()), - JS::GCCellPtr(entry.value().closure.get())); - } -} diff --git a/js/src/jswatchpoint.h b/js/src/jswatchpoint.h deleted file mode 100644 index bba6c38ce..000000000 --- a/js/src/jswatchpoint.h +++ /dev/null @@ -1,90 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef jswatchpoint_h -#define jswatchpoint_h - -#include "jsalloc.h" - -#include "gc/Barrier.h" -#include "js/HashTable.h" - -namespace js { - -struct WeakMapTracer; - -struct WatchKey { - WatchKey() {} - WatchKey(JSObject* obj, jsid id) : object(obj), id(id) {} - WatchKey(const WatchKey& key) : object(key.object.get()), id(key.id.get()) {} - - // These are traced unconditionally during minor GC, so do not require - // post-barriers. - PreBarrieredObject object; - PreBarrieredId id; - - bool operator!=(const WatchKey& other) const { - return object != other.object || id != other.id; - } -}; - -typedef bool -(* JSWatchPointHandler)(JSContext* cx, JSObject* obj, jsid id, const JS::Value& old, - JS::Value* newp, void* closure); - -struct Watchpoint { - JSWatchPointHandler handler; - PreBarrieredObject closure; /* This is always marked in minor GCs and so doesn't require a postbarrier. */ - bool held; /* true if currently running handler */ - Watchpoint(JSWatchPointHandler handler, JSObject* closure, bool held) - : handler(handler), closure(closure), held(held) {} -}; - -struct WatchKeyHasher -{ - typedef WatchKey Lookup; - static inline js::HashNumber hash(const Lookup& key); - - static bool match(const WatchKey& k, const Lookup& l) { - return MovableCellHasher<PreBarrieredObject>::match(k.object, l.object) && - DefaultHasher<PreBarrieredId>::match(k.id, l.id); - } - - static void rekey(WatchKey& k, const WatchKey& newKey) { - k.object.unsafeSet(newKey.object); - k.id.unsafeSet(newKey.id); - } -}; - -class WatchpointMap { - public: - typedef HashMap<WatchKey, Watchpoint, WatchKeyHasher, SystemAllocPolicy> Map; - - bool init(); - bool watch(JSContext* cx, HandleObject obj, HandleId id, - JSWatchPointHandler handler, HandleObject closure); - void unwatch(JSObject* obj, jsid id, - JSWatchPointHandler* handlerp, JSObject** closurep); - void unwatchObject(JSObject* obj); - void clear(); - - bool triggerWatchpoint(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp); - - bool markIteratively(JSTracer* trc); - void markAll(JSTracer* trc); - static void sweepAll(JSRuntime* rt); - void sweep(); - - static void traceAll(WeakMapTracer* trc); - void trace(WeakMapTracer* trc); - - private: - Map map; -}; - -} // namespace js - -#endif /* jswatchpoint_h */ diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index 5f3704e32..32336c68b 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -315,13 +315,6 @@ class JS_FRIEND_API(SecurityWrapper) : public Base virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override; virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override; - // Allow isCallable and isConstructor. They used to be class-level, and so could not be guarded - // against. - - virtual bool watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, - JS::HandleObject callable) const override; - virtual bool unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id) const override; - /* * Allow our subclasses to select the superclass behavior they want without * needing to specify an exact superclass. diff --git a/js/src/moz.build b/js/src/moz.build index a0f074d1c..5287aef00 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -291,7 +291,6 @@ UNIFIED_SOURCES += [ 'jspropertytree.cpp', 'jsscript.cpp', 'jsstr.cpp', - 'jswatchpoint.cpp', 'jsweakmap.cpp', 'perf/jsperf.cpp', 'proxy/BaseProxyHandler.cpp', @@ -306,6 +305,7 @@ UNIFIED_SOURCES += [ 'vm/ArgumentsObject.cpp', 'vm/ArrayBufferObject.cpp', 'vm/AsyncFunction.cpp', + 'vm/AsyncIteration.cpp', 'vm/Caches.cpp', 'vm/CallNonGenericMethod.cpp', 'vm/CharacterEncoding.cpp', @@ -721,6 +721,14 @@ if CONFIG['OS_ARCH'] == 'Linux': 'dl', ] +if CONFIG['OS_ARCH'] == 'SunOS': + OS_LIBS += [ + 'posix4', + 'dl', + 'nsl', + 'socket', + ] + OS_LIBS += CONFIG['REALTIME_LIBS'] CFLAGS += CONFIG['MOZ_ICU_CFLAGS'] @@ -740,6 +748,7 @@ selfhosted.inputs = [ 'builtin/SelfHostingDefines.h', 'builtin/Utilities.js', 'builtin/Array.js', + 'builtin/AsyncIteration.js', 'builtin/Classes.js', 'builtin/Date.js', 'builtin/Error.js', @@ -752,6 +761,7 @@ selfhosted.inputs = [ 'builtin/Module.js', 'builtin/Number.js', 'builtin/Object.js', + 'builtin/Promise.js', 'builtin/Reflect.js', 'builtin/RegExp.js', 'builtin/RegExpGlobalReplaceOpt.h.js', @@ -765,9 +775,6 @@ selfhosted.inputs = [ 'builtin/WeakSet.js' ] -if CONFIG['SPIDERMONKEY_PROMISE']: - selfhosted.inputs += ['builtin/Promise.js'] - if CONFIG['JS_HAS_CTYPES']: if CONFIG['MOZ_SYSTEM_FFI']: CXXFLAGS += CONFIG['MOZ_FFI_CFLAGS'] @@ -783,3 +790,6 @@ if CONFIG['GNU_CXX']: # Suppress warnings in third-party code. if CONFIG['CLANG_CXX'] or CONFIG['GNU_CXX']: SOURCES['jsdtoa.cpp'].flags += ['-Wno-implicit-fallthrough'] + +if CONFIG['OS_ARCH'] == 'WINNT': + DEFINES['NOMINMAX'] = True diff --git a/js/src/old-configure.in b/js/src/old-configure.in index 45108ee59..6566ce05e 100644 --- a/js/src/old-configure.in +++ b/js/src/old-configure.in @@ -923,6 +923,14 @@ case "$target" in fi ;; +i*86-*-solaris*) + MOZ_FIX_LINK_PATHS="-L${DIST}/bin -R'\$\$ORIGIN':/usr/gcc/7/lib" + ;; + +x86_64-*-solaris*) + MOZ_FIX_LINK_PATHS="-L${DIST}/bin -R'\$\$ORIGIN':/usr/gcc/7/lib/amd64" + ;; + esac dnl Only one oddball right now (QNX), but this gives us flexibility diff --git a/js/src/proxy/BaseProxyHandler.cpp b/js/src/proxy/BaseProxyHandler.cpp index 8d5f30a19..423aa8671 100644 --- a/js/src/proxy/BaseProxyHandler.cpp +++ b/js/src/proxy/BaseProxyHandler.cpp @@ -419,20 +419,6 @@ BaseProxyHandler::setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* } bool -BaseProxyHandler::watch(JSContext* cx, HandleObject proxy, HandleId id, HandleObject callable) const -{ - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_WATCH, - proxy->getClass()->name); - return false; -} - -bool -BaseProxyHandler::unwatch(JSContext* cx, HandleObject proxy, HandleId id) const -{ - return true; -} - -bool BaseProxyHandler::getElements(JSContext* cx, HandleObject proxy, uint32_t begin, uint32_t end, ElementAdder* adder) const { diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp index 2c1cffb77..6b3a3d1e9 100644 --- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -505,20 +505,6 @@ Proxy::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp JSObject * const TaggedProto::LazyProto = reinterpret_cast<JSObject*>(0x1); /* static */ bool -Proxy::watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleObject callable) -{ - JS_CHECK_RECURSION(cx, return false); - return proxy->as<ProxyObject>().handler()->watch(cx, proxy, id, callable); -} - -/* static */ bool -Proxy::unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id) -{ - JS_CHECK_RECURSION(cx, return false); - return proxy->as<ProxyObject>().handler()->unwatch(cx, proxy, id); -} - -/* static */ bool Proxy::getElements(JSContext* cx, HandleObject proxy, uint32_t begin, uint32_t end, ElementAdder* adder) { @@ -699,18 +685,6 @@ js::proxy_Construct(JSContext* cx, unsigned argc, Value* vp) } bool -js::proxy_Watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable) -{ - return Proxy::watch(cx, obj, id, callable); -} - -bool -js::proxy_Unwatch(JSContext* cx, HandleObject obj, HandleId id) -{ - return Proxy::unwatch(cx, obj, id); -} - -bool js::proxy_GetElements(JSContext* cx, HandleObject proxy, uint32_t begin, uint32_t end, ElementAdder* adder) { @@ -750,7 +724,6 @@ const ObjectOps js::ProxyObjectOps = { js::proxy_SetProperty, js::proxy_GetOwnPropertyDescriptor, js::proxy_DeleteProperty, - js::proxy_Watch, js::proxy_Unwatch, js::proxy_GetElements, nullptr, /* enumerate */ js::proxy_FunToString diff --git a/js/src/proxy/Proxy.h b/js/src/proxy/Proxy.h index 89909a085..4a8ecf8ab 100644 --- a/js/src/proxy/Proxy.h +++ b/js/src/proxy/Proxy.h @@ -65,9 +65,6 @@ class Proxy static bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g); static bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp); - static bool watch(JSContext* cx, HandleObject proxy, HandleId id, HandleObject callable); - static bool unwatch(JSContext* cx, HandleObject proxy, HandleId id); - static bool getElements(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end, ElementAdder* adder); diff --git a/js/src/proxy/SecurityWrapper.cpp b/js/src/proxy/SecurityWrapper.cpp index 710faf9b0..71d22fca6 100644 --- a/js/src/proxy/SecurityWrapper.cpp +++ b/js/src/proxy/SecurityWrapper.cpp @@ -130,24 +130,5 @@ SecurityWrapper<Base>::defineProperty(JSContext* cx, HandleObject wrapper, Handl return Base::defineProperty(cx, wrapper, id, desc, result); } -template <class Base> -bool -SecurityWrapper<Base>::watch(JSContext* cx, HandleObject proxy, - HandleId id, HandleObject callable) const -{ - ReportUnwrapDenied(cx); - return false; -} - -template <class Base> -bool -SecurityWrapper<Base>::unwatch(JSContext* cx, HandleObject proxy, - HandleId id) const -{ - ReportUnwrapDenied(cx); - return false; -} - - template class js::SecurityWrapper<Wrapper>; template class js::SecurityWrapper<CrossCompartmentWrapper>; diff --git a/js/src/shell/OSObject.cpp b/js/src/shell/OSObject.cpp index 846ec7b15..4fb3d4e77 100644 --- a/js/src/shell/OSObject.cpp +++ b/js/src/shell/OSObject.cpp @@ -184,6 +184,11 @@ FileAsTypedArray(JSContext* cx, JS::HandleString pathnameStr) return nullptr; JS_ReportErrorUTF8(cx, "can't seek start of %s", pathname.ptr()); } else { + if (len > INT32_MAX) { + JS_ReportErrorUTF8(cx, "file %s is too large for a Uint8Array", + pathname.ptr()); + return nullptr; + } obj = JS_NewUint8Array(cx, len); if (!obj) return nullptr; diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 51cd11fe8..36558a694 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -85,6 +85,7 @@ #include "threading/Thread.h" #include "vm/ArgumentsObject.h" #include "vm/AsyncFunction.h" +#include "vm/AsyncIteration.h" #include "vm/Compression.h" #include "vm/Debugger.h" #include "vm/HelperThreads.h" @@ -153,7 +154,6 @@ static const double MAX_TIMEOUT_SECONDS = 1800.0; # define SHARED_MEMORY_DEFAULT 0 #endif -#ifdef SPIDERMONKEY_PROMISE using JobQueue = GCVector<JSObject*, 0, SystemAllocPolicy>; struct ShellAsyncTasks @@ -166,7 +166,6 @@ struct ShellAsyncTasks size_t outstanding; Vector<JS::AsyncTask*> finished; }; -#endif // SPIDERMONKEY_PROMISE enum class ScriptKind { @@ -271,12 +270,10 @@ struct ShellContext JS::PersistentRootedValue interruptFunc; bool lastWarningEnabled; JS::PersistentRootedValue lastWarning; -#ifdef SPIDERMONKEY_PROMISE JS::PersistentRootedValue promiseRejectionTrackerCallback; JS::PersistentRooted<JobQueue> jobQueue; ExclusiveData<ShellAsyncTasks> asyncTasks; bool drainingJobQueue; -#endif // SPIDERMONKEY_PROMISE /* * Watchdog thread state. @@ -434,11 +431,9 @@ ShellContext::ShellContext(JSContext* cx) interruptFunc(cx, NullValue()), lastWarningEnabled(false), lastWarning(cx, NullValue()), -#ifdef SPIDERMONKEY_PROMISE promiseRejectionTrackerCallback(cx, NullValue()), asyncTasks(mutexid::ShellAsyncTasks, cx), drainingJobQueue(false), -#endif // SPIDERMONKEY_PROMISE watchdogLock(mutexid::ShellContextWatchdog), exitCode(0), quitting(false), @@ -738,7 +733,6 @@ RunModule(JSContext* cx, const char* filename, FILE* file, bool compileOnly) return JS_CallFunction(cx, loaderObj, importFun, args, &value); } -#ifdef SPIDERMONKEY_PROMISE static JSObject* ShellGetIncumbentGlobalCallback(JSContext* cx) { @@ -775,12 +769,10 @@ ShellFinishAsyncTaskCallback(JS::AsyncTask* task) asyncTasks->outstanding--; return asyncTasks->finished.append(task); } -#endif // SPIDERMONKEY_PROMISE static bool DrainJobQueue(JSContext* cx) { -#ifdef SPIDERMONKEY_PROMISE ShellContext* sc = GetShellContext(cx); if (sc->quitting || sc->drainingJobQueue) return true; @@ -831,7 +823,6 @@ DrainJobQueue(JSContext* cx) } sc->jobQueue.clear(); sc->drainingJobQueue = false; -#endif // SPIDERMONKEY_PROMISE return true; } @@ -846,7 +837,6 @@ DrainJobQueue(JSContext* cx, unsigned argc, Value* vp) return true; } -#ifdef SPIDERMONKEY_PROMISE static void ForwardingPromiseRejectionTrackerCallback(JSContext* cx, JS::HandleObject promise, PromiseRejectionHandlingState state, void* data) @@ -869,14 +859,12 @@ ForwardingPromiseRejectionTrackerCallback(JSContext* cx, JS::HandleObject promis if (!Call(cx, callback, UndefinedHandleValue, args, &rval)) JS_ClearPendingException(cx); } -#endif // SPIDERMONKEY_PROMISE static bool SetPromiseRejectionTrackerCallback(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); -#ifdef SPIDERMONKEY_PROMISE if (!IsCallable(args.get(0))) { JS_ReportErrorASCII(cx, "setPromiseRejectionTrackerCallback expects a function as its sole " @@ -887,7 +875,6 @@ SetPromiseRejectionTrackerCallback(JSContext* cx, unsigned argc, Value* vp) GetShellContext(cx)->promiseRejectionTrackerCallback = args[0]; JS::SetPromiseRejectionTrackerCallback(cx, ForwardingPromiseRejectionTrackerCallback); -#endif // SPIDERMONKEY_PROMISE args.rval().setUndefined(); return true; } @@ -2304,6 +2291,8 @@ ValueToScript(JSContext* cx, HandleValue v, JSFunction** funp = nullptr) // Get unwrapped async function. if (IsWrappedAsyncFunction(fun)) fun = GetUnwrappedAsyncFunction(fun); + if (IsWrappedAsyncGenerator(fun)) + fun = GetUnwrappedAsyncGenerator(fun); if (!fun->isInterpreted()) { JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_SCRIPTS_ONLY); @@ -3346,12 +3335,10 @@ WorkerMain(void* arg) return; } -#ifdef SPIDERMONKEY_PROMISE sc->jobQueue.init(cx, JobQueue(SystemAllocPolicy())); JS::SetEnqueuePromiseJobCallback(cx, ShellEnqueuePromiseJobCallback); JS::SetGetIncumbentGlobalCallback(cx, ShellGetIncumbentGlobalCallback); JS::SetAsyncTaskCallbacks(cx, ShellStartAsyncTaskCallback, ShellFinishAsyncTaskCallback); -#endif // SPIDERMONKEY_PROMISE EnvironmentPreparer environmentPreparer(cx); @@ -3382,11 +3369,9 @@ WorkerMain(void* arg) JS::SetLargeAllocationFailureCallback(cx, nullptr, nullptr); -#ifdef SPIDERMONKEY_PROMISE JS::SetGetIncumbentGlobalCallback(cx, nullptr); JS::SetEnqueuePromiseJobCallback(cx, nullptr); sc->jobQueue.reset(); -#endif // SPIDERMONKEY_PROMISE KillWatchdog(cx); @@ -7942,12 +7927,10 @@ main(int argc, char** argv, char** envp) if (!JS::InitSelfHostedCode(cx)) return 1; -#ifdef SPIDERMONKEY_PROMISE sc->jobQueue.init(cx, JobQueue(SystemAllocPolicy())); JS::SetEnqueuePromiseJobCallback(cx, ShellEnqueuePromiseJobCallback); JS::SetGetIncumbentGlobalCallback(cx, ShellGetIncumbentGlobalCallback); JS::SetAsyncTaskCallbacks(cx, ShellStartAsyncTaskCallback, ShellFinishAsyncTaskCallback); -#endif // SPIDERMONKEY_PROMISE EnvironmentPreparer environmentPreparer(cx); @@ -7977,11 +7960,9 @@ main(int argc, char** argv, char** envp) JS::SetLargeAllocationFailureCallback(cx, nullptr, nullptr); -#ifdef SPIDERMONKEY_PROMISE JS::SetGetIncumbentGlobalCallback(cx, nullptr); JS::SetEnqueuePromiseJobCallback(cx, nullptr); sc->jobQueue.reset(); -#endif // SPIDERMONKEY_PROMISE KillWatchdog(cx); diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js b/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js index 8cda44d87..8bd0512c5 100644 --- a/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js +++ b/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js @@ -1,7 +1,7 @@ // |reftest| skip-if(!this.hasOwnProperty("Intl")) // Generated by make_intl_data.py. DO NOT EDIT. -// tzdata version = 2019a +// tzdata version = 2019c const tzMapper = [ x => x, diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js index a3efe0310..c760fd85e 100644 --- a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js +++ b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js @@ -1,7 +1,7 @@ // |reftest| skip-if(!this.hasOwnProperty("Intl")) // Generated by make_intl_data.py. DO NOT EDIT. -// tzdata version = 2019a +// tzdata version = 2019c const tzMapper = [ x => x, diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js index b61593f78..38db5e77d 100644 --- a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js +++ b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js @@ -1,7 +1,7 @@ // |reftest| skip-if(!this.hasOwnProperty("Intl")) // Generated by make_intl_data.py. DO NOT EDIT. -// tzdata version = 2019a +// tzdata version = 2019c const tzMapper = [ x => x, diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js b/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js index 12b55c214..64a25c241 100644 --- a/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js +++ b/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js @@ -1,7 +1,7 @@ // |reftest| skip-if(!this.hasOwnProperty("Intl")) // Generated by make_intl_data.py. DO NOT EDIT. -// tzdata version = 2019a +// tzdata version = 2019c const tzMapper = [ x => x, diff --git a/js/src/tests/ecma_2017/AsyncFunctions/properties.js b/js/src/tests/ecma_2017/AsyncFunctions/properties.js new file mode 100644 index 000000000..ca383901b --- /dev/null +++ b/js/src/tests/ecma_2017/AsyncFunctions/properties.js @@ -0,0 +1,76 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function assertOwnDescriptor(object, propertyKey, expected) { + var desc = Object.getOwnPropertyDescriptor(object, propertyKey); + if (desc === undefined) { + assertEq(expected, undefined, "Property shouldn't be present"); + return; + } + + assertEq(desc.enumerable, expected.enumerable, `${String(propertyKey)}.[[Enumerable]]`); + assertEq(desc.configurable, expected.configurable, `${String(propertyKey)}.[[Configurable]]`); + + if (Object.prototype.hasOwnProperty.call(desc, "value")) { + assertEq(desc.value, expected.value, `${String(propertyKey)}.[[Value]]`); + assertEq(desc.writable, expected.writable, `${String(propertyKey)}.[[Writable]]`); + } else { + assertEq(desc.get, expected.get, `${String(propertyKey)}.[[Get]]`); + assertEq(desc.set, expected.set, `${String(propertyKey)}.[[Set]]`); + } +} + +async function asyncFunc(){} +var AsyncFunctionPrototype = Object.getPrototypeOf(asyncFunc); +var AsyncFunction = AsyncFunctionPrototype.constructor; + + +// ES2017, 25.5.2 Properties of the AsyncFunction Constructor + +assertEqArray(Object.getOwnPropertyNames(AsyncFunction).sort(), ["length", "name", "prototype"]); +assertEqArray(Object.getOwnPropertySymbols(AsyncFunction), []); + +assertOwnDescriptor(AsyncFunction, "length", { + value: 1, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(AsyncFunction, "name", { + value: "AsyncFunction", writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(AsyncFunction, "prototype", { + value: AsyncFunctionPrototype, writable: false, enumerable: false, configurable: false +}); + + +// ES2017, 25.5.3 Properties of the AsyncFunction Prototype Object + +assertEqArray(Object.getOwnPropertyNames(AsyncFunctionPrototype).sort(), ["constructor"]); +assertEqArray(Object.getOwnPropertySymbols(AsyncFunctionPrototype), [Symbol.toStringTag]); + +assertOwnDescriptor(AsyncFunctionPrototype, "constructor", { + value: AsyncFunction, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(AsyncFunctionPrototype, Symbol.toStringTag, { + value: "AsyncFunction", writable: false, enumerable: false, configurable: true +}); + + +// ES2017, 25.5.4 AsyncFunction Instances + +assertEqArray(Object.getOwnPropertyNames(asyncFunc).sort(), ["length", "name"]); +assertEqArray(Object.getOwnPropertySymbols(asyncFunc), []); + +assertOwnDescriptor(asyncFunc, "length", { + value: 0, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(asyncFunc, "name", { + value: "asyncFunc", writable: false, enumerable: false, configurable: true +}); + + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_5/Array/frozen-dense-array.js b/js/src/tests/ecma_5/Array/frozen-dense-array.js index 9db63036f..cdb86daa3 100644 --- a/js/src/tests/ecma_5/Array/frozen-dense-array.js +++ b/js/src/tests/ecma_5/Array/frozen-dense-array.js @@ -38,24 +38,5 @@ assertEq(delete a[0], false); assertArrayIsExpected(); -var watchpointCalled = false; -// NOTE: Be careful with the position of this test, since this sparsifies the -// elements and you might not test what you think you're testing otherwise. -a.watch(2, function(prop, oldValue, newValue) { - watchpointCalled = true; - assertEq(prop, 2); - assertEq(oldValue, 1); - assertEq(newValue, "foo"); -}); - -assertArrayIsExpected(); - -a.length = 5; -a[2] = "foo"; -assertEq(watchpointCalled, true); -assertEq(delete a[0], false); - -assertArrayIsExpected(); - if (typeof reportCompare === "function") reportCompare(true, true); diff --git a/js/src/tests/ecma_5/extensions/watch-array-length.js b/js/src/tests/ecma_5/extensions/watch-array-length.js deleted file mode 100644 index e9b356efa..000000000 --- a/js/src/tests/ecma_5/extensions/watch-array-length.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ - -var hitCount; -function watcher(p,o,n) { hitCount++; return n; } - -var a = [1]; -a.watch('length', watcher); -hitCount = 0; -a.length = 0; -reportCompare(1, hitCount, "lenient; configurable: watchpoint hit"); - -var b = Object.defineProperty([1],'0',{configurable:false}); -b.watch('length', watcher); -hitCount = 0; -var result; -try { - b.length = 0; - result = "no error"; -} catch (x) { - result = x.toString(); -} -reportCompare(1, hitCount, "lenient; non-configurable: watchpoint hit"); -reportCompare(1, b.length, "lenient; non-configurable: length unchanged"); -reportCompare("no error", result, "lenient; non-configurable: no error thrown"); - -var c = Object.defineProperty([1],'0',{configurable:false}); -c.watch('length', watcher); -hitCount = 0; -var threwTypeError; -try { - (function(){'use strict'; c.length = 0;})(); - threwTypeError = false; -} catch (x) { - threwTypeError = x instanceof TypeError; -} -reportCompare(1, hitCount, "strict; non-configurable: watchpoint hit"); -reportCompare(1, c.length, "strict; non-configurable: length unchanged"); -reportCompare(true, threwTypeError, "strict; non-configurable: TypeError thrown"); diff --git a/js/src/tests/ecma_5/extensions/watch-inherited-property.js b/js/src/tests/ecma_5/extensions/watch-inherited-property.js deleted file mode 100644 index 1a0ad566b..000000000 --- a/js/src/tests/ecma_5/extensions/watch-inherited-property.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ - -/* Create a prototype object with a setter property. */ -var protoSetterCount; -var proto = ({ set x(v) { protoSetterCount++; } }); - -/* Put a watchpoint on that setter. */ -var protoWatchCount; -proto.watch('x', function() { protoWatchCount++; }); - -/* Make an object with the above as its prototype. */ -function C() { } -C.prototype = proto; -var o = new C(); - -/* - * Set a watchpoint on the property in the inheriting object. We have - * defined this to mean "duplicate the property, setter and all, in the - * inheriting object." I don't think debugging observation mechanisms - * should mutate the program being run, but that's what we've got. - */ -var oWatchCount; -o.watch('x', function() { oWatchCount++; }); - -/* - * Assign to the property. This should trip the watchpoint on the inheriting object and - * the setter. - */ -protoSetterCount = protoWatchCount = oWatchCount = 0; -o.x = 1; -assertEq(protoWatchCount, 0); -assertEq(oWatchCount, 1); -assertEq(protoSetterCount, 1); - -reportCompare(true, true); diff --git a/js/src/tests/ecma_5/extensions/watch-replaced-setter.js b/js/src/tests/ecma_5/extensions/watch-replaced-setter.js deleted file mode 100644 index 05cf60aff..000000000 --- a/js/src/tests/ecma_5/extensions/watch-replaced-setter.js +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ - -/* A stock watcher function. */ -var watcherCount; -function watcher(id, oldval, newval) { watcherCount++; return newval; } - -/* Create an object with a JavaScript setter. */ -var setterCount; -var o = { w:2, set x(v) { setterCount++; } }; - -/* - * Put the object in dictionary mode, so that JSObject::putProperty will - * mutate its shapes instead of creating new ones. - */ -delete o.w; - -/* - * Place a watchpoint on the property. The watchpoint structure holds the - * original JavaScript setter, and a pointer to the shape. - */ -o.watch('x', watcher); - -/* - * Replace the accessor property with a value property. The shape's setter - * should become a non-JS setter, js_watch_set, and the watchpoint - * structure's saved setter should be updated (in this case, cleared). - */ -Object.defineProperty(o, 'x', { value:3, - writable:true, - enumerable:true, - configurable:true }); - -/* - * Assign to the property. This should trigger js_watch_set, which should - * call the handler, and then see that there is no JS-level setter to pass - * control on to, and return. - */ -watcherCount = setterCount = 0; -o.x = 3; -assertEq(watcherCount, 1); -assertEq(setterCount, 0); - -reportCompare(true, true); diff --git a/js/src/tests/ecma_5/extensions/watch-setter-become-setter.js b/js/src/tests/ecma_5/extensions/watch-setter-become-setter.js deleted file mode 100644 index f5eff98b8..000000000 --- a/js/src/tests/ecma_5/extensions/watch-setter-become-setter.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ - -/* Create an object with a JavaScript setter. */ -var firstSetterCount; -var o = { w:2, set x(v) { firstSetterCount++; } }; - -/* - * Put the object in dictionary mode, so that JSObject::putProperty will - * mutate its shapes instead of creating new ones. - */ -delete o.w; - -/* A stock watcher function. */ -var watcherCount; -function watcher(id, oldval, newval) { watcherCount++; return newval; } - -/* - * Place a watchpoint on the property. The property's shape now has the - * watchpoint setter, with the original setter saved in the watchpoint - * structure. - */ -o.watch('x', watcher); - -/* - * Replace the setter with a new setter. The shape should get updated to - * refer to the new setter, and then the watchpoint setter should be - * re-established. - */ -var secondSetterCount; -Object.defineProperty(o, 'x', { set: function () { secondSetterCount++ } }); - -/* - * Assign to the property. This should trigger the watchpoint and the new setter. - */ -watcherCount = firstSetterCount = secondSetterCount = 0; -o.x = 3; -assertEq(watcherCount, 1); -assertEq(firstSetterCount, 0); -assertEq(secondSetterCount, 1); - -reportCompare(true, true); diff --git a/js/src/tests/ecma_5/extensions/watch-value-prop-becoming-setter.js b/js/src/tests/ecma_5/extensions/watch-value-prop-becoming-setter.js deleted file mode 100644 index 03cdd788e..000000000 --- a/js/src/tests/ecma_5/extensions/watch-value-prop-becoming-setter.js +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ - -/* A stock watcher function. */ -var watcherCount; -function watcher(id, old, newval) { - watcherCount++; - return newval; -} - -/* Create an object with a value property. */ -var o = { w:2, x:3 }; - -/* - * Place a watchpoint on the value property. The watchpoint structure holds - * the original JavaScript setter, and a pointer to the shape. - */ -o.watch('x', watcher); - -/* - * Put the object in dictionary mode, so that JSObject::putProperty will - * mutate its shapes instead of creating new ones. - */ -delete o.w; - -/* - * Replace the value property with a setter. - */ -var setterCount; -o.__defineSetter__('x', function() { setterCount++; }); - -/* - * Trigger the watchpoint. The watchpoint handler should run, and then the - * setter should run. - */ -watcherCount = setterCount = 0; -o.x = 4; -assertEq(watcherCount, 1); -assertEq(setterCount, 1); - -reportCompare(true, true); diff --git a/js/src/tests/ecma_5/extensions/watchpoint-deletes-JSPropertyOp-setter.js b/js/src/tests/ecma_5/extensions/watchpoint-deletes-JSPropertyOp-setter.js deleted file mode 100644 index 85410bbd4..000000000 --- a/js/src/tests/ecma_5/extensions/watchpoint-deletes-JSPropertyOp-setter.js +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ - -function make_watcher(name) { - return function (id, oldv, newv) { - print("watched " + name + "[0]"); - }; -} - -var o, p; -function f(flag) { - if (flag) { - o = arguments; - } else { - p = arguments; - o.watch(0, make_watcher('o')); - p.watch(0, make_watcher('p')); - - /* - * Previously, the watchpoint implementation actually substituted its magic setter - * functions for the setters of shared shapes, and then 1) carefully ignored calls - * to its magic setter from unrelated objects, and 2) avoided restoring the - * original setter until all watchpoints on that shape had been removed. - * - * However, when the watchpoint code began using JSObject::changeProperty and - * js_ChangeNativePropertyAttrs to change shapes' setters, the shape tree code - * became conscious of the presence of watchpoints, and shared shapes between - * objects only when their watchpoint nature coincided. Clearing the magic setter - * from one object's shape would not affect other objects, because the - * watchpointed and non-watchpointed shapes were distinct if they were shared. - * - * Thus, the first unwatch call must go ahead and fix p's shape, even though a - * watchpoint exists on the same shape in o. o's watchpoint's presence shouldn't - * cause 'unwatch' to leave p's magic setter in place. - */ - - /* DropWatchPointAndUnlock would see o's watchpoint, and not change p's property. */ - p.unwatch(0); - - /* DropWatchPointAndUnlock would fix o's property, but not p's; p's setter would be gone. */ - o.unwatch(0); - - /* This would fail to invoke the arguments object's setter. */ - p[0] = 4; - - /* And the formal parameter would not get updated. */ - assertEq(flag, 4); - } -} - -f(true); -f(false); - -reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/properties.js b/js/src/tests/ecma_6/Generators/properties.js new file mode 100644 index 000000000..7782e64c0 --- /dev/null +++ b/js/src/tests/ecma_6/Generators/properties.js @@ -0,0 +1,111 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function assertOwnDescriptor(object, propertyKey, expected) { + var desc = Object.getOwnPropertyDescriptor(object, propertyKey); + if (desc === undefined) { + assertEq(expected, undefined, "Property shouldn't be present"); + return; + } + + assertEq(desc.enumerable, expected.enumerable, `${String(propertyKey)}.[[Enumerable]]`); + assertEq(desc.configurable, expected.configurable, `${String(propertyKey)}.[[Configurable]]`); + + if (Object.prototype.hasOwnProperty.call(desc, "value")) { + assertEq(desc.value, expected.value, `${String(propertyKey)}.[[Value]]`); + assertEq(desc.writable, expected.writable, `${String(propertyKey)}.[[Writable]]`); + } else { + assertEq(desc.get, expected.get, `${String(propertyKey)}.[[Get]]`); + assertEq(desc.set, expected.set, `${String(propertyKey)}.[[Set]]`); + } +} + +function* generator(){} +var GeneratorFunctionPrototype = Object.getPrototypeOf(generator); +var GeneratorFunction = GeneratorFunctionPrototype.constructor; +var GeneratorPrototype = GeneratorFunctionPrototype.prototype; + + +// ES2017, 25.2.2 Properties of the GeneratorFunction Constructor + +assertEqArray(Object.getOwnPropertyNames(GeneratorFunction).sort(), ["length", "name", "prototype"]); +assertEqArray(Object.getOwnPropertySymbols(GeneratorFunction), []); + +assertOwnDescriptor(GeneratorFunction, "length", { + value: 1, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorFunction, "name", { + value: "GeneratorFunction", writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorFunction, "prototype", { + value: GeneratorFunctionPrototype, writable: false, enumerable: false, configurable: false +}); + + +// ES2017, 25.2.3 Properties of the GeneratorFunction Prototype Object + +assertEqArray(Object.getOwnPropertyNames(GeneratorFunctionPrototype).sort(), ["constructor", "prototype"]); +assertEqArray(Object.getOwnPropertySymbols(GeneratorFunctionPrototype), [Symbol.toStringTag]); + +assertOwnDescriptor(GeneratorFunctionPrototype, "constructor", { + value: GeneratorFunction, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorFunctionPrototype, "prototype", { + value: GeneratorPrototype, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorFunctionPrototype, Symbol.toStringTag, { + value: "GeneratorFunction", writable: false, enumerable: false, configurable: true +}); + + +// ES2017, 25.2.4 GeneratorFunction Instances + +assertEqArray(Object.getOwnPropertyNames(generator).sort(), ["length", "name", "prototype"]); +assertEqArray(Object.getOwnPropertySymbols(generator), []); + +assertOwnDescriptor(generator, "length", { + value: 0, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(generator, "name", { + value: "generator", writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(generator, "prototype", { + value: generator.prototype, writable: true, enumerable: false, configurable: false +}); + + +// ES2017, 25.3.1 Properties of Generator Prototype + +assertEqArray(Object.getOwnPropertyNames(GeneratorPrototype).sort(), ["constructor", "next", "return", "throw"]); +assertEqArray(Object.getOwnPropertySymbols(GeneratorPrototype), [Symbol.toStringTag]); + +assertOwnDescriptor(GeneratorPrototype, "constructor", { + value: GeneratorFunctionPrototype, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorPrototype, "next", { + value: GeneratorPrototype.next, writable: true, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorPrototype, "return", { + value: GeneratorPrototype.return, writable: true, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorPrototype, "throw", { + value: GeneratorPrototype.throw, writable: true, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorPrototype, Symbol.toStringTag, { + value: "Generator", writable: false, enumerable: false, configurable: true +}); + + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Promise/self-resolve.js b/js/src/tests/ecma_6/Promise/self-resolve.js index e16a2ceb3..4e7e36c6c 100644 --- a/js/src/tests/ecma_6/Promise/self-resolve.js +++ b/js/src/tests/ecma_6/Promise/self-resolve.js @@ -5,6 +5,7 @@ if (!this.Promise) { quit(0); } +// Resolve Promise with itself by directly calling the "Promise Resolve Function". let resolve; let promise = new Promise(function(x) { resolve = x; }); resolve(promise) @@ -20,4 +21,23 @@ drainJobQueue() assertEq(results.length, 1); assertEq(results[0], "rejected"); +// Resolve Promise with itself when the "Promise Resolve Function" is called +// from (the fast path in) PromiseReactionJob. +results = []; + +promise = new Promise(x => { resolve = x; }); +let promise2 = promise.then(() => promise2); + +promise2.then(() => assertEq(true, false, "not reached"), res => { + assertEq(res instanceof TypeError, true); + results.push("rejected"); +}); + +resolve(); + +drainJobQueue(); + +assertEq(results.length, 1); +assertEq(results[0], "rejected"); + this.reportCompare && reportCompare(0, 0, "ok"); diff --git a/js/src/tests/ecma_6/Symbol/well-known.js b/js/src/tests/ecma_6/Symbol/well-known.js index 8c5de1279..095d333a0 100644 --- a/js/src/tests/ecma_6/Symbol/well-known.js +++ b/js/src/tests/ecma_6/Symbol/well-known.js @@ -11,7 +11,8 @@ var names = [ "hasInstance", "split", "toPrimitive", - "unscopables" + "unscopables", + "asyncIterator" ]; for (var name of names) { diff --git a/js/src/tests/ecma_7/AsyncFunctions/syntax.js b/js/src/tests/ecma_7/AsyncFunctions/syntax.js index 28e5febc1..0b4b50af4 100644 --- a/js/src/tests/ecma_7/AsyncFunctions/syntax.js +++ b/js/src/tests/ecma_7/AsyncFunctions/syntax.js @@ -9,9 +9,6 @@ if (typeof Reflect !== "undefined" && Reflect.parse) { assertEq(Reflect.parse("async function a() {}").body[0].async, true); assertEq(Reflect.parse("() => {}").body[0].async, undefined); - // Async generators are not allowed (with regards to spec) - assertThrows(() => Reflect.parse("async function* a() {}"), SyntaxError); - // No line terminator after async assertEq(Reflect.parse("async\nfunction a(){}").body[0].expression.name, "async"); diff --git a/js/src/tests/js1_5/Object/regress-362872-01.js b/js/src/tests/js1_5/Object/regress-362872-01.js deleted file mode 100644 index 0808ee82b..000000000 --- a/js/src/tests/js1_5/Object/regress-362872-01.js +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 362872; -var summary = 'script should not drop watchpoint that is in use'; -var actual = 'No Crash'; -var expect = 'No Crash'; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - function exploit() { - var rooter = {}, object = {}, filler1 = "", filler2 = "\u5555"; - for(var i = 0; i < 32/2-2; i++) { filler1 += "\u5050"; } - object.watch("foo", function(){ - object.unwatch("foo"); - object.unwatch("foo"); - for(var i = 0; i < 8 * 1024; i++) { - rooter[i] = filler1 + filler2; - } - }); - object.foo = "bar"; - } - exploit(); - - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/Object/regress-362872-02.js b/js/src/tests/js1_5/Object/regress-362872-02.js deleted file mode 100644 index edee43a4a..000000000 --- a/js/src/tests/js1_5/Object/regress-362872-02.js +++ /dev/null @@ -1,24 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Blake Kaplan - */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 362872; -var summary = 'script should not drop watchpoint that is in use'; -var actual = 'No Crash'; -var expect = 'No Crash'; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -this.watch('x', function f() { - print("before"); - x = 3; - print("after"); - }); -x = 3; - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/Regress/regress-127243.js b/js/src/tests/js1_5/Regress/regress-127243.js deleted file mode 100644 index 11779f803..000000000 --- a/js/src/tests/js1_5/Regress/regress-127243.js +++ /dev/null @@ -1,75 +0,0 @@ -// |reftest| skip-if(xulRuntime.OS=="WINNT"&&isDebugBuild) slow -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 127243; -var summary = 'Do not crash on watch'; -var actual = 'No Crash'; -var expect = 'No Crash'; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -if (typeof window != 'undefined' && typeof document != 'undefined') -{ - // delay test driver end - gDelayTestDriverEnd = true; - window.addEventListener('load', handleLoad, false); -} -else -{ - printStatus('This test must be run in the browser'); - reportCompare(expect, actual, summary); - -} - -var div; - -function handleLoad() -{ - div = document.createElement('div'); - document.body.appendChild(div); - div.setAttribute('id', 'id1'); - div.style.width = '50px'; - div.style.height = '100px'; - div.style.overflow = 'auto'; - - for (var i = 0; i < 5; i++) - { - var p = document.createElement('p'); - var t = document.createTextNode('blah'); - p.appendChild(t); - div.appendChild(p); - } - - div.watch('scrollTop', wee); - - setTimeout('setScrollTop()', 1000); -} - -function wee(id, oldval, newval) -{ - var t = document.createTextNode('setting ' + id + - ' value ' + div.scrollTop + - ' oldval ' + oldval + - ' newval ' + newval); - var p = document.createElement('p'); - p.appendChild(t); - document.body.appendChild(p); - - return newval; -} - -function setScrollTop() -{ - div.scrollTop = 42; - - reportCompare(expect, actual, summary); - - gDelayTestDriverEnd = false; - jsTestDriverEnd(); - -} diff --git a/js/src/tests/js1_5/Regress/regress-213482.js b/js/src/tests/js1_5/Regress/regress-213482.js deleted file mode 100644 index 26ed7e894..000000000 --- a/js/src/tests/js1_5/Regress/regress-213482.js +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 213482; -var summary = 'Do not crash watching property when watcher sets property'; -var actual = 'No Crash'; -var expect = 'No Crash'; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -var testobj = {value: 'foo'}; - -function watched (a, b, c) { - testobj.value = (new Date()).getTime(); -} - -function setTest() { - testobj.value = 'b'; -} - -testobj.watch("value", watched); - -setTest(); - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/Regress/regress-240577.js b/js/src/tests/js1_5/Regress/regress-240577.js deleted file mode 100644 index ba8330aa0..000000000 --- a/js/src/tests/js1_5/Regress/regress-240577.js +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Bob Clary - */ - -//----------------------------------------------------------------------------- -// originally reported by Jens Thiele <karme@unforgettable.com> in -var BUGNUMBER = 240577; -var summary = 'object.watch execution context'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -var createWatcher = function ( watchlabel ) -{ - var watcher = function (property, oldvalue, newvalue) - { - actual += watchlabel; return newvalue; - }; - return watcher; -}; - -var watcher1 = createWatcher('watcher1'); - -var object = {property: 'value'}; - -object.watch('property', watcher1); - -object.property = 'newvalue'; - -expect = 'watcher1'; - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/Regress/regress-355341.js b/js/src/tests/js1_5/Regress/regress-355341.js deleted file mode 100644 index ab2a4b884..000000000 --- a/js/src/tests/js1_5/Regress/regress-355341.js +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 355341; -var summary = 'Do not crash with watch and setter'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - Object.defineProperty(this, "x", { set: Function, enumerable: true, configurable: true }); - this.watch('x', function () { }); x = 3; - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/Regress/regress-355344.js b/js/src/tests/js1_5/Regress/regress-355344.js deleted file mode 100644 index 00bd39147..000000000 --- a/js/src/tests/js1_5/Regress/regress-355344.js +++ /dev/null @@ -1,49 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 355344; -var summary = 'Exceptions thrown by watch point'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - var o = {}; - - expect = 'setter: yikes'; - - o.watch('x', function(){throw 'yikes'}); - try - { - o.x = 3; - } - catch(ex) - { - actual = "setter: " + ex; - } - - try - { - eval("") ; - } - catch(e) - { - actual = "eval: " + e; - } - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/Regress/regress-361467.js b/js/src/tests/js1_5/Regress/regress-361467.js deleted file mode 100644 index 371c0a8b5..000000000 --- a/js/src/tests/js1_5/Regress/regress-361467.js +++ /dev/null @@ -1,32 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 361467; -var summary = 'Do not crash with certain watchers'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - expect = actual = 'No Crash'; - - var x; - this.watch('x', print); - x = 5; - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/Regress/regress-361617.js b/js/src/tests/js1_5/Regress/regress-361617.js deleted file mode 100644 index 5d20fd78f..000000000 --- a/js/src/tests/js1_5/Regress/regress-361617.js +++ /dev/null @@ -1,35 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 361617; -var summary = 'Do not crash with getter, watch and gc'; -var actual = 'No Crash'; -var expect = 'No Crash'; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - (function() { - Object.defineProperty(this, "x", { get: function(){}, enumerable: true, configurable: true }); - })(); - this.watch('x', print); - Object.defineProperty(this, "x", { get: function(){}, enumerable: true, configurable: true }); - gc(); - this.unwatch('x'); - x; - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/Regress/regress-385393-06.js b/js/src/tests/js1_5/Regress/regress-385393-06.js deleted file mode 100644 index 327103f39..000000000 --- a/js/src/tests/js1_5/Regress/regress-385393-06.js +++ /dev/null @@ -1,28 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - - -//----------------------------------------------------------------------------- -var BUGNUMBER = 385393; -var summary = 'Regression test for bug 385393'; -var actual = 'No Crash'; -var expect = 'No Crash'; - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - reportCompare(expect, actual, summary); - - true.watch("x", function(){}); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/Regress/regress-506567.js b/js/src/tests/js1_5/Regress/regress-506567.js deleted file mode 100644 index e78dfe1db..000000000 --- a/js/src/tests/js1_5/Regress/regress-506567.js +++ /dev/null @@ -1,45 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 506567; -var summary = 'Do not crash with watched variables'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - if (typeof clearInterval == 'undefined') - { - clearInterval = (function () {}); - } - - var obj = new Object(); - obj.test = null; - obj.watch("test", (function(prop, oldval, newval) - { - if(false) - { - var test = newval % oldval; - var func = (function(){clearInterval(myInterval);}); - } - })); - - obj.test = 'null'; - print(obj.test); - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/extensions/regress-303277.js b/js/src/tests/js1_5/extensions/regress-303277.js deleted file mode 100644 index 319d9a2ed..000000000 --- a/js/src/tests/js1_5/extensions/regress-303277.js +++ /dev/null @@ -1,19 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 303277; -var summary = 'Do not crash with crash with a watchpoint for __proto__ property '; -var actual = 'No Crash'; -var expect = 'No Crash'; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -var o = {}; -o.watch("__proto__", function(){return null;}); -o.__proto__ = null; - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-355339.js b/js/src/tests/js1_5/extensions/regress-355339.js deleted file mode 100644 index 9b15bd742..000000000 --- a/js/src/tests/js1_5/extensions/regress-355339.js +++ /dev/null @@ -1,32 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 355339; -var summary = 'Do not assert: sprop->setter != js_watch_set'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - expect = actual = 'No Crash'; - o = {}; - o.watch("j", function(a,b,c) { print("*",a,b,c) }); - o.unwatch("j"); - o.watch("j", function(a,b,c) { print("*",a,b,c) }); - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/extensions/regress-361346.js b/js/src/tests/js1_5/extensions/regress-361346.js deleted file mode 100644 index 297c3b1f2..000000000 --- a/js/src/tests/js1_5/extensions/regress-361346.js +++ /dev/null @@ -1,22 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 361346; -var summary = 'Crash with setter, watch, GC'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -expect = actual = 'No Crash'; - -Object.defineProperty(this, "x", { set: new Function, enumerable: true, configurable: true }); -this.watch('x', function(){}); -gc(); -x = {}; - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-361360.js b/js/src/tests/js1_5/extensions/regress-361360.js deleted file mode 100644 index 98e6575d9..000000000 --- a/js/src/tests/js1_5/extensions/regress-361360.js +++ /dev/null @@ -1,32 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 361360; -var summary = 'Do not assert: !caller || caller->pc involving setter and watch'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - expect = actual = 'No Crash'; - - this.__defineSetter__('x', eval); - this.watch('x', function(){}); - x = 3; - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/extensions/regress-361552.js b/js/src/tests/js1_5/extensions/regress-361552.js deleted file mode 100644 index eed54e6dd..000000000 --- a/js/src/tests/js1_5/extensions/regress-361552.js +++ /dev/null @@ -1,27 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 361552; -var summary = 'Crash with setter, watch, Script'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -expect = actual = 'No Crash'; - -if (typeof Script == 'undefined') -{ - print('Test skipped. Script not defined.'); -} -else -{ - this.__defineSetter__('x', gc); - this.watch('x', new Script('')); - x = 3; -} -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-361558.js b/js/src/tests/js1_5/extensions/regress-361558.js deleted file mode 100644 index a9a3ae725..000000000 --- a/js/src/tests/js1_5/extensions/regress-361558.js +++ /dev/null @@ -1,19 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 361558; -var summary = 'Do not assert: sprop->setter != js_watch_set'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -expect = actual = 'No Crash'; - -({}.__proto__.watch('x', print)); ({}.watch('x', print)); - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-361571.js b/js/src/tests/js1_5/extensions/regress-361571.js deleted file mode 100644 index bf89d794b..000000000 --- a/js/src/tests/js1_5/extensions/regress-361571.js +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 361571; -var summary = 'Do not assert: fp->scopeChain == parent'; -var actual = 'No Crash'; -var expect = 'No Crash'; - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - try - { - o = {}; - o.__defineSetter__('y', eval); - o.watch('y', function () { return "";}); - o.y = 1; - } - catch(ex) - { - printStatus('Note eval can no longer be called directly'); - expect = 'EvalError: function eval must be called directly, and not by way of a function of another name'; - actual = ex + ''; - } - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/extensions/regress-361856.js b/js/src/tests/js1_5/extensions/regress-361856.js deleted file mode 100644 index e7e2f675b..000000000 --- a/js/src/tests/js1_5/extensions/regress-361856.js +++ /dev/null @@ -1,35 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 361856; -var summary = 'Do not assert: overwriting @ js_AddScopeProperty'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - function testit() { - var obj = {}; - obj.watch("foo", function(){}); - delete obj.foo; - obj = null; - gc(); - } - testit(); - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/extensions/regress-361964.js b/js/src/tests/js1_5/extensions/regress-361964.js deleted file mode 100644 index fcb8bba01..000000000 --- a/js/src/tests/js1_5/extensions/regress-361964.js +++ /dev/null @@ -1,54 +0,0 @@ -// |reftest| skip -- slow, alert not dismissed, now busted by harness -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 361964; -var summary = 'Crash [@ MarkGCThingChildren] involving watch and setter'; -var actual = 'No Crash'; -var expect = 'No Crash'; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - var doc; - if (typeof document == 'undefined') - { - doc = {}; - } - else - { - doc = document; - } - - if (typeof alert == 'undefined') - { - alert = print; - } - -// Crash: - doc.watch("title", function(a,b,c,d) { - return { toString : function() { alert(1); } }; - }); - doc.title = "xxx"; - -// No crash: - doc.watch("title", function() { - return { toString : function() { alert(1); } }; - }); - doc.title = "xxx"; - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/extensions/regress-385134.js b/js/src/tests/js1_5/extensions/regress-385134.js deleted file mode 100644 index 041f4d6e7..000000000 --- a/js/src/tests/js1_5/extensions/regress-385134.js +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 385134; -var summary = 'Do not crash with setter, watch, uneval'; -var actual = 'No Crash'; -var expect = 'No Crash'; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - if (typeof this.__defineSetter__ != 'undefined' && - typeof this.watch != 'undefined' && - typeof uneval != 'undefined') - { - try { - this.__defineSetter__(0, function(){}); - } catch (exc) { - // In the browser, this fails. Ignore the error. - } - this.watch(0, function(){}); - uneval(this); - } - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/extensions/regress-385393-09.js b/js/src/tests/js1_5/extensions/regress-385393-09.js deleted file mode 100644 index 42834824a..000000000 --- a/js/src/tests/js1_5/extensions/regress-385393-09.js +++ /dev/null @@ -1,18 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - - -//----------------------------------------------------------------------------- -var BUGNUMBER = 385393; -var summary = 'Regression test for bug 385393'; -var actual = 'No Crash'; -var expect = 'No Crash'; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -eval("this.__defineSetter__('x', gc); this.watch('x', [].slice); x = 1;"); - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-390597.js b/js/src/tests/js1_5/extensions/regress-390597.js deleted file mode 100644 index 9f8596adc..000000000 --- a/js/src/tests/js1_5/extensions/regress-390597.js +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 390597; -var summary = 'watch point + eval-as-setter allows access to dead JSStackFrame'; -var actual = 'No Crash'; -var expect = 'No Crash'; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - function exploit() { - try - { - var obj = this, args = null; - obj.__defineSetter__("evil", eval); - obj.watch("evil", function() { return "args = arguments;"; }); - obj.evil = null; - eval("print(args[0]);"); - } - catch(ex) - { - print('Caught ' + ex); - } - } - exploit(); - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_5/extensions/regress-420612.js b/js/src/tests/js1_5/extensions/regress-420612.js deleted file mode 100644 index a4491095e..000000000 --- a/js/src/tests/js1_5/extensions/regress-420612.js +++ /dev/null @@ -1,21 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 420612; -var summary = 'Do not assert: obj == pobj'; -var actual = 'No Crash'; -var expect = 'No Crash'; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -var obj = Object.create([]); -obj.unwatch("x"); - -if (typeof reportCompare === "function") - reportCompare(true, true); - -print("Tests complete"); diff --git a/js/src/tests/js1_5/extensions/regress-435345-01.js b/js/src/tests/js1_5/extensions/regress-435345-01.js deleted file mode 100644 index 28beab473..000000000 --- a/js/src/tests/js1_5/extensions/regress-435345-01.js +++ /dev/null @@ -1,100 +0,0 @@ -// |reftest| fails -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 435345; -var summary = 'Watch the length property of arrays'; -var actual = ''; -var expect = ''; - -// see http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Object:watch - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - var arr; - - try - { - expect = 'watcher: propname=length, oldval=0, newval=1; '; - actual = ''; - arr = []; - arr.watch('length', watcher); - arr[0] = '0'; - } - catch(ex) - { - actual = ex + ''; - } - reportCompare(expect, actual, summary + ': 1'); - - try - { - expect = 'watcher: propname=length, oldval=1, newval=2; ' + - 'watcher: propname=length, oldval=2, newval=2; '; - actual = ''; - arr.push(5); - } - catch(ex) - { - actual = ex + ''; - } - reportCompare(expect, actual, summary + ': 2'); - - try - { - expect = 'watcher: propname=length, oldval=2, newval=1; '; - actual = ''; - arr.pop(); - } - catch(ex) - { - actual = ex + ''; - } - reportCompare(expect, actual, summary + ': 3'); - - try - { - expect = 'watcher: propname=length, oldval=1, newval=2; '; - actual = ''; - arr.length++; - } - catch(ex) - { - actual = ex + ''; - } - reportCompare(expect, actual, summary + ': 4'); - - try - { - expect = 'watcher: propname=length, oldval=2, newval=5; '; - actual = ''; - arr.length = 5; - } - catch(ex) - { - actual = ex + ''; - } - reportCompare(expect, actual, summary + ': 5'); - - exitFunc ('test'); -} - -function watcher(propname, oldval, newval) -{ - actual += 'watcher: propname=' + propname + ', oldval=' + oldval + - ', newval=' + newval + '; '; - - return newval; -} - diff --git a/js/src/tests/js1_5/extensions/regress-454040.js b/js/src/tests/js1_5/extensions/regress-454040.js deleted file mode 100644 index 63b5e1488..000000000 --- a/js/src/tests/js1_5/extensions/regress-454040.js +++ /dev/null @@ -1,25 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 454040; -var summary = 'Do not crash @ js_ComputeFilename'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -try -{ - this.__defineGetter__("x", Function); - this.__defineSetter__("x", Function); - this.watch("x", x.__proto__); - x = 1; -} -catch(ex) -{ -} -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-454142.js b/js/src/tests/js1_5/extensions/regress-454142.js deleted file mode 100644 index 05f331278..000000000 --- a/js/src/tests/js1_5/extensions/regress-454142.js +++ /dev/null @@ -1,30 +0,0 @@ -// |reftest| skip-if(Android) -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 454142; -var summary = 'Do not crash with gczeal, setter, watch'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -this.watch("x", function(){}); -delete x; -if (typeof gczeal == 'function') -{ - gczeal(2); -} - -this.__defineSetter__("x", function(){}); - -if (typeof gczeal == 'function') -{ - gczeal(0); -} - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-455413.js b/js/src/tests/js1_5/extensions/regress-455413.js deleted file mode 100644 index d00ab135b..000000000 --- a/js/src/tests/js1_5/extensions/regress-455413.js +++ /dev/null @@ -1,24 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 455413; -var summary = 'Do not assert with JIT: (m != JSVAL_INT) || isInt32(*vp)'; -var actual = 'No Crash'; -var expect = 'No Crash'; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -printBugNumber(BUGNUMBER); -printStatus (summary); - - -this.watch('x', Math.pow); (function() { for(var j=0;j<4;++j){x=1;} })(); - - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-465145.js b/js/src/tests/js1_5/extensions/regress-465145.js deleted file mode 100644 index 84f81703b..000000000 --- a/js/src/tests/js1_5/extensions/regress-465145.js +++ /dev/null @@ -1,24 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 465145; -var summary = 'Do not crash with watched defined setter'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - - -this.__defineSetter__("x", function(){}); -this.watch("x", function(){}); -y = this; -for (var z = 0; z < 2; ++z) { x = y }; -this.__defineSetter__("x", function(){}); -for (var z = 0; z < 2; ++z) { x = y }; - - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-472787.js b/js/src/tests/js1_5/extensions/regress-472787.js deleted file mode 100755 index d3196f05f..000000000 --- a/js/src/tests/js1_5/extensions/regress-472787.js +++ /dev/null @@ -1,31 +0,0 @@ -// |reftest| skip-if(Android) -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 472787; -var summary = 'Do not hang with gczeal, watch and concat'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -this.__defineSetter__("x", Math.sin); -this.watch("x", '' .concat); - -if (typeof gczeal == 'function') -{ - gczeal(2); -} - -x = 1; - -if (typeof gczeal == 'function') -{ - gczeal(0); -} - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-488995.js b/js/src/tests/js1_5/extensions/regress-488995.js deleted file mode 100644 index f7abbe439..000000000 --- a/js/src/tests/js1_5/extensions/regress-488995.js +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 488995; -var summary = 'Do not crash with watch, __defineSetter__ on svg'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - if (typeof document == 'undefined') - { - print('Test skipped: requires browser.'); - } - else - { - try - { - var o=document.createElementNS("http://www.w3.org/2000/svg", "svg"); - var p=o.y; - p.watch('animVal', function(id, oldvar, newval) {} ); - p.type='xxx'; - p.__defineSetter__('animVal', function() {}); - p.animVal=0; - } - catch(ex) - { - } - } - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_6/Regress/regress-476655.js b/js/src/tests/js1_6/Regress/regress-476655.js deleted file mode 100644 index 9077ba3a0..000000000 --- a/js/src/tests/js1_6/Regress/regress-476655.js +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 476655; -var summary = 'TM: Do not assert: count <= (size_t) (fp->regs->sp - StackBase(fp) - depth)'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - - try - { - eval( - "(function (){ for (var y in this) {} })();" + - "[''.watch(\"\", function(){}) for each (x in ['', '', eval, '', '']) if " + - "(x)].map(Function)" - ); - } - catch(ex) - { - } - - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_6/extensions/regress-457521.js b/js/src/tests/js1_6/extensions/regress-457521.js deleted file mode 100644 index 6f4fbda50..000000000 --- a/js/src/tests/js1_6/extensions/regress-457521.js +++ /dev/null @@ -1,24 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 457521; -var summary = 'Do not crash @ js_DecompileValueGenerator'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -try -{ - this.__defineSetter__("x", [,,,].map); - this.watch("x", (new Function("var y, eval"))); - x = true; -} -catch(ex) -{ -} -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_6/extensions/regress-479567.js b/js/src/tests/js1_6/extensions/regress-479567.js deleted file mode 100644 index 6f3525130..000000000 --- a/js/src/tests/js1_6/extensions/regress-479567.js +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 479567; -var summary = 'Do not assert: thing'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - - -function f() -{ - (eval("(function(){for each (y in [false, false, false]);});"))(); -} - -try -{ - this.__defineGetter__("x", gc); - uneval(this.watch("y", this.toSource)) - f(); - throw x; -} -catch(ex) -{ -} - - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_7/extensions/regress-453955.js b/js/src/tests/js1_7/extensions/regress-453955.js deleted file mode 100644 index 454f712f3..000000000 --- a/js/src/tests/js1_7/extensions/regress-453955.js +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 453955; -var summary = 'Do not assert: sprop->setter != js_watch_set || pobj != obj'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - for (var z = 0; z < 2; ++z) - { - [].filter.watch("9", function(y) { yield y; }); - } - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_7/extensions/regress-473282.js b/js/src/tests/js1_7/extensions/regress-473282.js deleted file mode 100644 index c50ac9234..000000000 --- a/js/src/tests/js1_7/extensions/regress-473282.js +++ /dev/null @@ -1,20 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 473282; -var summary = 'Do not assert: thing'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -this.watch("b", "".substring); -this.__defineGetter__("a", gc); -for each (b in [this, null, null]); -a; - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_7/regress/regress-385133-01.js b/js/src/tests/js1_7/regress/regress-385133-01.js deleted file mode 100644 index 03b09f535..000000000 --- a/js/src/tests/js1_7/regress/regress-385133-01.js +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 385133; -var summary = 'Do not crash due to recursion with watch, setter, delete, generator'; -var actual = 'No Crash'; -var expect = 'No Crash'; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - try - { - Object.defineProperty(this, "x", { set: {}.watch, enumerable: true, configurable: true }); - this.watch('x', 'foo'.split); - delete x; - function g(){ x = 1; yield; } - for (i in g()) { } - } - catch(ex) - { - } - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_8/extensions/regress-394709.js b/js/src/tests/js1_8/extensions/regress-394709.js deleted file mode 100644 index d04d8832f..000000000 --- a/js/src/tests/js1_8/extensions/regress-394709.js +++ /dev/null @@ -1,51 +0,0 @@ -// |reftest| skip-if(!xulRuntime.shell) -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 394709; -var summary = 'Do not leak with object.watch and closure'; -var actual = 'No Leak'; -var expect = 'No Leak'; - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - assertEq(finalizeCount(), 0, "invalid initial state"); - - runtest(); - gc(); - assertEq(finalizeCount(), 1, "leaked"); - - runtest(); - gc(); - assertEq(finalizeCount(), 2, "leaked"); - - runtest(); - gc(); - assertEq(finalizeCount(), 3, "leaked"); - - - function runtest () { - var obj = { b: makeFinalizeObserver() }; - obj.watch('b', watcher); - - function watcher(id, old, value) { - ++obj.n; - return value; - } - } - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_8/extensions/regress-481989.js b/js/src/tests/js1_8/extensions/regress-481989.js deleted file mode 100644 index 36efb7567..000000000 --- a/js/src/tests/js1_8/extensions/regress-481989.js +++ /dev/null @@ -1,19 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 481989; -var summary = 'TM: Do not assert: SPROP_HAS_STUB_SETTER(sprop)'; -var actual = ''; -var expect = ''; - -printBugNumber(BUGNUMBER); -printStatus (summary); - - -y = this.watch("x", function(){}); for each (let y in ['', '']) x = y; - - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_8_1/extensions/regress-452498-193.js b/js/src/tests/js1_8_1/extensions/regress-452498-193.js deleted file mode 100644 index 1397bf4bb..000000000 --- a/js/src/tests/js1_8_1/extensions/regress-452498-193.js +++ /dev/null @@ -1,34 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 452498; -var summary = 'TM: upvar2 regression tests'; -var actual = ''; -var expect = ''; - -//------- Comment #193 From Gary Kwong - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - -// Assertion failure: afunbox->parent, at ../jsparse.cpp:1912 - - this.x = undefined; - this.watch("x", Function); - NaN = uneval({ get \u3056 (){ return undefined } }); - x+=NaN; - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_8_1/extensions/regress-452498-196.js b/js/src/tests/js1_8_1/extensions/regress-452498-196.js index 69d5a3586..5b9191199 100644 --- a/js/src/tests/js1_8_1/extensions/regress-452498-196.js +++ b/js/src/tests/js1_8_1/extensions/regress-452498-196.js @@ -21,15 +21,6 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); -// Assertion failure: localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST, at ../jsfun.cpp:916 - - this.x = undefined; - this.watch("x", Function); - NaN = uneval({ get \u3056 (){ return undefined } }); - x+=NaN; - - reportCompare(expect, actual, summary + ': 1'); - // Assertion failure: lexdep->isLet(), at ../jsparse.cpp:1900 (function (){ diff --git a/js/src/tests/js1_8_1/extensions/regress-520572.js b/js/src/tests/js1_8_1/extensions/regress-520572.js deleted file mode 100644 index 97f00029a..000000000 --- a/js/src/tests/js1_8_1/extensions/regress-520572.js +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Blake Kaplan - */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 520572; -var summary = 'watch should innerize the object being watched'; -var actual = 0; -var expect = 2; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - if ("evalcx" in this) { - // shell - let s = evalcx("lazy"); - s.n = 0; - evalcx('this.watch("x", function(){ n++; }); this.x = 4; x = 6', s); - actual = s.n; - reportCompare(expect, actual, summary); - } else { - // browser - this.watch('x', function(){ actual++; }); - this.x = 4; - x = 6; - reportCompare(expect, actual, summary); - } - - exitFunc ('test'); -} diff --git a/js/src/tests/js1_8_1/regress/regress-452498-160.js b/js/src/tests/js1_8_1/regress/regress-452498-160.js index 6498a0b5a..305b41795 100644 --- a/js/src/tests/js1_8_1/regress/regress-452498-160.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-160.js @@ -21,11 +21,6 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); -// crash [@ js_Interpret] - (eval("(function(){ this.watch(\"x\", function () { new function ()y } ); const y = undefined });"))(); - x = NaN; - reportCompare(expect, actual, summary + ': 2'); - // Assertion failure: JOF_OPTYPE(op) == JOF_ATOM, at ../jsemit.cpp:5916 ({ set z(v){}, set y(v)--x, set w(v)--w }); reportCompare(expect, actual, summary + ': 3'); diff --git a/js/src/tests/js1_8_5/extensions/regress-604781-1.js b/js/src/tests/js1_8_5/extensions/regress-604781-1.js deleted file mode 100644 index a7c43f95d..000000000 --- a/js/src/tests/js1_8_5/extensions/regress-604781-1.js +++ /dev/null @@ -1,24 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -var watcherCount, setterCount; -function watcher(id, oldval, newval) { watcherCount++; return newval; } -function setter(newval) { setterCount++; } - -var p = { set x(v) { setter(v); } }; -p.watch('x', watcher); - -watcherCount = setterCount = 0; -p.x = 2; -assertEq(setterCount, 1); -assertEq(watcherCount, 1); - -var o = Object.defineProperty({}, 'x', { set:setter, enumerable:true, configurable:true }); -o.watch('x', watcher); - -watcherCount = setterCount = 0; -o.x = 2; -assertEq(setterCount, 1); -assertEq(watcherCount, 1); - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-604781-2.js b/js/src/tests/js1_8_5/extensions/regress-604781-2.js deleted file mode 100644 index 7aba4a274..000000000 --- a/js/src/tests/js1_8_5/extensions/regress-604781-2.js +++ /dev/null @@ -1,13 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -var log; -function watcher(id, old, newval) { log += 'watcher'; return newval; } -var o = { set x(v) { log += 'setter'; } }; -o.watch('x', watcher); -Object.defineProperty(o, 'x', {value: 3, writable: true}); -log = ''; -o.x = 3; -assertEq(log, 'watcher'); - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-1.js b/js/src/tests/js1_8_5/extensions/regress-627984-1.js deleted file mode 100644 index a3726630a..000000000 --- a/js/src/tests/js1_8_5/extensions/regress-627984-1.js +++ /dev/null @@ -1,16 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -// See bug 627984, comment 17, item 1. -var obj; -var methods = []; -for (var i = 0; i < 2; i++) { - obj = {m: function () { return this.x; }}; - obj.watch("m", function (id, oldval, newval) { methods[i] = oldval; }); - obj.m = 0; -} -assertEq(typeof methods[0], "function"); -assertEq(typeof methods[1], "function"); -assertEq(methods[0] !== methods[1], true); - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-2.js b/js/src/tests/js1_8_5/extensions/regress-627984-2.js deleted file mode 100644 index c4f1b508c..000000000 --- a/js/src/tests/js1_8_5/extensions/regress-627984-2.js +++ /dev/null @@ -1,15 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -// See bug 627984, comment 17, item 2. -var obj = {}; -var x; -obj.watch("m", function (id, oldval, newval) { - x = this.m; - return newval; - }); -delete obj.m; -obj.m = function () { return this.method; }; -obj.m = 2; - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-3.js b/js/src/tests/js1_8_5/extensions/regress-627984-3.js deleted file mode 100644 index cbe4e10fa..000000000 --- a/js/src/tests/js1_8_5/extensions/regress-627984-3.js +++ /dev/null @@ -1,14 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -// Don't write string value to method slot. -// See bug 627984, comment 17, item 2. -var obj = {}; -obj.watch("m", function (id, oldval, newval) { - return 'ok'; - }); -delete obj.m; -obj.m = function () { return this.x; }; -assertEq(obj.m, 'ok'); - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-4.js b/js/src/tests/js1_8_5/extensions/regress-627984-4.js deleted file mode 100644 index bbc017ffb..000000000 --- a/js/src/tests/js1_8_5/extensions/regress-627984-4.js +++ /dev/null @@ -1,15 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -// See bug 627984, comment 17, item 3. -var obj = {}; -obj.watch("m", function (id, oldval, newval) { - delete obj.m; - obj.m = function () {}; - return newval; - }); -delete obj.m; -obj.m = 1; -assertEq(obj.m, 1); - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-5.js b/js/src/tests/js1_8_5/extensions/regress-627984-5.js deleted file mode 100644 index 704d8421c..000000000 --- a/js/src/tests/js1_8_5/extensions/regress-627984-5.js +++ /dev/null @@ -1,13 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -// Bug 627984 comment 11. -var o = ({}); -o.p = function() {}; -o.watch('p', function() { }); -o.q = function() {} -delete o.p; -o.p = function() {}; -assertEq(o.p, void 0); - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-6.js b/js/src/tests/js1_8_5/extensions/regress-627984-6.js deleted file mode 100644 index cb1a0fca9..000000000 --- a/js/src/tests/js1_8_5/extensions/regress-627984-6.js +++ /dev/null @@ -1,15 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -// Bug 627984 description. -var o = Array; -o.p = function() {}; -o.watch('p', function() { }); -for(var x in o) { - o[x]; -} -delete o.p; -o.p = function() {}; -assertEq(o.p, void 0); - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-7.js b/js/src/tests/js1_8_5/extensions/regress-627984-7.js deleted file mode 100644 index b13a0e912..000000000 --- a/js/src/tests/js1_8_5/extensions/regress-627984-7.js +++ /dev/null @@ -1,9 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -// See bug 627984 comment 20. -var obj = {m: function () {}}; -obj.watch("m", function () { throw 'FAIL'; }); -var f = obj.m; // don't call the watchpoint - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-631723.js b/js/src/tests/js1_8_5/extensions/regress-631723.js deleted file mode 100644 index f7c755603..000000000 --- a/js/src/tests/js1_8_5/extensions/regress-631723.js +++ /dev/null @@ -1,10 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -var o = {a:1, b:2}; -o.watch("p", function() { return 13; }); -delete o.p; -o.p = 0; -assertEq(o.p, 13); - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-636697.js b/js/src/tests/js1_8_5/extensions/regress-636697.js deleted file mode 100644 index 6b3b1de37..000000000 --- a/js/src/tests/js1_8_5/extensions/regress-636697.js +++ /dev/null @@ -1,11 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -var a = {set p(x) {}}; -a.watch('p', function () {}); -var b = Object.create(a); -b.watch('p', function () {}); -delete b.p; -b.p = 0; - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-637985.js b/js/src/tests/js1_8_5/extensions/regress-637985.js deleted file mode 100644 index 305bfc820..000000000 --- a/js/src/tests/js1_8_5/extensions/regress-637985.js +++ /dev/null @@ -1,8 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -var obj = {}; -obj.watch(-1, function(){}); -obj.unwatch("-1"); // don't assert - -reportCompare(0, 0, 'ok');
\ No newline at end of file diff --git a/js/src/tests/js1_8_5/extensions/regress-691746.js b/js/src/tests/js1_8_5/extensions/regress-691746.js deleted file mode 100644 index 26f732d07..000000000 --- a/js/src/tests/js1_8_5/extensions/regress-691746.js +++ /dev/null @@ -1,11 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -var obj = {}; -try { - obj.watch(QName(), function () {}); -} catch (exc) { -} -gc(); - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/watch-undefined-setter.js b/js/src/tests/js1_8_5/extensions/watch-undefined-setter.js deleted file mode 100644 index 92608de0e..000000000 --- a/js/src/tests/js1_8_5/extensions/watch-undefined-setter.js +++ /dev/null @@ -1,19 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: - * Gary Kwong - */ - -var gTestfile = 'watch-undefined-setter.js'; -//----------------------------------------------------------------------------- -var BUGNUMBER = 560277; -var summary = - 'Crash [@ JSObject::getParent] or [@ js_WrapWatchedSetter] or ' + - '[@ js_GetClassPrototype]'; - -this.watch("x", function() { }); -Object.defineProperty(this, "x", { set: undefined, configurable: true }); - -reportCompare(true, true); diff --git a/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js b/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js index be65bd76e..51b7c6926 100644 --- a/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js +++ b/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js @@ -53,9 +53,8 @@ function asyncFunDecl(id, params, body) { params: params, defaults: [], body: body, - generator: true, - async: true, - style: "es6" }); + generator: false, + async: true }); } function varDecl(decls) { return Pattern({ type: "VariableDeclaration", declarations: decls, kind: "var" }); @@ -181,9 +180,8 @@ function asyncFunExpr(id, args, body) { id: id, params: args, body: body, - generator: true, - async: true, - style: "es6" }); + generator: false, + async: true }); } function arrowExpr(args, body) { return Pattern({ type: "ArrowFunctionExpression", @@ -194,10 +192,9 @@ function asyncArrowExpr(isExpression, args, body) { return Pattern({ type: "ArrowFunctionExpression", params: args, body: body, - generator: true, + generator: false, async: true, - expression: isExpression, - style: "es6" }); + expression: isExpression }); } function metaProperty(meta, property) { diff --git a/js/src/tests/js1_8_5/regress/regress-533876.js b/js/src/tests/js1_8_5/regress/regress-533876.js deleted file mode 100644 index e44bc8a4f..000000000 --- a/js/src/tests/js1_8_5/regress/regress-533876.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributors: Gary Kwong and Jason Orendorff - */ - -var savedEval = eval; -var x = [0]; -eval(); - -x.__proto__ = this; // x has non-dictionary scope -try { - DIE; -} catch(e) { -} - -delete eval; // force dictionary scope for global -gc(); -eval = savedEval; -var f = eval("(function () { return /x/; })"); -x.watch('x', f); // clone property from global to x, including SPROP_IN_DICTIONARY flag - -reportCompare("ok", "ok", "bug 533876"); diff --git a/js/src/tests/js1_8_5/regress/regress-548276.js b/js/src/tests/js1_8_5/regress/regress-548276.js deleted file mode 100644 index 5e306eba1..000000000 --- a/js/src/tests/js1_8_5/regress/regress-548276.js +++ /dev/null @@ -1,10 +0,0 @@ -// |reftest| skip -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributors: Gary Kwong and Jason Orendorff - */ -var obj = {}; -obj.__defineSetter__("x", function() {}); -obj.watch("x", function() {}); -obj.__defineSetter__("x", /a/); diff --git a/js/src/tests/js1_8_5/regress/regress-584648.js b/js/src/tests/js1_8_5/regress/regress-584648.js deleted file mode 100644 index a1635ea51..000000000 --- a/js/src/tests/js1_8_5/regress/regress-584648.js +++ /dev/null @@ -1,16 +0,0 @@ -// |reftest| skip -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ -// Contributors: Gary Kwong <gary@rumblingedge.com> -// Jason Orendorff <jorendorff@mozilla.com> - -// on a non-global object -var x = {}; -x.watch("p", function () { evalcx(''); }); -x.p = 0; - -// on the global -watch("e", (function () { evalcx(''); })); -e = function () {}; - -reportCompare(0, 0, "ok"); diff --git a/js/src/tests/js1_8_5/regress/regress-635195.js b/js/src/tests/js1_8_5/regress/regress-635195.js deleted file mode 100644 index 89980b05a..000000000 --- a/js/src/tests/js1_8_5/regress/regress-635195.js +++ /dev/null @@ -1,8 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -var obj = {set x(v) {}}; -obj.watch("x", function() { delete obj.x; }); -obj.x = "hi"; // don't assert - -reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/regress/regress-636394.js b/js/src/tests/js1_8_5/regress/regress-636394.js deleted file mode 100644 index d1a249786..000000000 --- a/js/src/tests/js1_8_5/regress/regress-636394.js +++ /dev/null @@ -1,10 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -var a = {p0: function () {}}; -var b = /f/; -b.__proto__ = a; -b.watch("p0", function () {}); -b.p0; - -reportCompare(0, 0, "ok"); diff --git a/js/src/vm/AsyncFunction.cpp b/js/src/vm/AsyncFunction.cpp index e14b77424..7132ed984 100644 --- a/js/src/vm/AsyncFunction.cpp +++ b/js/src/vm/AsyncFunction.cpp @@ -40,8 +40,11 @@ GlobalObject::initAsyncFunction(JSContext* cx, Handle<GlobalObject*> global) proto)); if (!asyncFunction) return false; - if (!LinkConstructorAndPrototype(cx, asyncFunction, asyncFunctionProto)) + if (!LinkConstructorAndPrototype(cx, asyncFunction, asyncFunctionProto, + JSPROP_PERMANENT | JSPROP_READONLY, JSPROP_READONLY)) + { return false; + } global->setReservedSlot(ASYNC_FUNCTION, ObjectValue(*asyncFunction)); global->setReservedSlot(ASYNC_FUNCTION_PROTO, ObjectValue(*asyncFunctionProto)); @@ -109,7 +112,7 @@ WrappedAsyncFunction(JSContext* cx, unsigned argc, Value* vp) JSObject* js::WrapAsyncFunctionWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto) { - MOZ_ASSERT(unwrapped->isStarGenerator()); + MOZ_ASSERT(unwrapped->isAsync()); MOZ_ASSERT(proto, "We need an explicit prototype to avoid the default" "%FunctionPrototype% fallback in NewFunctionWithProto()."); @@ -171,22 +174,14 @@ AsyncFunctionResume(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleV : cx->names().StarGeneratorThrow; FixedInvokeArgs<1> args(cx); args[0].set(valueOrReason); - RootedValue result(cx); - if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result)) - return AsyncFunctionThrown(cx, resultPromise); - - RootedObject resultObj(cx, &result.toObject()); - RootedValue doneVal(cx); RootedValue value(cx); - if (!GetProperty(cx, resultObj, resultObj, cx->names().done, &doneVal)) - return false; - if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value)) - return false; + if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &value)) + return AsyncFunctionThrown(cx, resultPromise); - if (doneVal.toBoolean()) - return AsyncFunctionReturned(cx, resultPromise, value); + if (generatorVal.toObject().as<GeneratorObject>().isAfterAwait()) + return AsyncFunctionAwait(cx, resultPromise, value); - return AsyncFunctionAwait(cx, resultPromise, value); + return AsyncFunctionReturned(cx, resultPromise, value); } // Async Functions proposal 2.2 steps 3-8. @@ -242,9 +237,3 @@ js::IsWrappedAsyncFunction(JSFunction* fun) { return fun->maybeNative() == WrappedAsyncFunction; } - -MOZ_MUST_USE bool -js::CheckAsyncResumptionValue(JSContext* cx, HandleValue v) -{ - return CheckStarGeneratorResumptionValue(cx, v); -} diff --git a/js/src/vm/AsyncFunction.h b/js/src/vm/AsyncFunction.h index d7f2c1311..de7c87d13 100644 --- a/js/src/vm/AsyncFunction.h +++ b/js/src/vm/AsyncFunction.h @@ -35,9 +35,6 @@ MOZ_MUST_USE bool AsyncFunctionAwaitedRejected(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue generatorVal, HandleValue reason); -MOZ_MUST_USE bool -CheckAsyncResumptionValue(JSContext* cx, HandleValue v); - } // namespace js #endif /* vm_AsyncFunction_h */ diff --git a/js/src/vm/AsyncIteration.cpp b/js/src/vm/AsyncIteration.cpp new file mode 100644 index 000000000..300179374 --- /dev/null +++ b/js/src/vm/AsyncIteration.cpp @@ -0,0 +1,644 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "vm/AsyncIteration.h" + +#include "jsarray.h" +#include "jscompartment.h" + +#include "builtin/Promise.h" +#include "vm/GeneratorObject.h" +#include "vm/GlobalObject.h" +#include "vm/Interpreter.h" +#include "vm/SelfHosting.h" + +#include "jscntxtinlines.h" +#include "jsobjinlines.h" + +#include "vm/NativeObject-inl.h" + +using namespace js; +using namespace js::gc; + +#define UNWRAPPED_ASYNC_WRAPPED_SLOT 1 +#define WRAPPED_ASYNC_UNWRAPPED_SLOT 0 + +// Async Iteration proposal 8.3.10 Runtime Semantics: EvaluateBody. +static bool +WrappedAsyncGenerator(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + RootedFunction wrapped(cx, &args.callee().as<JSFunction>()); + RootedValue unwrappedVal(cx, wrapped->getExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT)); + RootedFunction unwrapped(cx, &unwrappedVal.toObject().as<JSFunction>()); + RootedValue thisValue(cx, args.thisv()); + + // Step 1. + RootedValue generatorVal(cx); + InvokeArgs args2(cx); + if (!args2.init(cx, argc)) + return false; + for (size_t i = 0, len = argc; i < len; i++) + args2[i].set(args[i]); + if (!Call(cx, unwrappedVal, thisValue, args2, &generatorVal)) + return false; + + // Step 2. + Rooted<AsyncGeneratorObject*> asyncGenObj( + cx, AsyncGeneratorObject::create(cx, wrapped, generatorVal)); + if (!asyncGenObj) + return false; + + // Step 3 (skipped). + // Done in AsyncGeneratorObject::create and generator. + + // Step 4. + args.rval().setObject(*asyncGenObj); + return true; +} + +JSObject* +js::WrapAsyncGeneratorWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto) +{ + MOZ_ASSERT(unwrapped->isAsync()); + MOZ_ASSERT(proto, "We need an explicit prototype to avoid the default" + "%FunctionPrototype% fallback in NewFunctionWithProto()."); + + // Create a new function with AsyncGeneratorPrototype, reusing the name and + // the length of `unwrapped`. + + RootedAtom funName(cx, unwrapped->explicitName()); + uint16_t length; + if (!JSFunction::getLength(cx, unwrapped, &length)) + return nullptr; + + RootedFunction wrapped(cx, NewFunctionWithProto(cx, WrappedAsyncGenerator, length, + JSFunction::NATIVE_FUN, nullptr, + funName, proto, + AllocKind::FUNCTION_EXTENDED, + TenuredObject)); + if (!wrapped) + return nullptr; + + if (unwrapped->hasCompileTimeName()) + wrapped->setCompileTimeName(unwrapped->compileTimeName()); + + // Link them to each other to make GetWrappedAsyncGenerator and + // GetUnwrappedAsyncGenerator work. + unwrapped->setExtendedSlot(UNWRAPPED_ASYNC_WRAPPED_SLOT, ObjectValue(*wrapped)); + wrapped->setExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT, ObjectValue(*unwrapped)); + + return wrapped; +} + +JSObject* +js::WrapAsyncGenerator(JSContext* cx, HandleFunction unwrapped) +{ + RootedObject proto(cx, GlobalObject::getOrCreateAsyncGenerator(cx, cx->global())); + if (!proto) + return nullptr; + + return WrapAsyncGeneratorWithProto(cx, unwrapped, proto); +} + +bool +js::IsWrappedAsyncGenerator(JSFunction* fun) +{ + return fun->maybeNative() == WrappedAsyncGenerator; +} + +JSFunction* +js::GetWrappedAsyncGenerator(JSFunction* unwrapped) +{ + MOZ_ASSERT(unwrapped->isAsync()); + return &unwrapped->getExtendedSlot(UNWRAPPED_ASYNC_WRAPPED_SLOT).toObject().as<JSFunction>(); +} + +JSFunction* +js::GetUnwrappedAsyncGenerator(JSFunction* wrapped) +{ + MOZ_ASSERT(IsWrappedAsyncGenerator(wrapped)); + JSFunction* unwrapped = &wrapped->getExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT) + .toObject().as<JSFunction>(); + MOZ_ASSERT(unwrapped->isAsync()); + return unwrapped; +} + +// Async Iteration proposal 4.1.1 Await Fulfilled Functions. +MOZ_MUST_USE bool +js::AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue value) +{ + return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Normal, value); +} + +// Async Iteration proposal 4.1.2 Await Rejected Functions. +MOZ_MUST_USE bool +js::AsyncGeneratorAwaitedRejected(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue reason) +{ + return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason); +} + +// Async Iteration proposal 11.4.3.7 step 8.d-e. +MOZ_MUST_USE bool +js::AsyncGeneratorYieldReturnAwaitedFulfilled(JSContext* cx, + Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue value) +{ + return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Return, value); +} + +// Async Iteration proposal 11.4.3.7 step 8.d-e. +MOZ_MUST_USE bool +js::AsyncGeneratorYieldReturnAwaitedRejected(JSContext* cx, + Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue reason) +{ + return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason); +} + +const Class AsyncFromSyncIteratorObject::class_ = { + "AsyncFromSyncIteratorObject", + JSCLASS_HAS_RESERVED_SLOTS(AsyncFromSyncIteratorObject::Slots) +}; + +// Async Iteration proposal 11.1.3.1. +JSObject* +js::CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter) +{ + // Step 1 (implicit). + // Done in bytecode emitted by emitAsyncIterator. + + // Steps 2-4. + return AsyncFromSyncIteratorObject::create(cx, iter); +} + +// Async Iteration proposal 11.1.3.1 steps 2-4. +/* static */ JSObject* +AsyncFromSyncIteratorObject::create(JSContext* cx, HandleObject iter) +{ + // Step 2. + RootedObject proto(cx, GlobalObject::getOrCreateAsyncFromSyncIteratorPrototype(cx, + cx->global())); + if (!proto) + return nullptr; + + RootedObject obj(cx, NewNativeObjectWithGivenProto(cx, &class_, proto)); + if (!obj) + return nullptr; + + Handle<AsyncFromSyncIteratorObject*> asyncIter = obj.as<AsyncFromSyncIteratorObject>(); + + // Step 3. + asyncIter->setIterator(iter); + + // Step 4. + return asyncIter; +} + +// Async Iteration proposal 11.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next. +static bool +AsyncFromSyncIteratorNext(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Normal); +} + +// Async Iteration proposal 11.1.3.2.2 %AsyncFromSyncIteratorPrototype%.return. +static bool +AsyncFromSyncIteratorReturn(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Return); +} + +// Async Iteration proposal 11.1.3.2.3 %AsyncFromSyncIteratorPrototype%.throw. +static bool +AsyncFromSyncIteratorThrow(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Throw); +} + +// Async Iteration proposal 11.4.1.2 AsyncGenerator.prototype.next. +static bool +AsyncGeneratorNext(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // Steps 1-3. + return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Normal, args.get(0), + args.rval()); +} + +// Async Iteration proposal 11.4.1.3 AsyncGenerator.prototype.return. +static bool +AsyncGeneratorReturn(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // Steps 1-3. + return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Return, args.get(0), + args.rval()); +} + +// Async Iteration proposal 11.4.1.4 AsyncGenerator.prototype.throw. +static bool +AsyncGeneratorThrow(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // Steps 1-3. + return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Throw, args.get(0), + args.rval()); +} + +const Class AsyncGeneratorObject::class_ = { + "AsyncGenerator", + JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorObject::Slots) +}; + +// ES 2017 draft 9.1.13. +template <typename ProtoGetter> +static JSObject* +OrdinaryCreateFromConstructor(JSContext* cx, HandleFunction fun, + ProtoGetter protoGetter, const Class* clasp) +{ + // Step 1 (skipped). + + // Step 2. + RootedValue protoVal(cx); + if (!GetProperty(cx, fun, fun, cx->names().prototype, &protoVal)) + return nullptr; + + RootedObject proto(cx, protoVal.isObject() ? &protoVal.toObject() : nullptr); + if (!proto) { + proto = protoGetter(cx, cx->global()); + if (!proto) + return nullptr; + } + + // Step 3. + return NewNativeObjectWithGivenProto(cx, clasp, proto); +} + +/* static */ AsyncGeneratorObject* +AsyncGeneratorObject::create(JSContext* cx, HandleFunction asyncGen, HandleValue generatorVal) +{ + MOZ_ASSERT(generatorVal.isObject()); + MOZ_ASSERT(generatorVal.toObject().is<GeneratorObject>()); + + RootedObject obj( + cx, OrdinaryCreateFromConstructor(cx, asyncGen, + GlobalObject::getOrCreateAsyncGeneratorPrototype, + &class_)); + if (!obj) + return nullptr; + + Handle<AsyncGeneratorObject*> asyncGenObj = obj.as<AsyncGeneratorObject>(); + + // Async Iteration proposal 6.4.3.2 AsyncGeneratorStart. + // Step 6. + asyncGenObj->setGenerator(generatorVal); + + // Step 7. + asyncGenObj->setSuspendedStart(); + + // Step 8. + asyncGenObj->clearSingleQueueRequest(); + + return asyncGenObj; +} + +static MOZ_MUST_USE bool +InternalEnqueue(JSContext* cx, HandleArrayObject queue, HandleValue val) +{ + uint32_t length; + if (!GetLengthProperty(cx, queue, &length)) + return false; + + if (length >= MAX_ARRAY_INDEX) { + ReportOutOfMemory(cx); + return false; + } + + if (!DefineElement(cx, queue, length, val)) + return false; + return SetLengthProperty(cx, queue, length + 1); +} + +static MOZ_MUST_USE bool +InternalDequeue(JSContext* cx, HandleArrayObject queue, MutableHandleValue val) +{ + uint32_t length; + if (!GetLengthProperty(cx, queue, &length)) + return false; + + MOZ_ASSERT(length != 0, "Queue should not be empty here"); + + if (!GetElement(cx, queue, queue, 0, val)) + return false; + + uint32_t newlength = length - 1; + RootedValue tmp(cx); + for (uint32_t i = 0; i < newlength; i++) { + if (!GetElement(cx, queue, queue, i + 1, &tmp)) + return false; + if (!DefineElement(cx, queue, i, tmp)) + return false; + } + ObjectOpResult result; + if (!DeleteElement(cx, queue, newlength, result)) + return false; + if (!result) { + RootedId id(cx, INT_TO_JSID(newlength)); + return result.reportError(cx, queue, id); + } + return SetLengthProperty(cx, queue, newlength); +} + +/* static */ MOZ_MUST_USE bool +AsyncGeneratorObject::enqueueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + Handle<AsyncGeneratorRequest*> request) +{ + if (asyncGenObj->isSingleQueue()) { + if (asyncGenObj->isSingleQueueEmpty()) { + asyncGenObj->setSingleQueueRequest(request); + return true; + } + + RootedArrayObject queue(cx, NewDenseEmptyArray(cx)); + if (!queue) + return false; + + if (!NewbornArrayPush(cx, queue, ObjectValue(*asyncGenObj->singleQueueRequest()))) + return false; + if (!NewbornArrayPush(cx, queue, ObjectValue(*request))) + return false; + + asyncGenObj->setQueue(queue); + return true; + } + + RootedArrayObject queue(cx, asyncGenObj->queue()); + RootedValue requestVal(cx, ObjectValue(*request)); + return InternalEnqueue(cx, queue, requestVal); +} + +/* static */ AsyncGeneratorRequest* +AsyncGeneratorObject::dequeueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj) +{ + if (asyncGenObj->isSingleQueue()) { + AsyncGeneratorRequest* request = asyncGenObj->singleQueueRequest(); + asyncGenObj->clearSingleQueueRequest(); + return request; + } + + RootedArrayObject queue(cx, asyncGenObj->queue()); + RootedValue requestVal(cx); + if (!InternalDequeue(cx, queue, &requestVal)) + return nullptr; + + return &requestVal.toObject().as<AsyncGeneratorRequest>(); +} + +/* static */ AsyncGeneratorRequest* +AsyncGeneratorObject::peekRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj) +{ + if (asyncGenObj->isSingleQueue()) + return asyncGenObj->singleQueueRequest(); + + RootedArrayObject queue(cx, asyncGenObj->queue()); + + RootedValue requestVal(cx); + if (!GetElement(cx, queue, queue, 0, &requestVal)) + return nullptr; + + return &requestVal.toObject().as<AsyncGeneratorRequest>(); +} + +const Class AsyncGeneratorRequest::class_ = { + "AsyncGeneratorRequest", + JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorRequest::Slots) +}; + +// Async Iteration proposal 11.4.3.1. +/* static */ AsyncGeneratorRequest* +AsyncGeneratorRequest::create(JSContext* cx, CompletionKind completionKind_, + HandleValue completionValue_, HandleObject promise_) +{ + RootedObject obj(cx, NewNativeObjectWithGivenProto(cx, &class_, nullptr)); + if (!obj) + return nullptr; + + Handle<AsyncGeneratorRequest*> request = obj.as<AsyncGeneratorRequest>(); + request->setCompletionKind(completionKind_); + request->setCompletionValue(completionValue_); + request->setPromise(promise_); + return request; +} + +// Async Iteration proposal 11.4.3.2 AsyncGeneratorStart steps 5.d-g. +static MOZ_MUST_USE bool +AsyncGeneratorReturned(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue value) +{ + // Step 5.d. + asyncGenObj->setCompleted(); + + // Step 5.e (done in bytecode). + // Step 5.f.i (implicit). + + // Step 5.g. + return AsyncGeneratorResolve(cx, asyncGenObj, value, true); +} + +// Async Iteration proposal 11.4.3.2 AsyncGeneratorStart steps 5.d, f. +static MOZ_MUST_USE bool +AsyncGeneratorThrown(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj) +{ + // Step 5.d. + asyncGenObj->setCompleted(); + + // Not much we can do about uncatchable exceptions, so just bail. + if (!cx->isExceptionPending()) + return false; + + // Step 5.f.i. + RootedValue value(cx); + if (!GetAndClearException(cx, &value)) + return false; + + // Step 5.f.ii. + return AsyncGeneratorReject(cx, asyncGenObj, value); +} + +// Async Iteration proposal 11.4.3.7 (partially). +// Most steps are done in generator. +static MOZ_MUST_USE bool +AsyncGeneratorYield(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, HandleValue value) +{ + // Step 5 is done in bytecode. + + // Step 6. + asyncGenObj->setSuspendedYield(); + + // Step 9. + return AsyncGeneratorResolve(cx, asyncGenObj, value, false); +} + +// Async Iteration proposal 4.1 Await steps 2-9. +// Async Iteration proposal 8.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix. +// Async Iteration proposal 11.4.3.2 AsyncGeneratorStart step 5.f-g. +// Async Iteration proposal 11.4.3.5 AsyncGeneratorResumeNext +// steps 12-14, 16-20. +// Execution context switching is handled in generator. +MOZ_MUST_USE bool +js::AsyncGeneratorResume(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + CompletionKind completionKind, HandleValue argument) +{ + RootedValue generatorVal(cx, asyncGenObj->generatorVal()); + + // 11.4.3.5 steps 12-14, 16-20. + HandlePropertyName funName = completionKind == CompletionKind::Normal + ? cx->names().StarGeneratorNext + : completionKind == CompletionKind::Throw + ? cx->names().StarGeneratorThrow + : cx->names().StarGeneratorReturn; + FixedInvokeArgs<1> args(cx); + args[0].set(argument); + RootedValue result(cx); + if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result)) { + // 11.4.3.2 step 5.d, f. + return AsyncGeneratorThrown(cx, asyncGenObj); + } + + // 4.1 steps 2-9. + if (asyncGenObj->generatorObj()->isAfterAwait()) + return AsyncGeneratorAwait(cx, asyncGenObj, result); + + // The following code corresponds to the following 3 cases: + // * yield + // * yield* + // * return + // For yield and return, property access is done on an internal result + // object and it's not observable. + // For yield*, it's done on a possibly user-provided result object, and + // it's observable. + // + // Note that IteratorComplete steps in 8.2.1 are done in bytecode. + + // 8.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix. + RootedObject resultObj(cx, &result.toObject()); + RootedValue value(cx); + + if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value)) + return false; + + if (asyncGenObj->generatorObj()->isAfterYield()) + return AsyncGeneratorYield(cx, asyncGenObj, value); + + // 11.4.3.2 step 5.d-g. + return AsyncGeneratorReturned(cx, asyncGenObj, value); +} + +static const JSFunctionSpec async_iterator_proto_methods[] = { + JS_SELF_HOSTED_SYM_FN(asyncIterator, "AsyncIteratorIdentity", 0, 0), + JS_FS_END +}; + +static const JSFunctionSpec async_from_sync_iter_methods[] = { + JS_FN("next", AsyncFromSyncIteratorNext, 1, 0), + JS_FN("throw", AsyncFromSyncIteratorThrow, 1, 0), + JS_FN("return", AsyncFromSyncIteratorReturn, 1, 0), + JS_FS_END +}; + +static const JSFunctionSpec async_generator_methods[] = { + JS_FN("next", AsyncGeneratorNext, 1, 0), + JS_FN("throw", AsyncGeneratorThrow, 1, 0), + JS_FN("return", AsyncGeneratorReturn, 1, 0), + JS_FS_END +}; + +/* static */ MOZ_MUST_USE bool +GlobalObject::initAsyncGenerators(JSContext* cx, Handle<GlobalObject*> global) +{ + if (global->getReservedSlot(ASYNC_ITERATOR_PROTO).isObject()) + return true; + + // Async Iteration proposal 11.1.2 %AsyncIteratorPrototype%. + RootedObject asyncIterProto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global)); + if (!asyncIterProto) + return false; + if (!DefinePropertiesAndFunctions(cx, asyncIterProto, nullptr, async_iterator_proto_methods)) + return false; + + // Async Iteration proposal 11.1.3.2 %AsyncFromSyncIteratorPrototype%. + RootedObject asyncFromSyncIterProto( + cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_, + asyncIterProto)); + if (!asyncFromSyncIterProto) + return false; + if (!DefinePropertiesAndFunctions(cx, asyncFromSyncIterProto, nullptr, + async_from_sync_iter_methods) || + !DefineToStringTag(cx, asyncFromSyncIterProto, cx->names().AsyncFromSyncIterator)) + { + return false; + } + + // Async Iteration proposal 11.4.1 %AsyncGeneratorPrototype%. + RootedObject asyncGenProto( + cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_, + asyncIterProto)); + if (!asyncGenProto) + return false; + if (!DefinePropertiesAndFunctions(cx, asyncGenProto, nullptr, async_generator_methods) || + !DefineToStringTag(cx, asyncGenProto, cx->names().AsyncGenerator)) + { + return false; + } + + // Async Iteration proposal 11.3.3 %AsyncGenerator%. + RootedObject asyncGenerator(cx, NewSingletonObjectWithFunctionPrototype(cx, global)); + if (!asyncGenerator) + return false; + if (!JSObject::setDelegate(cx, asyncGenerator)) + return false; + if (!LinkConstructorAndPrototype(cx, asyncGenerator, asyncGenProto, JSPROP_READONLY, + JSPROP_READONLY) || + !DefineToStringTag(cx, asyncGenerator, cx->names().AsyncGeneratorFunction)) + { + return false; + } + + RootedValue function(cx, global->getConstructor(JSProto_Function)); + if (!function.toObjectOrNull()) + return false; + RootedObject proto(cx, &function.toObject()); + RootedAtom name(cx, cx->names().AsyncGeneratorFunction); + + // Async Iteration proposal 11.3.2 %AsyncGeneratorFunction%. + RootedObject asyncGenFunction( + cx, NewFunctionWithProto(cx, AsyncGeneratorConstructor, 1, JSFunction::NATIVE_CTOR, + nullptr, name, proto, gc::AllocKind::FUNCTION, SingletonObject)); + if (!asyncGenFunction) + return false; + if (!LinkConstructorAndPrototype(cx, asyncGenFunction, asyncGenerator, + JSPROP_PERMANENT | JSPROP_READONLY, JSPROP_READONLY)) + { + return false; + } + + global->setReservedSlot(ASYNC_ITERATOR_PROTO, ObjectValue(*asyncIterProto)); + global->setReservedSlot(ASYNC_FROM_SYNC_ITERATOR_PROTO, ObjectValue(*asyncFromSyncIterProto)); + global->setReservedSlot(ASYNC_GENERATOR, ObjectValue(*asyncGenerator)); + global->setReservedSlot(ASYNC_GENERATOR_FUNCTION, ObjectValue(*asyncGenFunction)); + global->setReservedSlot(ASYNC_GENERATOR_PROTO, ObjectValue(*asyncGenProto)); + return true; +} diff --git a/js/src/vm/AsyncIteration.h b/js/src/vm/AsyncIteration.h new file mode 100644 index 000000000..58c43131b --- /dev/null +++ b/js/src/vm/AsyncIteration.h @@ -0,0 +1,256 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef vm_AsyncIteration_h +#define vm_AsyncIteration_h + +#include "jscntxt.h" +#include "jsobj.h" + +#include "builtin/Promise.h" +#include "vm/GeneratorObject.h" + +namespace js { + +// Async generator consists of 2 functions, |wrapped| and |unwrapped|. +// |unwrapped| is a generator function compiled from async generator script, +// |await| behaves just like |yield| there. |unwrapped| isn't exposed to user +// script. +// |wrapped| is a native function that is the value of async generator. + +JSObject* +WrapAsyncGeneratorWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto); + +JSObject* +WrapAsyncGenerator(JSContext* cx, HandleFunction unwrapped); + +bool +IsWrappedAsyncGenerator(JSFunction* fun); + +JSFunction* +GetWrappedAsyncGenerator(JSFunction* unwrapped); + +JSFunction* +GetUnwrappedAsyncGenerator(JSFunction* wrapped); + +MOZ_MUST_USE bool +AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue value); +MOZ_MUST_USE bool +AsyncGeneratorAwaitedRejected(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue reason); +MOZ_MUST_USE bool +AsyncGeneratorYieldReturnAwaitedFulfilled(JSContext* cx, + Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue value); +MOZ_MUST_USE bool +AsyncGeneratorYieldReturnAwaitedRejected(JSContext* cx, + Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue reason); + +class AsyncGeneratorRequest : public NativeObject +{ + private: + enum AsyncGeneratorRequestSlots { + Slot_CompletionKind = 0, + Slot_CompletionValue, + Slot_Promise, + Slots, + }; + + void setCompletionKind(CompletionKind completionKind_) { + setFixedSlot(Slot_CompletionKind, + Int32Value(static_cast<int32_t>(completionKind_))); + } + void setCompletionValue(HandleValue completionValue_) { + setFixedSlot(Slot_CompletionValue, completionValue_); + } + void setPromise(HandleObject promise_) { + setFixedSlot(Slot_Promise, ObjectValue(*promise_)); + } + + public: + static const Class class_; + + static AsyncGeneratorRequest* + create(JSContext* cx, CompletionKind completionKind, HandleValue completionValue, + HandleObject promise); + + CompletionKind completionKind() const { + return static_cast<CompletionKind>(getFixedSlot(Slot_CompletionKind).toInt32()); + } + JS::Value completionValue() const { + return getFixedSlot(Slot_CompletionValue); + } + JSObject* promise() const { + return &getFixedSlot(Slot_Promise).toObject(); + } +}; + +class AsyncGeneratorObject : public NativeObject +{ + private: + enum AsyncGeneratorObjectSlots { + Slot_State = 0, + Slot_Generator, + Slot_QueueOrRequest, + Slots + }; + + enum State { + State_SuspendedStart, + State_SuspendedYield, + State_Executing, + // State_AwaitingYieldReturn corresponds to the case that + // AsyncGenerator#return is called while State_Executing, + // just like the case that AsyncGenerator#return is called + // while State_Completed. + State_AwaitingYieldReturn, + State_AwaitingReturn, + State_Completed + }; + + State state() const { + return static_cast<State>(getFixedSlot(Slot_State).toInt32()); + } + void setState(State state_) { + setFixedSlot(Slot_State, Int32Value(state_)); + } + + void setGenerator(const Value& value) { + setFixedSlot(Slot_Generator, value); + } + + // Queue is implemented in 2 ways. If only one request is queued ever, + // request is stored directly to the slot. Once 2 requests are queued, an + // array is created and requests are pushed into it, and the array is + // stored to the slot. + + bool isSingleQueue() const { + return getFixedSlot(Slot_QueueOrRequest).isNull() || + getFixedSlot(Slot_QueueOrRequest).toObject().is<AsyncGeneratorRequest>(); + } + bool isSingleQueueEmpty() const { + return getFixedSlot(Slot_QueueOrRequest).isNull(); + } + void setSingleQueueRequest(AsyncGeneratorRequest* request) { + setFixedSlot(Slot_QueueOrRequest, ObjectValue(*request)); + } + void clearSingleQueueRequest() { + setFixedSlot(Slot_QueueOrRequest, NullHandleValue); + } + AsyncGeneratorRequest* singleQueueRequest() const { + return &getFixedSlot(Slot_QueueOrRequest).toObject().as<AsyncGeneratorRequest>(); + } + + ArrayObject* queue() const { + return &getFixedSlot(Slot_QueueOrRequest).toObject().as<ArrayObject>(); + } + void setQueue(JSObject* queue_) { + setFixedSlot(Slot_QueueOrRequest, ObjectValue(*queue_)); + } + + public: + static const Class class_; + + static AsyncGeneratorObject* + create(JSContext* cx, HandleFunction asyncGen, HandleValue generatorVal); + + bool isSuspendedStart() const { + return state() == State_SuspendedStart; + } + bool isSuspendedYield() const { + return state() == State_SuspendedYield; + } + bool isExecuting() const { + return state() == State_Executing; + } + bool isAwaitingYieldReturn() const { + return state() == State_AwaitingYieldReturn; + } + bool isAwaitingReturn() const { + return state() == State_AwaitingReturn; + } + bool isCompleted() const { + return state() == State_Completed; + } + + void setSuspendedStart() { + setState(State_SuspendedStart); + } + void setSuspendedYield() { + setState(State_SuspendedYield); + } + void setExecuting() { + setState(State_Executing); + } + void setAwaitingYieldReturn() { + setState(State_AwaitingYieldReturn); + } + void setAwaitingReturn() { + setState(State_AwaitingReturn); + } + void setCompleted() { + setState(State_Completed); + } + + JS::Value generatorVal() const { + return getFixedSlot(Slot_Generator); + } + GeneratorObject* generatorObj() const { + return &getFixedSlot(Slot_Generator).toObject().as<GeneratorObject>(); + } + + static MOZ_MUST_USE bool + enqueueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + Handle<AsyncGeneratorRequest*> request); + + static AsyncGeneratorRequest* + dequeueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj); + + static AsyncGeneratorRequest* + peekRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj); + + bool isQueueEmpty() const { + if (isSingleQueue()) + return isSingleQueueEmpty(); + return queue()->length() == 0; + } +}; + +JSObject* +CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter); + +class AsyncFromSyncIteratorObject : public NativeObject +{ + private: + enum AsyncFromSyncIteratorObjectSlots { + Slot_Iterator = 0, + Slots + }; + + void setIterator(HandleObject iterator_) { + setFixedSlot(Slot_Iterator, ObjectValue(*iterator_)); + } + + public: + static const Class class_; + + static JSObject* + create(JSContext* cx, HandleObject iter); + + JSObject* iterator() const { + return &getFixedSlot(Slot_Iterator).toObject(); + } +}; + +MOZ_MUST_USE bool +AsyncGeneratorResume(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + CompletionKind completionKind, HandleValue argument); + +} // namespace js + +#endif /* vm_AsyncIteration_h */ diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index fd1c9f5e6..6a8afb56b 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -29,7 +29,10 @@ macro(ArrayValuesAt, ArrayValuesAt, "ArrayValuesAt") \ macro(as, as, "as") \ macro(Async, Async, "Async") \ + macro(AsyncFromSyncIterator, AsyncFromSyncIterator, "Async-from-Sync Iterator") \ macro(AsyncFunction, AsyncFunction, "AsyncFunction") \ + macro(AsyncGenerator, AsyncGenerator, "AsyncGenerator") \ + macro(AsyncGeneratorFunction, AsyncGeneratorFunction, "AsyncGeneratorFunction") \ macro(AsyncWrapped, AsyncWrapped, "AsyncWrapped") \ macro(async, async, "async") \ macro(await, await, "await") \ @@ -97,6 +100,7 @@ macro(displayURL, displayURL, "displayURL") \ macro(do, do_, "do") \ macro(done, done, "done") \ + macro(dotall, dotall, "dotall") \ macro(dotGenerator, dotGenerator, ".generator") \ macro(dotThis, dotThis, ".this") \ macro(each, each, "each") \ @@ -281,6 +285,7 @@ macro(RegExpFlagsGetter, RegExpFlagsGetter, "RegExpFlagsGetter") \ macro(RegExpMatcher, RegExpMatcher, "RegExpMatcher") \ macro(RegExpSearcher, RegExpSearcher, "RegExpSearcher") \ + macro(RegExpStringIterator, RegExpStringIterator, "RegExp String Iterator") \ macro(RegExpTester, RegExpTester, "RegExpTester") \ macro(RegExp_prototype_Exec, RegExp_prototype_Exec, "RegExp_prototype_Exec") \ macro(Reify, Reify, "Reify") \ @@ -309,6 +314,7 @@ macro(star, star, "*") \ macro(starDefaultStar, starDefaultStar, "*default*") \ macro(StarGeneratorNext, StarGeneratorNext, "StarGeneratorNext") \ + macro(StarGeneratorReturn, StarGeneratorReturn, "StarGeneratorReturn") \ macro(StarGeneratorThrow, StarGeneratorThrow, "StarGeneratorThrow") \ macro(startTimestamp, startTimestamp, "startTimestamp") \ macro(state, state, "state") \ diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index d68d1b75e..c49df5aa9 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -32,7 +32,6 @@ #include "js/Vector.h" #include "proxy/ScriptedProxyHandler.h" #include "vm/ArgumentsObject.h" -#include "vm/AsyncFunction.h" #include "vm/DebuggerMemory.h" #include "vm/GeneratorObject.h" #include "vm/SPSProfiler.h" @@ -1560,16 +1559,11 @@ CheckResumptionValue(JSContext* cx, AbstractFramePtr frame, const Maybe<HandleVa JSTrapStatus status, MutableHandleValue vp) { if (status == JSTRAP_RETURN && frame && frame.isFunctionFrame()) { - // Don't let a { return: ... } resumption value make a generator or - // async function violate the iterator protocol. The return value from + // Don't let a { return: ... } resumption value make a generator + // function violate the iterator protocol. The return value from // such a frame must have the form { done: <bool>, value: <anything> }. RootedFunction callee(cx, frame.callee()); - if (callee->isAsync()) { - if (!CheckAsyncResumptionValue(cx, vp)) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_AWAIT); - return false; - } - } else if (callee->isStarGenerator()) { + if (callee->isStarGenerator()) { if (!CheckStarGeneratorResumptionValue(cx, vp)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_YIELD); return false; @@ -7356,7 +7350,8 @@ DebuggerFrame::getEnvironment(JSContext* cx, HandleDebuggerFrame frame, /* static */ bool DebuggerFrame::getIsGenerator(HandleDebuggerFrame frame) { - return DebuggerFrame::getReferent(frame).script()->isGenerator(); + return DebuggerFrame::getReferent(frame).script()->isStarGenerator() || + DebuggerFrame::getReferent(frame).script()->isLegacyGenerator(); } /* static */ bool @@ -8733,7 +8728,6 @@ DebuggerObject::proxyHandlerGetter(JSContext* cx, unsigned argc, Value* vp) return true; } -#ifdef SPIDERMONKEY_PROMISE /* static */ bool DebuggerObject::isPromiseGetter(JSContext* cx, unsigned argc, Value* vp) { @@ -8902,7 +8896,6 @@ DebuggerObject::promiseDependentPromisesGetter(JSContext* cx, unsigned argc, Val args.rval().setObject(*promises); return true; } -#endif // SPIDERMONKEY_PROMISE /* static */ bool DebuggerObject::isExtensibleMethod(JSContext* cx, unsigned argc, Value* vp) @@ -9341,7 +9334,6 @@ const JSPropertySpec DebuggerObject::properties_[] = { JS_PS_END }; -#ifdef SPIDERMONKEY_PROMISE const JSPropertySpec DebuggerObject::promiseProperties_[] = { JS_PSG("isPromise", DebuggerObject::isPromiseGetter, 0), JS_PSG("promiseState", DebuggerObject::promiseStateGetter, 0), @@ -9355,7 +9347,6 @@ const JSPropertySpec DebuggerObject::promiseProperties_[] = { JS_PSG("promiseDependentPromises", DebuggerObject::promiseDependentPromisesGetter, 0), JS_PS_END }; -#endif // SPIDERMONKEY_PROMISE const JSFunctionSpec DebuggerObject::methods_[] = { JS_FN("isExtensible", DebuggerObject::isExtensibleMethod, 0, 0), @@ -9395,10 +9386,8 @@ DebuggerObject::initClass(JSContext* cx, HandleObject obj, HandleObject debugCto if (!objectProto) return nullptr; -#ifdef SPIDERMONKEY_PROMISE if (!DefinePropertiesAndFunctions(cx, objectProto, promiseProperties_, nullptr)) return nullptr; -#endif // SPIDERMONKEY_PROMISE return objectProto; } @@ -9466,7 +9455,6 @@ DebuggerObject::isScriptedProxy() const return js::IsScriptedProxy(referent()); } -#ifdef SPIDERMONKEY_PROMISE bool DebuggerObject::isPromise() const { @@ -9480,7 +9468,6 @@ DebuggerObject::isPromise() const return referent->is<PromiseObject>(); } -#endif // SPIDERMONKEY_PROMISE /* static */ bool DebuggerObject::getClassName(JSContext* cx, HandleDebuggerObject object, @@ -9763,7 +9750,6 @@ DebuggerObject::getErrorColumnNumber(JSContext* cx, HandleDebuggerObject object, return true; } -#ifdef SPIDERMONKEY_PROMISE /* static */ bool DebuggerObject::getPromiseValue(JSContext* cx, HandleDebuggerObject object, MutableHandleValue result) @@ -9783,7 +9769,6 @@ DebuggerObject::getPromiseReason(JSContext* cx, HandleDebuggerObject object, result.set(object->promise()->reason()); return object->owner()->wrapDebuggeeValue(cx, result); } -#endif // SPIDERMONKEY_PROMISE /* static */ bool DebuggerObject::isExtensible(JSContext* cx, HandleDebuggerObject object, bool& result) @@ -10253,7 +10238,6 @@ DebuggerObject::requireGlobal(JSContext* cx, HandleDebuggerObject object) return true; } -#ifdef SPIDERMONKEY_PROMISE /* static */ bool DebuggerObject::requirePromise(JSContext* cx, HandleDebuggerObject object) { @@ -10275,7 +10259,6 @@ DebuggerObject::requirePromise(JSContext* cx, HandleDebuggerObject object) return true; } -#endif // SPIDERMONKEY_PROMISE /* static */ bool DebuggerObject::getScriptedProxyTarget(JSContext* cx, HandleDebuggerObject object, diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h index cdcf2d67f..204d1ca3e 100644 --- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -1256,12 +1256,10 @@ class DebuggerObject : public NativeObject MutableHandleDebuggerObject result); static MOZ_MUST_USE bool getScriptedProxyHandler(JSContext* cx, HandleDebuggerObject object, MutableHandleDebuggerObject result); -#ifdef SPIDERMONKEY_PROMISE static MOZ_MUST_USE bool getPromiseValue(JSContext* cx, HandleDebuggerObject object, MutableHandleValue result); static MOZ_MUST_USE bool getPromiseReason(JSContext* cx, HandleDebuggerObject object, MutableHandleValue result); -#endif // SPIDERMONKEY_PROMISE // Methods static MOZ_MUST_USE bool isExtensible(JSContext* cx, HandleDebuggerObject object, @@ -1311,16 +1309,12 @@ class DebuggerObject : public NativeObject bool isArrowFunction() const; bool isGlobal() const; bool isScriptedProxy() const; -#ifdef SPIDERMONKEY_PROMISE bool isPromise() const; -#endif // SPIDERMONKEY_PROMISE JSAtom* name() const; JSAtom* displayName() const; -#ifdef SPIDERMONKEY_PROMISE JS::PromiseState promiseState() const; double promiseLifetime() const; double promiseTimeToResolution() const; -#endif // SPIDERMONKEY_PROMISE private: enum { @@ -1332,9 +1326,7 @@ class DebuggerObject : public NativeObject static const ClassOps classOps_; static const JSPropertySpec properties_[]; -#ifdef SPIDERMONKEY_PROMISE static const JSPropertySpec promiseProperties_[]; -#endif // SPIDERMONKEY_PROMISE static const JSFunctionSpec methods_[]; JSObject* referent() const { @@ -1345,14 +1337,10 @@ class DebuggerObject : public NativeObject Debugger* owner() const; -#ifdef SPIDERMONKEY_PROMISE PromiseObject* promise() const; -#endif // SPIDERMONKEY_PROMISE static MOZ_MUST_USE bool requireGlobal(JSContext* cx, HandleDebuggerObject object); -#ifdef SPIDERMONKEY_PROMISE static MOZ_MUST_USE bool requirePromise(JSContext* cx, HandleDebuggerObject object); -#endif // SPIDERMONKEY_PROMISE static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp); @@ -1379,7 +1367,6 @@ class DebuggerObject : public NativeObject static MOZ_MUST_USE bool isProxyGetter(JSContext* cx, unsigned argc, Value* vp); static MOZ_MUST_USE bool proxyTargetGetter(JSContext* cx, unsigned argc, Value* vp); static MOZ_MUST_USE bool proxyHandlerGetter(JSContext* cx, unsigned argc, Value* vp); -#ifdef SPIDERMONKEY_PROMISE static MOZ_MUST_USE bool isPromiseGetter(JSContext* cx, unsigned argc, Value* vp); static MOZ_MUST_USE bool promiseStateGetter(JSContext* cx, unsigned argc, Value* vp); static MOZ_MUST_USE bool promiseValueGetter(JSContext* cx, unsigned argc, Value* vp); @@ -1390,7 +1377,6 @@ class DebuggerObject : public NativeObject static MOZ_MUST_USE bool promiseResolutionSiteGetter(JSContext* cx, unsigned argc, Value* vp); static MOZ_MUST_USE bool promiseIDGetter(JSContext* cx, unsigned argc, Value* vp); static MOZ_MUST_USE bool promiseDependentPromisesGetter(JSContext* cx, unsigned argc, Value* vp); -#endif // SPIDERMONKEY_PROMISE // JSNative methods static MOZ_MUST_USE bool isExtensibleMethod(JSContext* cx, unsigned argc, Value* vp); diff --git a/js/src/vm/EnvironmentObject.cpp b/js/src/vm/EnvironmentObject.cpp index a5aac2ab4..7834940f1 100644 --- a/js/src/vm/EnvironmentObject.cpp +++ b/js/src/vm/EnvironmentObject.cpp @@ -408,7 +408,6 @@ const ObjectOps ModuleEnvironmentObject::objectOps_ = { ModuleEnvironmentObject::setProperty, ModuleEnvironmentObject::getOwnPropertyDescriptor, ModuleEnvironmentObject::deleteProperty, - nullptr, nullptr, /* watch/unwatch */ nullptr, /* getElements */ ModuleEnvironmentObject::enumerate, nullptr @@ -491,7 +490,7 @@ ModuleEnvironmentObject::createImportBinding(JSContext* cx, HandleAtom importNam { RootedId importNameId(cx, AtomToId(importName)); RootedId localNameId(cx, AtomToId(localName)); - RootedModuleEnvironmentObject env(cx, module->environment()); + RootedModuleEnvironmentObject env(cx, &module->initialEnvironment()); if (!importBindings().putNew(cx, importNameId, env, localNameId)) { ReportOutOfMemory(cx); return false; @@ -790,7 +789,6 @@ static const ObjectOps WithEnvironmentObjectOps = { with_SetProperty, with_GetOwnPropertyDescriptor, with_DeleteProperty, - nullptr, nullptr, /* watch/unwatch */ nullptr, /* getElements */ nullptr, /* enumerate (native enumeration of target doesn't work) */ nullptr, @@ -1159,7 +1157,6 @@ static const ObjectOps RuntimeLexicalErrorObjectObjectOps = { lexicalError_SetProperty, lexicalError_GetOwnPropertyDescriptor, lexicalError_DeleteProperty, - nullptr, nullptr, /* watch/unwatch */ nullptr, /* getElements */ nullptr, /* enumerate (native enumeration of target doesn't work) */ nullptr, /* this */ @@ -2451,7 +2448,9 @@ DebugEnvironments::addDebugEnvironment(JSContext* cx, const EnvironmentIter& ei, MOZ_ASSERT(cx->compartment() == debugEnv->compartment()); // Generators should always have environments. MOZ_ASSERT_IF(ei.scope().is<FunctionScope>(), - !ei.scope().as<FunctionScope>().canonicalFunction()->isGenerator()); + !ei.scope().as<FunctionScope>().canonicalFunction()->isStarGenerator() && + !ei.scope().as<FunctionScope>().canonicalFunction()->isLegacyGenerator() && + !ei.scope().as<FunctionScope>().canonicalFunction()->isAsync()); if (!CanUseDebugEnvironmentMaps(cx)) return true; @@ -2597,8 +2596,11 @@ DebugEnvironments::onPopCall(JSContext* cx, AbstractFramePtr frame) if (!frame.environmentChain()->is<CallObject>()) return; - if (frame.callee()->isGenerator()) + if (frame.callee()->isStarGenerator() || frame.callee()->isLegacyGenerator() || + frame.callee()->isAsync()) + { return; + } CallObject& callobj = frame.environmentChain()->as<CallObject>(); envs->liveEnvs.remove(&callobj); @@ -2729,8 +2731,13 @@ DebugEnvironments::updateLiveEnvironments(JSContext* cx) if (frame.environmentChain()->compartment() != cx->compartment()) continue; - if (frame.isFunctionFrame() && frame.callee()->isGenerator()) - continue; + if (frame.isFunctionFrame()) { + if (frame.callee()->isStarGenerator() || frame.callee()->isLegacyGenerator() || + frame.callee()->isAsync()) + { + continue; + } + } if (!frame.isDebuggee()) continue; @@ -2885,7 +2892,8 @@ GetDebugEnvironmentForMissing(JSContext* cx, const EnvironmentIter& ei) if (ei.scope().is<FunctionScope>()) { RootedFunction callee(cx, ei.scope().as<FunctionScope>().canonicalFunction()); // Generators should always reify their scopes. - MOZ_ASSERT(!callee->isGenerator()); + MOZ_ASSERT(!callee->isStarGenerator() && !callee->isLegacyGenerator() && + !callee->isAsync()); JS::ExposeObjectToActiveJS(callee); Rooted<CallObject*> callobj(cx, CallObject::createHollowForDebug(cx, callee)); diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp index ba28501e6..2f5c63c28 100644 --- a/js/src/vm/GeneratorObject.cpp +++ b/js/src/vm/GeneratorObject.cpp @@ -19,12 +19,13 @@ using namespace js; JSObject* GeneratorObject::create(JSContext* cx, AbstractFramePtr frame) { - MOZ_ASSERT(frame.script()->isGenerator()); + MOZ_ASSERT(frame.script()->isStarGenerator() || frame.script()->isLegacyGenerator() || + frame.script()->isAsync()); MOZ_ASSERT(frame.script()->nfixed() == 0); Rooted<GlobalObject*> global(cx, cx->global()); RootedNativeObject obj(cx); - if (frame.script()->isStarGenerator()) { + if (frame.script()->isStarGenerator() || frame.script()->isAsync()) { RootedValue pval(cx); RootedObject fun(cx, frame.callee()); // FIXME: This would be faster if we could avoid doing a lookup to get @@ -63,10 +64,14 @@ bool GeneratorObject::suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame, jsbytecode* pc, Value* vp, unsigned nvalues) { - MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD); + MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD || *pc == JSOP_AWAIT); Rooted<GeneratorObject*> genObj(cx, &obj->as<GeneratorObject>()); MOZ_ASSERT(!genObj->hasExpressionStack()); + MOZ_ASSERT_IF(*pc == JSOP_AWAIT, genObj->callee().isAsync()); + MOZ_ASSERT_IF(*pc == JSOP_YIELD, + genObj->callee().isStarGenerator() || + genObj->callee().isLegacyGenerator()); if (*pc == JSOP_YIELD && genObj->isClosing() && genObj->is<LegacyGeneratorObject>()) { RootedValue val(cx, ObjectValue(*frame.callee())); @@ -74,8 +79,8 @@ GeneratorObject::suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame return false; } - uint32_t yieldIndex = GET_UINT24(pc); - genObj->setYieldIndex(yieldIndex); + uint32_t yieldAndAwaitIndex = GET_UINT24(pc); + genObj->setYieldAndAwaitIndex(yieldAndAwaitIndex); genObj->setEnvironmentChain(*frame.environmentChain()); if (nvalues) { @@ -172,7 +177,7 @@ GeneratorObject::resume(JSContext* cx, InterpreterActivation& activation, } JSScript* script = callee->nonLazyScript(); - uint32_t offset = script->yieldOffsets()[genObj->yieldIndex()]; + uint32_t offset = script->yieldAndAwaitOffsets()[genObj->yieldAndAwaitIndex()]; activation.regs().pc = script->offsetToPC(offset); // Always push on a value, even if we are raising an exception. In the @@ -311,7 +316,8 @@ GlobalObject::initStarGenerators(JSContext* cx, Handle<GlobalObject*> global) RootedObject genFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global)); if (!genFunctionProto || !JSObject::setDelegate(cx, genFunctionProto)) return false; - if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto) || + if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto, JSPROP_READONLY, + JSPROP_READONLY) || !DefineToStringTag(cx, genFunctionProto, cx->names().GeneratorFunction)) { return false; @@ -328,8 +334,11 @@ GlobalObject::initStarGenerators(JSContext* cx, Handle<GlobalObject*> global) SingletonObject)); if (!genFunction) return false; - if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto)) + if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto, + JSPROP_PERMANENT | JSPROP_READONLY, JSPROP_READONLY)) + { return false; + } global->setReservedSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto)); global->setReservedSlot(STAR_GENERATOR_FUNCTION, ObjectValue(*genFunction)); @@ -365,3 +374,34 @@ js::CheckStarGeneratorResumptionValue(JSContext* cx, HandleValue v) return true; } + +bool +GeneratorObject::isAfterYield() +{ + return isAfterYieldOrAwait(JSOP_YIELD); +} + +bool +GeneratorObject::isAfterAwait() +{ + return isAfterYieldOrAwait(JSOP_AWAIT); +} + +bool +GeneratorObject::isAfterYieldOrAwait(JSOp op) +{ + if (isClosed() || isClosing() || isRunning()) + return false; + + JSScript* script = callee().nonLazyScript(); + jsbytecode* code = script->code(); + uint32_t nextOffset = script->yieldAndAwaitOffsets()[yieldAndAwaitIndex()]; + if (code[nextOffset] != JSOP_DEBUGAFTERYIELD) + return false; + + uint32_t offset = nextOffset - JSOP_YIELD_LENGTH; + MOZ_ASSERT(code[offset] == JSOP_INITIALYIELD || code[offset] == JSOP_YIELD || + code[offset] == JSOP_AWAIT); + + return code[offset] == op; +} diff --git a/js/src/vm/GeneratorObject.h b/js/src/vm/GeneratorObject.h index ca1452b34..f19ca2aac 100644 --- a/js/src/vm/GeneratorObject.h +++ b/js/src/vm/GeneratorObject.h @@ -21,15 +21,15 @@ class GeneratorObject : public NativeObject public: // Magic values stored in the yield index slot when the generator is // running or closing. See the yield index comment below. - static const int32_t YIELD_INDEX_RUNNING = INT32_MAX; - static const int32_t YIELD_INDEX_CLOSING = INT32_MAX - 1; + static const int32_t YIELD_AND_AWAIT_INDEX_RUNNING = INT32_MAX; + static const int32_t YIELD_AND_AWAIT_INDEX_CLOSING = INT32_MAX - 1; enum { CALLEE_SLOT = 0, ENV_CHAIN_SLOT, ARGS_OBJ_SLOT, EXPRESSION_STACK_SLOT, - YIELD_INDEX_SLOT, + YIELD_AND_AWAIT_INDEX_SLOT, NEWTARGET_SLOT, RESERVED_SLOTS }; @@ -124,47 +124,48 @@ class GeneratorObject : public NativeObject // The yield index slot is abused for a few purposes. It's undefined if // it hasn't been set yet (before the initial yield), and null if the // generator is closed. If the generator is running, the yield index is - // YIELD_INDEX_RUNNING. If the generator is in that bizarre "closing" - // state, the yield index is YIELD_INDEX_CLOSING. + // YIELD_AND_AWAIT_INDEX_RUNNING. If the generator is in that bizarre + // "closing" state, the yield index is YIELD_AND_AWAIT_INDEX_CLOSING. // // If the generator is suspended, it's the yield index (stored as - // JSOP_INITIALYIELD/JSOP_YIELD operand) of the yield instruction that - // suspended the generator. The yield index can be mapped to the bytecode - // offset (interpreter) or to the native code offset (JIT). + // JSOP_INITIALYIELD/JSOP_YIELD/JSOP_AWAIT operand) of the yield + // instruction that suspended the generator. The yield index can be mapped + // to the bytecode offset (interpreter) or to the native code offset (JIT). bool isRunning() const { MOZ_ASSERT(!isClosed()); - return getFixedSlot(YIELD_INDEX_SLOT).toInt32() == YIELD_INDEX_RUNNING; + return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() == YIELD_AND_AWAIT_INDEX_RUNNING; } bool isClosing() const { - return getFixedSlot(YIELD_INDEX_SLOT).toInt32() == YIELD_INDEX_CLOSING; + return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() == YIELD_AND_AWAIT_INDEX_CLOSING; } bool isSuspended() const { // Note: also update Baseline's IsSuspendedStarGenerator code if this // changes. MOZ_ASSERT(!isClosed()); - static_assert(YIELD_INDEX_CLOSING < YIELD_INDEX_RUNNING, - "test below should return false for YIELD_INDEX_RUNNING"); - return getFixedSlot(YIELD_INDEX_SLOT).toInt32() < YIELD_INDEX_CLOSING; + static_assert(YIELD_AND_AWAIT_INDEX_CLOSING < YIELD_AND_AWAIT_INDEX_RUNNING, + "test below should return false for YIELD_AND_AWAIT_INDEX_RUNNING"); + return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() < YIELD_AND_AWAIT_INDEX_CLOSING; } void setRunning() { MOZ_ASSERT(isSuspended()); - setFixedSlot(YIELD_INDEX_SLOT, Int32Value(YIELD_INDEX_RUNNING)); + setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(YIELD_AND_AWAIT_INDEX_RUNNING)); } void setClosing() { MOZ_ASSERT(isSuspended()); - setFixedSlot(YIELD_INDEX_SLOT, Int32Value(YIELD_INDEX_CLOSING)); - } - void setYieldIndex(uint32_t yieldIndex) { - MOZ_ASSERT_IF(yieldIndex == 0, getFixedSlot(YIELD_INDEX_SLOT).isUndefined()); - MOZ_ASSERT_IF(yieldIndex != 0, isRunning() || isClosing()); - MOZ_ASSERT(yieldIndex < uint32_t(YIELD_INDEX_CLOSING)); - setFixedSlot(YIELD_INDEX_SLOT, Int32Value(yieldIndex)); + setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(YIELD_AND_AWAIT_INDEX_CLOSING)); + } + void setYieldAndAwaitIndex(uint32_t yieldAndAwaitIndex) { + MOZ_ASSERT_IF(yieldAndAwaitIndex == 0, + getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).isUndefined()); + MOZ_ASSERT_IF(yieldAndAwaitIndex != 0, isRunning() || isClosing()); + MOZ_ASSERT(yieldAndAwaitIndex < uint32_t(YIELD_AND_AWAIT_INDEX_CLOSING)); + setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(yieldAndAwaitIndex)); MOZ_ASSERT(isSuspended()); } - uint32_t yieldIndex() const { + uint32_t yieldAndAwaitIndex() const { MOZ_ASSERT(isSuspended()); - return getFixedSlot(YIELD_INDEX_SLOT).toInt32(); + return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32(); } bool isClosed() const { return getFixedSlot(CALLEE_SLOT).isNull(); @@ -174,10 +175,17 @@ class GeneratorObject : public NativeObject setFixedSlot(ENV_CHAIN_SLOT, NullValue()); setFixedSlot(ARGS_OBJ_SLOT, NullValue()); setFixedSlot(EXPRESSION_STACK_SLOT, NullValue()); - setFixedSlot(YIELD_INDEX_SLOT, NullValue()); + setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, NullValue()); setFixedSlot(NEWTARGET_SLOT, NullValue()); } + bool isAfterYield(); + bool isAfterAwait(); + +private: + bool isAfterYieldOrAwait(JSOp op); + +public: static size_t offsetOfCalleeSlot() { return getFixedSlotOffset(CALLEE_SLOT); } @@ -187,8 +195,8 @@ class GeneratorObject : public NativeObject static size_t offsetOfArgsObjSlot() { return getFixedSlotOffset(ARGS_OBJ_SLOT); } - static size_t offsetOfYieldIndexSlot() { - return getFixedSlotOffset(YIELD_INDEX_SLOT); + static size_t offsetOfYieldAndAwaitIndexSlot() { + return getFixedSlotOffset(YIELD_AND_AWAIT_INDEX_SLOT); } static size_t offsetOfExpressionStackSlot() { return getFixedSlotOffset(EXPRESSION_STACK_SLOT); diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index 85707e1c6..a8d401af4 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -21,9 +21,7 @@ #include "builtin/MapObject.h" #include "builtin/ModuleObject.h" #include "builtin/Object.h" -#ifdef SPIDERMONKEY_PROMISE #include "builtin/Promise.h" -#endif #include "builtin/RegExp.h" #include "builtin/SelfHostingDefines.h" #include "builtin/SymbolObject.h" @@ -468,62 +466,29 @@ GlobalObject::initSelfHostingBuiltins(JSContext* cx, Handle<GlobalObject*> globa return false; } - RootedValue std_isConcatSpreadable(cx); - std_isConcatSpreadable.setSymbol(cx->wellKnownSymbols().get(JS::SymbolCode::isConcatSpreadable)); - if (!JS_DefineProperty(cx, global, "std_isConcatSpreadable", std_isConcatSpreadable, - JSPROP_PERMANENT | JSPROP_READONLY)) - { - return false; - } - - // Define a top-level property 'std_iterator' with the name of the method - // used by for-of loops to create an iterator. - RootedValue std_iterator(cx); - std_iterator.setSymbol(cx->wellKnownSymbols().get(JS::SymbolCode::iterator)); - if (!JS_DefineProperty(cx, global, "std_iterator", std_iterator, - JSPROP_PERMANENT | JSPROP_READONLY)) - { - return false; - } - - RootedValue std_match(cx); - std_match.setSymbol(cx->wellKnownSymbols().get(JS::SymbolCode::match)); - if (!JS_DefineProperty(cx, global, "std_match", std_match, - JSPROP_PERMANENT | JSPROP_READONLY)) - { - return false; - } - - RootedValue std_replace(cx); - std_replace.setSymbol(cx->wellKnownSymbols().get(JS::SymbolCode::replace)); - if (!JS_DefineProperty(cx, global, "std_replace", std_replace, - JSPROP_PERMANENT | JSPROP_READONLY)) - { - return false; - } - - RootedValue std_search(cx); - std_search.setSymbol(cx->wellKnownSymbols().get(JS::SymbolCode::search)); - if (!JS_DefineProperty(cx, global, "std_search", std_search, - JSPROP_PERMANENT | JSPROP_READONLY)) - { - return false; - } - - RootedValue std_species(cx); - std_species.setSymbol(cx->wellKnownSymbols().get(JS::SymbolCode::species)); - if (!JS_DefineProperty(cx, global, "std_species", std_species, - JSPROP_PERMANENT | JSPROP_READONLY)) - { - return false; - } - - RootedValue std_split(cx); - std_split.setSymbol(cx->wellKnownSymbols().get(JS::SymbolCode::split)); - if (!JS_DefineProperty(cx, global, "std_split", std_split, - JSPROP_PERMANENT | JSPROP_READONLY)) - { - return false; + struct SymbolAndName { + JS::SymbolCode code; + const char* name; + }; + + SymbolAndName wellKnownSymbols[] = { + {JS::SymbolCode::isConcatSpreadable, "std_isConcatSpreadable"}, + {JS::SymbolCode::iterator, "std_iterator"}, + {JS::SymbolCode::match, "std_match"}, + {JS::SymbolCode::matchAll, "std_matchAll"}, + {JS::SymbolCode::replace, "std_replace"}, + {JS::SymbolCode::search, "std_search"}, + {JS::SymbolCode::species, "std_species"}, + {JS::SymbolCode::split, "std_split"}, + }; + + RootedValue symVal(cx); + for (const auto& sym : wellKnownSymbols) { + symVal.setSymbol(cx->wellKnownSymbols().get(sym.code)); + if (!JS_DefineProperty(cx, global, sym.name, symVal, + JSPROP_PERMANENT | JSPROP_READONLY)) { + return false; + } } return InitBareBuiltinCtor(cx, global, JSProto_Array) && @@ -619,17 +584,18 @@ GlobalObject::createBlankPrototypeInheriting(JSContext* cx, Handle<GlobalObject* } bool -js::LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor_, JSObject* proto_) +js::LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor_, JSObject* proto_, + unsigned prototypeAttrs, unsigned constructorAttrs) { RootedObject ctor(cx, ctor_), proto(cx, proto_); RootedValue protoVal(cx, ObjectValue(*proto)); RootedValue ctorVal(cx, ObjectValue(*ctor)); - return DefineProperty(cx, ctor, cx->names().prototype, protoVal, - nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY) && - DefineProperty(cx, proto, cx->names().constructor, ctorVal, - nullptr, nullptr, 0); + return DefineProperty(cx, ctor, cx->names().prototype, protoVal, nullptr, nullptr, + prototypeAttrs) && + DefineProperty(cx, proto, cx->names().constructor, ctorVal, nullptr, nullptr, + constructorAttrs); } bool diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index 5aacfc5dc..3fd2762f8 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -93,12 +93,18 @@ class GlobalObject : public NativeObject ITERATOR_PROTO, ARRAY_ITERATOR_PROTO, STRING_ITERATOR_PROTO, + REGEXP_STRING_ITERATOR_PROTO, LEGACY_GENERATOR_OBJECT_PROTO, STAR_GENERATOR_OBJECT_PROTO, STAR_GENERATOR_FUNCTION_PROTO, STAR_GENERATOR_FUNCTION, ASYNC_FUNCTION_PROTO, ASYNC_FUNCTION, + ASYNC_ITERATOR_PROTO, + ASYNC_FROM_SYNC_ITERATOR_PROTO, + ASYNC_GENERATOR, + ASYNC_GENERATOR_FUNCTION, + ASYNC_GENERATOR_PROTO, MAP_ITERATOR_PROTO, SET_ITERATOR_PROTO, COLLATOR_PROTO, @@ -583,6 +589,12 @@ class GlobalObject : public NativeObject } static NativeObject* + getOrCreateRegExpStringIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) { + return MaybeNativeObject(getOrCreateObject(cx, global, REGEXP_STRING_ITERATOR_PROTO, + initRegExpStringIteratorProto)); + } + + static NativeObject* getOrCreateLegacyGeneratorObjectPrototype(JSContext* cx, Handle<GlobalObject*> global) { return MaybeNativeObject(getOrCreateObject(cx, global, LEGACY_GENERATOR_OBJECT_PROTO, initLegacyGeneratorProto)); @@ -617,6 +629,36 @@ class GlobalObject : public NativeObject return getOrCreateObject(cx, global, ASYNC_FUNCTION, initAsyncFunction); } + static NativeObject* + getOrCreateAsyncIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) + { + return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_ITERATOR_PROTO, + initAsyncGenerators)); + } + + static NativeObject* + getOrCreateAsyncFromSyncIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) { + return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_FROM_SYNC_ITERATOR_PROTO, + initAsyncGenerators)); + } + + static NativeObject* + getOrCreateAsyncGenerator(JSContext* cx, Handle<GlobalObject*> global) { + return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_GENERATOR, + initAsyncGenerators)); + } + + static JSObject* + getOrCreateAsyncGeneratorFunction(JSContext* cx, Handle<GlobalObject*> global) { + return getOrCreateObject(cx, global, ASYNC_GENERATOR_FUNCTION, initAsyncGenerators); + } + + static NativeObject* + getOrCreateAsyncGeneratorPrototype(JSContext* cx, Handle<GlobalObject*> global) { + return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_GENERATOR_PROTO, + initAsyncGenerators)); + } + static JSObject* getOrCreateMapIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) { return getOrCreateObject(cx, global, MAP_ITERATOR_PROTO, initMapIteratorProto); @@ -767,6 +809,7 @@ class GlobalObject : public NativeObject static bool initIteratorProto(JSContext* cx, Handle<GlobalObject*> global); static bool initArrayIteratorProto(JSContext* cx, Handle<GlobalObject*> global); static bool initStringIteratorProto(JSContext* cx, Handle<GlobalObject*> global); + static bool initRegExpStringIteratorProto(JSContext* cx, Handle<GlobalObject*> global); // Implemented in vm/GeneratorObject.cpp. static bool initLegacyGeneratorProto(JSContext* cx, Handle<GlobalObject*> global); @@ -774,6 +817,8 @@ class GlobalObject : public NativeObject static bool initAsyncFunction(JSContext* cx, Handle<GlobalObject*> global); + static bool initAsyncGenerators(JSContext* cx, Handle<GlobalObject*> global); + // Implemented in builtin/MapObject.cpp. static bool initMapIteratorProto(JSContext* cx, Handle<GlobalObject*> global); static bool initSetIteratorProto(JSContext* cx, Handle<GlobalObject*> global); @@ -977,12 +1022,14 @@ GlobalObject::createArrayFromBuffer<uint8_clamped>() const } /* - * Define ctor.prototype = proto as non-enumerable, non-configurable, and - * non-writable; define proto.constructor = ctor as non-enumerable but - * configurable and writable. + * Unless otherwise specified, define ctor.prototype = proto as non-enumerable, + * non-configurable, and non-writable; and define proto.constructor = ctor as + * non-enumerable but configurable and writable. */ extern bool -LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor, JSObject* proto); +LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor, JSObject* proto, + unsigned prototypeAttrs = JSPROP_PERMANENT | JSPROP_READONLY, + unsigned constructorAttrs = 0); /* * Define properties and/or functions on any object. Either ps or fs, or both, diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 030f0f3b6..3cf9b57f6 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -39,6 +39,7 @@ #include "jit/Ion.h" #include "jit/IonAnalysis.h" #include "vm/AsyncFunction.h" +#include "vm/AsyncIteration.h" #include "vm/Debugger.h" #include "vm/GeneratorObject.h" #include "vm/Opcodes.h" @@ -1939,9 +1940,6 @@ CASE(EnableInterruptsPseudoOpcode) CASE(JSOP_NOP) CASE(JSOP_NOP_DESTRUCTURING) CASE(JSOP_UNUSED126) -CASE(JSOP_UNUSED192) -CASE(JSOP_UNUSED209) -CASE(JSOP_UNUSED210) CASE(JSOP_UNUSED211) CASE(JSOP_TRY_DESTRUCTURING_ITERCLOSE) CASE(JSOP_UNUSED221) @@ -3585,6 +3583,29 @@ CASE(JSOP_TOASYNC) } END_CASE(JSOP_TOASYNC) +CASE(JSOP_TOASYNCGEN) +{ + ReservedRooted<JSFunction*> unwrapped(&rootFunction0, + ®S.sp[-1].toObject().as<JSFunction>()); + JSObject* wrapped = WrapAsyncGenerator(cx, unwrapped); + if (!wrapped) + goto error; + + REGS.sp[-1].setObject(*wrapped); +} +END_CASE(JSOP_TOASYNCGEN) + +CASE(JSOP_TOASYNCITER) +{ + ReservedRooted<JSObject*> iter(&rootObject1, ®S.sp[-1].toObject()); + JSObject* asyncIter = CreateAsyncFromSyncIterator(cx, iter); + if (!asyncIter) + goto error; + + REGS.sp[-1].setObject(*asyncIter); +} +END_CASE(JSOP_TOASYNCITER) + CASE(JSOP_SETFUNNAME) { MOZ_ASSERT(REGS.stackDepth() >= 2); @@ -3994,6 +4015,7 @@ CASE(JSOP_INITIALYIELD) } CASE(JSOP_YIELD) +CASE(JSOP_AWAIT) { MOZ_ASSERT(!cx->isExceptionPending()); MOZ_ASSERT(REGS.fp()->isFunctionFrame()); @@ -5126,6 +5148,10 @@ js::ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind) case CheckIsObjectKind::GetIterator: JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_GET_ITER_RETURNED_PRIMITIVE); break; + case CheckIsObjectKind::GetAsyncIterator: + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE); + break; default: MOZ_CRASH("Unknown kind"); } diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h index 9fefd75cc..5e7087f7f 100644 --- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -561,7 +561,8 @@ enum class CheckIsObjectKind : uint8_t { IteratorNext, IteratorReturn, IteratorThrow, - GetIterator + GetIterator, + GetAsyncIterator }; bool diff --git a/js/src/vm/NativeObject-inl.h b/js/src/vm/NativeObject-inl.h index e55e3db04..030d92c12 100644 --- a/js/src/vm/NativeObject-inl.h +++ b/js/src/vm/NativeObject-inl.h @@ -158,11 +158,11 @@ NativeObject::extendDenseElements(ExclusiveContext* cx, MOZ_ASSERT(!denseElementsAreFrozen()); /* - * Don't grow elements for non-extensible objects or watched objects. Dense - * elements can be added/written with no extensible or watchpoint checks as - * long as there is capacity for them. + * Don't grow elements for non-extensible objects. Dense elements can be + * added/written with no extensible checks as long as there is capacity + * for them. */ - if (!nonProxyIsExtensible() || watched()) { + if (!nonProxyIsExtensible()) { MOZ_ASSERT(getDenseCapacity() == 0); return DenseElementResult::Incomplete; } diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index da0f59fe2..d801fad06 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -9,8 +9,6 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/Casting.h" -#include "jswatchpoint.h" - #include "gc/Marking.h" #include "js/Value.h" #include "vm/Debugger.h" @@ -602,7 +600,7 @@ NativeObject::maybeDensifySparseElements(js::ExclusiveContext* cx, HandleNativeO return DenseElementResult::Incomplete; /* Watch for conditions under which an object's elements cannot be dense. */ - if (!obj->nonProxyIsExtensible() || obj->watched()) + if (!obj->nonProxyIsExtensible()) return DenseElementResult::Incomplete; /* @@ -2410,17 +2408,9 @@ SetExistingProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleVa } bool -js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue value, +js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue v, HandleValue receiver, QualifiedBool qualified, ObjectOpResult& result) { - // Fire watchpoints, if any. - RootedValue v(cx, value); - if (MOZ_UNLIKELY(obj->watched())) { - WatchpointMap* wpmap = cx->compartment()->watchpointMap; - if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, &v)) - return false; - } - // Step numbers below reference ES6 rev 27 9.1.9, the [[Set]] internal // method for ordinary objects. We substitute our own names for these names // used in the spec: O -> pobj, P -> id, ownDesc -> shape. diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp index ec0a7aec1..91070b3f6 100644 --- a/js/src/vm/ObjectGroup.cpp +++ b/js/src/vm/ObjectGroup.cpp @@ -184,7 +184,7 @@ ObjectGroup::useSingletonForNewObject(JSContext* cx, JSScript* script, jsbytecod * Sub2 lets us continue to distinguish the two subclasses and any extra * properties added to those prototype objects. */ - if (script->isGenerator()) + if (script->isStarGenerator() || script->isLegacyGenerator() || script->isAsync()) return false; if (JSOp(*pc) != JSOP_NEW) return false; diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index 3c4d61a67..e13cd6221 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -1952,7 +1952,15 @@ * Stack: this => this */ \ macro(JSOP_CHECKTHISREINIT,191,"checkthisreinit",NULL,1, 1, 1, JOF_BYTE) \ - macro(JSOP_UNUSED192, 192,"unused192", NULL, 1, 0, 0, JOF_BYTE) \ + /* + * Pops the top of stack value as 'unwrapped', converts it to async + * generator 'wrapped', and pushes 'wrapped' back on the stack. + * Category: Statements + * Type: Generator + * Operands: + * Stack: unwrapped => wrapped + */ \ + macro(JSOP_TOASYNCGEN, 192, "toasyncgen", NULL, 1, 1, 1, JOF_BYTE) \ \ /* * Pops the top two values on the stack as 'propval' and 'obj', pushes @@ -2055,7 +2063,7 @@ * interpretation. * Category: Statements * Type: Generator - * Operands: uint24_t yieldIndex + * Operands: uint24_t yieldAndAwaitIndex * Stack: generator => */ \ macro(JSOP_INITIALYIELD, 202,"initialyield", NULL, 4, 1, 1, JOF_UINT24) \ @@ -2064,7 +2072,7 @@ * returns 'rval1'. Pushes sent value from 'send()' onto the stack. * Category: Statements * Type: Generator - * Operands: uint24_t yieldIndex + * Operands: uint24_t yieldAndAwaitIndex * Stack: rval1, gen => rval2 */ \ macro(JSOP_YIELD, 203,"yield", NULL, 4, 2, 1, JOF_UINT24) \ @@ -2119,8 +2127,24 @@ * Stack: => */ \ macro(JSOP_DEBUGAFTERYIELD, 208, "debugafteryield", NULL, 1, 0, 0, JOF_BYTE) \ - macro(JSOP_UNUSED209, 209, "unused209", NULL, 1, 0, 0, JOF_BYTE) \ - macro(JSOP_UNUSED210, 210, "unused210", NULL, 1, 0, 0, JOF_BYTE) \ + /* + * Pops the generator and the return value 'promise', stops interpretation + * and returns 'promise'. Pushes resolved value onto the stack. + * Category: Statements + * Type: Generator + * Operands: uint24_t yieldAndAwaitIndex + * Stack: promise, gen => resolved + */ \ + macro(JSOP_AWAIT, 209, "await", NULL, 4, 2, 1, JOF_UINT24) \ + /* + * Pops the iterator from the top of the stack, and create async iterator + * from it and push the async iterator back onto the stack. + * Category: Statements + * Type: Generator + * Operands: + * Stack: iter => asynciter + */ \ + macro(JSOP_TOASYNCITER, 210, "toasynciter", NULL, 1, 1, 1, JOF_BYTE) \ macro(JSOP_UNUSED211, 211, "unused211", NULL, 1, 0, 0, JOF_BYTE) \ /* * Initializes generator frame, creates a generator and pushes it on the diff --git a/js/src/vm/Probes-inl.h b/js/src/vm/Probes-inl.h index 347f842b8..822a8ac59 100644 --- a/js/src/vm/Probes-inl.h +++ b/js/src/vm/Probes-inl.h @@ -41,7 +41,10 @@ probes::EnterScript(JSContext* cx, JSScript* script, JSFunction* maybeFun, if (rt->spsProfiler.enabled()) { if (!rt->spsProfiler.enter(cx, script, maybeFun)) return false; - MOZ_ASSERT_IF(!fp->script()->isGenerator(), !fp->hasPushedSPSFrame()); + MOZ_ASSERT_IF(!fp->script()->isStarGenerator() && + !fp->script()->isLegacyGenerator() && + !fp->script()->isAsync(), + !fp->hasPushedSPSFrame()); fp->setPushedSPSFrame(); } diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index ef97ed816..cd0b54c9d 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -49,6 +49,7 @@ JS_STATIC_ASSERT(GlobalFlag == JSREG_GLOB); JS_STATIC_ASSERT(MultilineFlag == JSREG_MULTILINE); JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY); JS_STATIC_ASSERT(UnicodeFlag == JSREG_UNICODE); +JS_STATIC_ASSERT(DotAllFlag == JSREG_DOTALL); RegExpObject* js::RegExpAlloc(ExclusiveContext* cx, HandleObject proto /* = nullptr */) @@ -267,7 +268,7 @@ RegExpObject::create(ExclusiveContext* cx, HandleAtom source, RegExpFlag flags, tokenStream = dummyTokenStream.ptr(); } - if (!irregexp::ParsePatternSyntax(*tokenStream, alloc, source, flags & UnicodeFlag)) + if (!irregexp::ParsePatternSyntax(*tokenStream, alloc, source, flags & UnicodeFlag, flags & DotAllFlag)) return nullptr; Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx)); @@ -1017,7 +1018,7 @@ RegExpShared::compile(JSContext* cx, HandleAtom pattern, HandleLinearString inpu irregexp::RegExpCompileData data; if (!irregexp::ParsePattern(dummyTokenStream, cx->tempLifoAlloc(), pattern, multiline(), mode == MatchOnly, unicode(), ignoreCase(), - global(), sticky(), &data)) + global(), sticky(), dotall(), &data)) { return false; } diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h index f1ea101ed..95c64fa67 100644 --- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -53,16 +53,18 @@ enum RegExpFlag MultilineFlag = 0x04, StickyFlag = 0x08, UnicodeFlag = 0x10, + DotAllFlag = 0x20, NoFlags = 0x00, - AllFlags = 0x1f + AllFlags = 0x3f }; static_assert(IgnoreCaseFlag == REGEXP_IGNORECASE_FLAG && GlobalFlag == REGEXP_GLOBAL_FLAG && MultilineFlag == REGEXP_MULTILINE_FLAG && StickyFlag == REGEXP_STICKY_FLAG && - UnicodeFlag == REGEXP_UNICODE_FLAG, + UnicodeFlag == REGEXP_UNICODE_FLAG && + DotAllFlag == REGEXP_DOTALL_FLAG, "Flag values should be in sync with self-hosted JS"); enum RegExpRunStatus @@ -193,6 +195,7 @@ class RegExpShared bool multiline() const { return flags & MultilineFlag; } bool sticky() const { return flags & StickyFlag; } bool unicode() const { return flags & UnicodeFlag; } + bool dotall() const { return flags & DotAllFlag; } bool isCompiled(CompilationMode mode, bool latin1, ForceByteCodeEnum force = DontForceByteCode) const { @@ -480,6 +483,7 @@ class RegExpObject : public NativeObject bool multiline() const { return getFlags() & MultilineFlag; } bool sticky() const { return getFlags() & StickyFlag; } bool unicode() const { return getFlags() & UnicodeFlag; } + bool dotall() const { return getFlags() & DotAllFlag; } static bool isOriginalFlagGetter(JSNative native, RegExpFlag* mask); diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 5fc8e0e17..284a4f3d7 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -34,7 +34,6 @@ #include "jsnativestack.h" #include "jsobj.h" #include "jsscript.h" -#include "jswatchpoint.h" #include "jswin.h" #include "jswrapper.h" diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 792a00490..2216bf91e 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -27,6 +27,7 @@ #include "builtin/MapObject.h" #include "builtin/ModuleObject.h" #include "builtin/Object.h" +#include "builtin/Promise.h" #include "builtin/Reflect.h" #include "builtin/SelfHostingDefines.h" #include "builtin/SIMD.h" @@ -857,33 +858,20 @@ intrinsic_NewStringIterator(JSContext* cx, unsigned argc, Value* vp) } static bool -intrinsic_NewListIterator(JSContext* cx, unsigned argc, Value* vp) +intrinsic_NewRegExpStringIterator(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 0); - RootedObject proto(cx, GlobalObject::getOrCreateIteratorPrototype(cx, cx->global())); + RootedObject proto(cx, GlobalObject::getOrCreateRegExpStringIteratorPrototype(cx, cx->global())); if (!proto) return false; - RootedObject iterator(cx); - iterator = NewObjectWithGivenProto(cx, &ListIteratorObject::class_, proto); - if (!iterator) + JSObject* obj = NewObjectWithGivenProto(cx, &RegExpStringIteratorObject::class_, proto); + if (!obj) return false; - args.rval().setObject(*iterator); - return true; -} - -static bool -intrinsic_ActiveFunction(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - MOZ_ASSERT(args.length() == 0); - - ScriptFrameIter iter(cx); - MOZ_ASSERT(iter.isFunctionFrame()); - args.rval().setObject(*iter.callee(cx)); + args.rval().setObject(*obj); return true; } @@ -2133,6 +2121,120 @@ intrinsic_ModuleNamespaceExports(JSContext* cx, unsigned argc, Value* vp) return true; } +static bool +intrinsic_PromiseResolve(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + + RootedObject constructor(cx, &args[0].toObject()); + JSObject* promise = js::PromiseResolve(cx, constructor, args[1]); + if (!promise) + return false; + + args.rval().setObject(*promise); + return true; +} + +static bool +intrinsic_CreatePendingPromise(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + RootedObject promise(cx, PromiseObject::createSkippingExecutor(cx)); + if (!promise) + return false; + args.rval().setObject(*promise); + return true; +} + +static bool +intrinsic_CreatePromiseResolvedWith(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + RootedObject promise(cx, PromiseObject::unforgeableResolve(cx, args[0])); + if (!promise) + return false; + args.rval().setObject(*promise); + return true; +} + +static bool +intrinsic_CreatePromiseRejectedWith(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + RootedObject promise(cx, PromiseObject::unforgeableReject(cx, args[0])); + if (!promise) + return false; + args.rval().setObject(*promise); + return true; +} + +static bool +intrinsic_ResolvePromise(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + Rooted<PromiseObject*> promise(cx, &args[0].toObject().as<PromiseObject>()); + if (!PromiseObject::resolve(cx, promise, args[1])) + return false; + args.rval().setUndefined(); + return true; +} + +static bool +intrinsic_RejectPromise(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + Rooted<PromiseObject*> promise(cx, &args[0].toObject().as<PromiseObject>()); + if (!PromiseObject::reject(cx, promise, args[1])) + return false; + args.rval().setUndefined(); + return true; +} + +static bool +intrinsic_CallOriginalPromiseThen(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() >= 2); + + RootedObject promise(cx, &args[0].toObject()); + Value val = args[1]; + RootedObject onResolvedObj(cx, val.isUndefined() ? nullptr : val.toObjectOrNull()); + val = args.get(2); + RootedObject onRejectedObj(cx, val.isUndefined() ? nullptr : val.toObjectOrNull()); + + RootedObject resultPromise(cx, JS::CallOriginalPromiseThen(cx, promise, onResolvedObj, + onRejectedObj)); + if (!resultPromise) + return false; + args.rval().setObject(*resultPromise); + return true; +} + +static bool +intrinsic_AddPromiseReactions(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() >= 2); + + RootedObject promise(cx, &args[0].toObject()); + Value val = args[1]; + RootedObject onResolvedObj(cx, val.isUndefined() ? nullptr : val.toObjectOrNull()); + val = args.get(2); + RootedObject onRejectedObj(cx, val.isUndefined() ? nullptr : val.toObjectOrNull()); + + bool result = JS::AddPromiseReactions(cx, promise, onResolvedObj, onRejectedObj); + if (!result) + return false; + args.rval().setUndefined(); + return true; +} + // The self-hosting global isn't initialized with the normal set of builtins. // Instead, individual C++-implemented functions that're required by // self-hosted code are defined as global functions. Accessing these @@ -2290,11 +2392,6 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("CallArrayIteratorMethodIfWrapped", CallNonGenericSelfhostedMethod<Is<ArrayIteratorObject>>, 2,0), - JS_FN("NewListIterator", intrinsic_NewListIterator, 0,0), - JS_FN("CallListIteratorMethodIfWrapped", - CallNonGenericSelfhostedMethod<Is<ListIteratorObject>>, 2,0), - JS_FN("ActiveFunction", intrinsic_ActiveFunction, 0,0), - JS_FN("_SetCanonicalName", intrinsic_SetCanonicalName, 2,0), JS_INLINABLE_FN("GuardToArrayIterator", @@ -2309,9 +2406,8 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_INLINABLE_FN("GuardToStringIterator", intrinsic_GuardToBuiltin<StringIteratorObject>, 1,0, IntrinsicGuardToStringIterator), - JS_INLINABLE_FN("IsListIterator", - intrinsic_IsInstanceOfBuiltin<ListIteratorObject>, 1,0, - IntrinsicIsListIterator), + JS_FN("GuardToRegExpStringIterator", + intrinsic_GuardToBuiltin<RegExpStringIteratorObject>, 1,0), JS_FN("_CreateMapIterationResultPair", intrinsic_CreateMapIterationResultPair, 0, 0), JS_INLINABLE_FN("_GetNextMapEntryForIterator", intrinsic_GetNextMapEntryForIterator, 2,0, @@ -2329,6 +2425,9 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("NewStringIterator", intrinsic_NewStringIterator, 0,0), JS_FN("CallStringIteratorMethodIfWrapped", CallNonGenericSelfhostedMethod<Is<StringIteratorObject>>, 2,0), + JS_FN("NewRegExpStringIterator", intrinsic_NewRegExpStringIterator, 0,0), + JS_FN("CallRegExpStringIteratorMethodIfWrapped", + CallNonGenericSelfhostedMethod<Is<RegExpStringIteratorObject>>, 2,0), JS_FN("IsStarGeneratorObject", intrinsic_IsInstanceOfBuiltin<StarGeneratorObject>, 1,0), @@ -2533,11 +2632,22 @@ static const JSFunctionSpec intrinsic_functions[] = { intrinsic_InstantiateModuleFunctionDeclarations, 1, 0), JS_FN("SetModuleState", intrinsic_SetModuleState, 1, 0), JS_FN("EvaluateModule", intrinsic_EvaluateModule, 1, 0), - JS_FN("IsModuleNamespace", intrinsic_IsInstanceOfBuiltin<ModuleNamespaceObject>, 1, 0), JS_FN("NewModuleNamespace", intrinsic_NewModuleNamespace, 2, 0), JS_FN("AddModuleNamespaceBinding", intrinsic_AddModuleNamespaceBinding, 4, 0), JS_FN("ModuleNamespaceExports", intrinsic_ModuleNamespaceExports, 1, 0), + JS_FN("CreatePendingPromise", intrinsic_CreatePendingPromise, 0, 0), + JS_FN("CreatePromiseResolvedWith", intrinsic_CreatePromiseResolvedWith, 1, 0), + JS_FN("CreatePromiseRejectedWith", intrinsic_CreatePromiseRejectedWith, 1, 0), + JS_FN("ResolvePromise", intrinsic_ResolvePromise, 2, 0), + JS_FN("RejectPromise", intrinsic_RejectPromise, 2, 0), + JS_FN("AddPromiseReactions", intrinsic_AddPromiseReactions, 3, 0), + JS_FN("CallOriginalPromiseThen", intrinsic_CallOriginalPromiseThen, 3, 0), + + JS_FN("IsPromiseObject", intrinsic_IsInstanceOfBuiltin<PromiseObject>, 1, 0), + JS_FN("CallPromiseMethodIfWrapped", CallNonGenericSelfhostedMethod<Is<PromiseObject>>, 2, 0), + JS_FN("PromiseResolve", intrinsic_PromiseResolve, 2, 0), + JS_FS_END }; @@ -3003,7 +3113,8 @@ JSRuntime::cloneSelfHostedFunctionScript(JSContext* cx, HandlePropertyName name, return false; // JSFunction::generatorKind can't handle lazy self-hosted functions, so we make sure there // aren't any. - MOZ_ASSERT(!sourceFun->isGenerator()); + MOZ_ASSERT(!sourceFun->isStarGenerator() && !sourceFun->isLegacyGenerator() && + !sourceFun->isAsync()); MOZ_ASSERT(targetFun->isExtended()); MOZ_ASSERT(targetFun->isInterpretedLazy()); MOZ_ASSERT(targetFun->isSelfHostedBuiltin()); diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index fd6d843e0..85bc044a5 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -387,7 +387,7 @@ class BaseShape : public gc::TenuredCell INDEXED = 0x20, /* (0x40 is unused) */ HAD_ELEMENTS_ACCESS = 0x80, - WATCHED = 0x100, + /* (0x100 is unused) */ ITERATED_SINGLETON = 0x200, NEW_GROUP_UNKNOWN = 0x400, UNCACHEABLE_PROTO = 0x800, diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index 11a19d175..2f2b591e5 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -335,7 +335,7 @@ InterpreterStack::resumeGeneratorCallFrame(JSContext* cx, InterpreterRegs& regs, HandleFunction callee, HandleValue newTarget, HandleObject envChain) { - MOZ_ASSERT(callee->isGenerator()); + MOZ_ASSERT(callee->isStarGenerator() || callee->isLegacyGenerator() || callee->isAsync()); RootedScript script(cx, JSFunction::getOrCreateScript(cx, callee)); InterpreterFrame* prev = regs.fp(); jsbytecode* prevpc = regs.pc; diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index c5f2cf5f3..95940eeaf 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -270,7 +270,9 @@ InterpreterFrame::epilogue(JSContext* cx, jsbytecode* pc) UnwindAllEnvironmentsInFrame(cx, ei); if (isFunctionFrame()) { - if (!callee().isGenerator() && + if (!callee().isStarGenerator() && + !callee().isLegacyGenerator() && + !callee().isAsync() && isConstructing() && thisArgument().isObject() && returnValue().isPrimitive()) diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 23e621344..fe04a00f2 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -693,7 +693,8 @@ class InterpreterFrame } void resumeGeneratorFrame(JSObject* envChain) { - MOZ_ASSERT(script()->isGenerator()); + MOZ_ASSERT(script()->isStarGenerator() || script()->isLegacyGenerator() || + script()->isAsync()); MOZ_ASSERT(isFunctionFrame()); flags_ |= HAS_INITIAL_ENV; envChain_ = envChain; diff --git a/js/src/vm/Time.cpp b/js/src/vm/Time.cpp index 87531c148..a9a5b7f0f 100644 --- a/js/src/vm/Time.cpp +++ b/js/src/vm/Time.cpp @@ -11,6 +11,10 @@ #include "mozilla/DebugOnly.h" #include "mozilla/MathAlgorithms.h" +#ifdef XP_SOLARIS +#define _REENTRANT 1 +#endif + #include <string.h> #include <time.h> @@ -30,6 +34,10 @@ #ifdef XP_UNIX +#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */ +extern int gettimeofday(struct timeval* tv); +#endif + #include <sys/time.h> #endif /* XP_UNIX */ @@ -42,7 +50,11 @@ PRMJ_Now() { struct timeval tv; +#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */ + gettimeofday(&tv); +#else gettimeofday(&tv, 0); +#endif /* _SVID_GETTOD */ return int64_t(tv.tv_sec) * PRMJ_USEC_PER_SEC + int64_t(tv.tv_usec); } diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index 7c2c0194e..2b1fa0e3b 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -2670,14 +2670,6 @@ ObjectGroup::updateNewPropertyTypes(ExclusiveContext* cx, JSObject* objArg, jsid if (shape) UpdatePropertyType(cx, types, obj, shape, false); } - - if (obj->watched()) { - /* - * Mark the property as non-data, to inhibit optimizations on it - * and avoid bypassing the watchpoint handler. - */ - types->setNonDataProperty(cx); - } } void @@ -3084,29 +3076,39 @@ class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint }; bool -js::AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx, ObjectGroup* group, HandleId id) +js::AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx, + DPAConstraintInfo& constraintInfo, + ObjectGroup* group, + HandleId id, + bool* added) { /* * Ensure that if the properties named here could have a getter, setter or * a permanent property in any transitive prototype, the definite * properties get cleared from the group. */ + + *added = false; + RootedObject proto(cx, group->proto().toObjectOrNull()); while (proto) { ObjectGroup* protoGroup = JSObject::getGroup(cx, proto); if (!protoGroup) { - cx->recoverFromOutOfMemory(); return false; } if (protoGroup->unknownProperties()) - return false; + return true; HeapTypeSet* protoTypes = protoGroup->getProperty(cx, proto, id); - if (!protoTypes || protoTypes->nonDataProperty() || protoTypes->nonWritableProperty()) + if (!protoTypes) return false; - if (!protoTypes->addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(group))) + if (protoTypes->nonDataProperty() || protoTypes->nonWritableProperty()) + return true; + if (!constraintInfo.addProtoConstraint(proto, id)) return false; proto = proto->staticPrototype(); } + + *added = true; return true; } @@ -3612,6 +3614,43 @@ struct DestroyTypeNewScript } // namespace +bool DPAConstraintInfo::finishConstraints(JSContext* cx, ObjectGroup* group) { + for (const ProtoConstraint& constraint : protoConstraints_) { + ObjectGroup* protoGroup = constraint.proto->group(); + + // Note: we rely on the group's type information being unchanged since + // AddClearDefiniteGetterSetterForPrototypeChain. + + bool unknownProperties = protoGroup->unknownProperties(); + MOZ_RELEASE_ASSERT(!unknownProperties); + + HeapTypeSet* protoTypes = + protoGroup->getProperty(cx, constraint.proto, constraint.id); + MOZ_RELEASE_ASSERT(protoTypes); + + MOZ_ASSERT(!protoTypes->nonDataProperty()); + MOZ_ASSERT(!protoTypes->nonWritableProperty()); + + if (!protoTypes->addConstraint( + cx, + cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>( + group))) { + ReportOutOfMemory(cx); + return false; + } + } + + for (const InliningConstraint& constraint : inliningConstraints_) { + if (!AddClearDefiniteFunctionUsesInScript(cx, group, constraint.caller, + constraint.callee)) { + ReportOutOfMemory(cx); + return false; + } + } + + return true; +} + bool TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate, bool force) { @@ -3715,10 +3754,17 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate, return false; Vector<Initializer> initializerVector(cx); + + DPAConstraintInfo constraintInfo(cx); RootedPlainObject templateRoot(cx, templateObject()); RootedFunction fun(cx, function()); - if (!jit::AnalyzeNewScriptDefiniteProperties(cx, fun, group, templateRoot, &initializerVector)) + if (!jit::AnalyzeNewScriptDefiniteProperties(cx, + constraintInfo, + fun, + group, + templateRoot, + &initializerVector)) return false; if (!group->newScript()) @@ -3772,6 +3818,14 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate, // The definite properties analysis found exactly the properties that // are held in common by the preliminary objects. No further analysis // is needed. + + if (!constraintInfo.finishConstraints(cx, group)) { + return false; + } + if (!group->newScript()) { + return true; + } + group->addDefiniteProperties(cx, templateObject()->lastProperty()); destroyNewScript.group = nullptr; @@ -3793,6 +3847,16 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate, if (!initialGroup) return false; + // Add the constraints. Use the initialGroup as group referenced by the + // constraints because that's the group that will have the TypeNewScript + // associated with it. See the detachNewScript and setNewScript calls below. + if (!constraintInfo.finishConstraints(cx, initialGroup)) { + return false; + } + if (!group->newScript()) { + return true; + } + initialGroup->addDefiniteProperties(cx, templateObject()->lastProperty()); group->addDefiniteProperties(cx, prefixShape); diff --git a/js/src/vm/TypeInference.h b/js/src/vm/TypeInference.h index 94ce7e871..fd021fc96 100644 --- a/js/src/vm/TypeInference.h +++ b/js/src/vm/TypeInference.h @@ -789,8 +789,65 @@ class TemporaryTypeSet : public TypeSet TypedArraySharedness* sharedness); }; +// Stack class to record information about constraints that need to be added +// after finishing the Definite Properties Analysis. When the analysis succeeds, +// the |finishConstraints| method must be called to add the constraints to the +// TypeSets. +// +// There are two constraint types managed here: +// +// 1. Proto constraints for HeapTypeSets, to guard against things like getters +// and setters on the proto chain. +// +// 2. Inlining constraints for StackTypeSets, to invalidate when additional +// functions could be called at call sites where we inlined a function. +// +// This class uses bare GC-thing pointers because GC is suppressed when the +// analysis runs. +class MOZ_RAII DPAConstraintInfo { + struct ProtoConstraint { + JSObject* proto; + jsid id; + ProtoConstraint(JSObject* proto, jsid id) : proto(proto), id(id) {} + }; + struct InliningConstraint { + JSScript* caller; + JSScript* callee; + InliningConstraint(JSScript* caller, JSScript* callee) + : caller(caller), callee(callee) {} + }; + + JS::AutoCheckCannotGC nogc_; + Vector<ProtoConstraint, 8> protoConstraints_; + Vector<InliningConstraint, 4> inliningConstraints_; + +public: + explicit DPAConstraintInfo(JSContext* cx) + : nogc_(cx) + , protoConstraints_(cx) + , inliningConstraints_(cx) + { + } + + DPAConstraintInfo(const DPAConstraintInfo&) = delete; + void operator=(const DPAConstraintInfo&) = delete; + + MOZ_MUST_USE bool addProtoConstraint(JSObject* proto, jsid id) { + return protoConstraints_.emplaceBack(proto, id); + } + MOZ_MUST_USE bool addInliningConstraint(JSScript* caller, JSScript* callee) { + return inliningConstraints_.emplaceBack(caller, callee); + } + + MOZ_MUST_USE bool finishConstraints(JSContext* cx, ObjectGroup* group); +}; + bool -AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx, ObjectGroup* group, HandleId id); +AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx, + DPAConstraintInfo& constraintInfo, + ObjectGroup* group, + HandleId id, + bool* added); bool AddClearDefiniteFunctionUsesInScript(JSContext* cx, ObjectGroup* group, diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index 52b8eeed1..f3eebb85e 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -8608,9 +8608,12 @@ EstablishPreconditions(ExclusiveContext* cx, AsmJSParser& parser) break; } - if (parser.pc->isGenerator()) + if (parser.pc->isStarGenerator() || parser.pc->isLegacyGenerator()) return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by generator context"); + if (parser.pc->isAsync()) + return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by async context"); + if (parser.pc->isArrowFunction()) return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by arrow function context"); diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp index 8d4f575b2..fd9530e20 100644 --- a/js/src/wasm/WasmJS.cpp +++ b/js/src/wasm/WasmJS.cpp @@ -1640,7 +1640,6 @@ WebAssembly_toSource(JSContext* cx, unsigned argc, Value* vp) } #endif -#ifdef SPIDERMONKEY_PROMISE static bool Nop(JSContext* cx, unsigned argc, Value* vp) { @@ -1914,7 +1913,6 @@ WebAssembly_instantiate(JSContext* cx, unsigned argc, Value* vp) callArgs.rval().setObject(*promise); return true; } -#endif static bool WebAssembly_validate(JSContext* cx, unsigned argc, Value* vp) @@ -1949,10 +1947,8 @@ static const JSFunctionSpec WebAssembly_static_methods[] = #if JS_HAS_TOSOURCE JS_FN(js_toSource_str, WebAssembly_toSource, 0, 0), #endif -#ifdef SPIDERMONKEY_PROMISE JS_FN("compile", WebAssembly_compile, 1, 0), JS_FN("instantiate", WebAssembly_instantiate, 2, 0), -#endif JS_FN("validate", WebAssembly_validate, 1, 0), JS_FS_END }; diff --git a/js/src/wasm/WasmSignalHandlers.cpp b/js/src/wasm/WasmSignalHandlers.cpp index c4733cc96..21093ca9a 100644 --- a/js/src/wasm/WasmSignalHandlers.cpp +++ b/js/src/wasm/WasmSignalHandlers.cpp @@ -130,11 +130,16 @@ class AutoSetHandlingSegFault # define EPC_sig(p) ((p)->sc_pc) # define RFP_sig(p) ((p)->sc_regs[30]) # endif -#elif defined(__linux__) +#elif defined(__linux__) || defined(__sun) # if defined(__linux__) # define XMM_sig(p,i) ((p)->uc_mcontext.fpregs->_xmm[i]) # define EIP_sig(p) ((p)->uc_mcontext.gregs[REG_EIP]) -# else +# else // defined(__sun) +/* See https://www.illumos.org/issues/5876. They keep arguing over whether + * <ucontext.h> should provide the register index defines in regset.h or + * require applications to request them specifically, and we need them here. */ +#include <ucontext.h> +#include <sys/regset.h> # define XMM_sig(p,i) ((p)->uc_mcontext.fpregs.fp_reg_set.fpchip_state.xmm[i]) # define EIP_sig(p) ((p)->uc_mcontext.gregs[REG_PC]) # endif |