diff options
Diffstat (limited to 'js')
39 files changed, 2114 insertions, 579 deletions
diff --git a/js/src/builtin/Intl.cpp b/js/src/builtin/Intl.cpp index 990a4acdb..3a20c487b 100644 --- a/js/src/builtin/Intl.cpp +++ b/js/src/builtin/Intl.cpp @@ -765,42 +765,53 @@ static const JSFunctionSpec collator_methods[] = { }; /** - * Collator constructor. - * Spec: ECMAScript Internationalization API Specification, 10.1 + * 10.1.2 Intl.Collator([ locales [, options]]) + * + * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b */ static bool Collator(JSContext* cx, const CallArgs& args, bool construct) { RootedObject obj(cx); + // We're following ECMA-402 1st Edition when Collator is called because of + // backward compatibility issues. + // See https://github.com/tc39/ecma402/issues/57 if (!construct) { - // 10.1.2.1 step 3 + // ES Intl 1st ed., 10.1.2.1 step 3 JSObject* intl = cx->global()->getOrCreateIntlObject(cx); if (!intl) return false; RootedValue self(cx, args.thisv()); if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) { - // 10.1.2.1 step 4 + // ES Intl 1st ed., 10.1.2.1 step 4 obj = ToObject(cx, self); if (!obj) return false; - // 10.1.2.1 step 5 + // ES Intl 1st ed., 10.1.2.1 step 5 bool extensible; if (!IsExtensible(cx, obj, &extensible)) return false; if (!extensible) return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE); } else { - // 10.1.2.1 step 3.a + // ES Intl 1st ed., 10.1.2.1 step 3.a construct = true; } } if (construct) { - // 10.1.3.1 paragraph 2 - RootedObject proto(cx, cx->global()->getOrCreateCollatorPrototype(cx)); - if (!proto) + // Steps 2-5 (Inlined 9.1.14, OrdinaryCreateFromConstructor). + RootedObject proto(cx); + if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto)) return false; + + if (!proto) { + proto = cx->global()->getOrCreateCollatorPrototype(cx); + if (!proto) + return false; + } + obj = NewObjectWithGivenProto(cx, &CollatorClass, proto); if (!obj) return false; @@ -808,15 +819,13 @@ Collator(JSContext* cx, const CallArgs& args, bool construct) obj->as<NativeObject>().setReservedSlot(UCOLLATOR_SLOT, PrivateValue(nullptr)); } - // 10.1.2.1 steps 1 and 2; 10.1.3.1 steps 1 and 2 RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue()); RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue()); - // 10.1.2.1 step 6; 10.1.3.1 step 3 + // Step 6. if (!IntlInitialize(cx, obj, cx->names().InitializeCollator, locales, options)) return false; - // 10.1.2.1 steps 3.a and 7 args.rval().setObject(*obj); return true; } @@ -833,6 +842,7 @@ js::intl_Collator(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 2); + MOZ_ASSERT(!args.isConstructing()); // intl_Collator is an intrinsic for self-hosted JavaScript, so it cannot // be used with "new", but it still has to be treated as a constructor. return Collator(cx, args, true); @@ -1257,42 +1267,53 @@ static const JSFunctionSpec numberFormat_methods[] = { }; /** - * NumberFormat constructor. - * Spec: ECMAScript Internationalization API Specification, 11.1 + * 11.2.1 Intl.NumberFormat([ locales [, options]]) + * + * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b */ static bool NumberFormat(JSContext* cx, const CallArgs& args, bool construct) { RootedObject obj(cx); + // We're following ECMA-402 1st Edition when NumberFormat is called + // because of backward compatibility issues. + // See https://github.com/tc39/ecma402/issues/57 if (!construct) { - // 11.1.2.1 step 3 + // ES Intl 1st ed., 11.1.2.1 step 3 JSObject* intl = cx->global()->getOrCreateIntlObject(cx); if (!intl) return false; RootedValue self(cx, args.thisv()); if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) { - // 11.1.2.1 step 4 + // ES Intl 1st ed., 11.1.2.1 step 4 obj = ToObject(cx, self); if (!obj) return false; - // 11.1.2.1 step 5 + // ES Intl 1st ed., 11.1.2.1 step 5 bool extensible; if (!IsExtensible(cx, obj, &extensible)) return false; if (!extensible) return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE); } else { - // 11.1.2.1 step 3.a + // ES Intl 1st ed., 11.1.2.1 step 3.a construct = true; } } if (construct) { - // 11.1.3.1 paragraph 2 - RootedObject proto(cx, cx->global()->getOrCreateNumberFormatPrototype(cx)); - if (!proto) + // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor). + RootedObject proto(cx); + if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto)) return false; + + if (!proto) { + proto = cx->global()->getOrCreateNumberFormatPrototype(cx); + if (!proto) + return false; + } + obj = NewObjectWithGivenProto(cx, &NumberFormatClass, proto); if (!obj) return false; @@ -1300,15 +1321,13 @@ NumberFormat(JSContext* cx, const CallArgs& args, bool construct) obj->as<NativeObject>().setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nullptr)); } - // 11.1.2.1 steps 1 and 2; 11.1.3.1 steps 1 and 2 RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue()); RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue()); - // 11.1.2.1 step 6; 11.1.3.1 step 3 + // Step 3. if (!IntlInitialize(cx, obj, cx->names().InitializeNumberFormat, locales, options)) return false; - // 11.1.2.1 steps 3.a and 7 args.rval().setObject(*obj); return true; } @@ -1325,6 +1344,7 @@ js::intl_NumberFormat(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 2); + MOZ_ASSERT(!args.isConstructing()); // intl_NumberFormat is an intrinsic for self-hosted JavaScript, so it // cannot be used with "new", but it still has to be treated as a // constructor. @@ -1725,42 +1745,53 @@ static const JSFunctionSpec dateTimeFormat_methods[] = { }; /** - * DateTimeFormat constructor. - * Spec: ECMAScript Internationalization API Specification, 12.1 + * 12.2.1 Intl.DateTimeFormat([ locales [, options]]) + * + * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b */ static bool DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct) { RootedObject obj(cx); + // We're following ECMA-402 1st Edition when DateTimeFormat is called + // because of backward compatibility issues. + // See https://github.com/tc39/ecma402/issues/57 if (!construct) { - // 12.1.2.1 step 3 + // ES Intl 1st ed., 12.1.2.1 step 3 JSObject* intl = cx->global()->getOrCreateIntlObject(cx); if (!intl) return false; RootedValue self(cx, args.thisv()); if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) { - // 12.1.2.1 step 4 + // ES Intl 1st ed., 12.1.2.1 step 4 obj = ToObject(cx, self); if (!obj) return false; - // 12.1.2.1 step 5 + // ES Intl 1st ed., 12.1.2.1 step 5 bool extensible; if (!IsExtensible(cx, obj, &extensible)) return false; if (!extensible) return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE); } else { - // 12.1.2.1 step 3.a + // ES Intl 1st ed., 12.1.2.1 step 3.a construct = true; } } if (construct) { - // 12.1.3.1 paragraph 2 - RootedObject proto(cx, cx->global()->getOrCreateDateTimeFormatPrototype(cx)); - if (!proto) + // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor). + RootedObject proto(cx); + if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto)) return false; + + if (!proto) { + proto = cx->global()->getOrCreateDateTimeFormatPrototype(cx); + if (!proto) + return false; + } + obj = NewObjectWithGivenProto(cx, &DateTimeFormatClass, proto); if (!obj) return false; @@ -1768,15 +1799,13 @@ DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct) obj->as<NativeObject>().setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(nullptr)); } - // 12.1.2.1 steps 1 and 2; 12.1.3.1 steps 1 and 2 RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue()); RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue()); - // 12.1.2.1 step 6; 12.1.3.1 step 3 + // Step 3. if (!IntlInitialize(cx, obj, cx->names().InitializeDateTimeFormat, locales, options)) return false; - // 12.1.2.1 steps 3.a and 7 args.rval().setObject(*obj); return true; } @@ -1793,6 +1822,7 @@ js::intl_DateTimeFormat(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 2); + MOZ_ASSERT(!args.isConstructing()); // intl_DateTimeFormat is an intrinsic for self-hosted JavaScript, so it // cannot be used with "new", but it still has to be treated as a // constructor. diff --git a/js/src/builtin/TypedArray.js b/js/src/builtin/TypedArray.js index 4d2d6488f..4a3f38365 100644 --- a/js/src/builtin/TypedArray.js +++ b/js/src/builtin/TypedArray.js @@ -35,6 +35,10 @@ function IsDetachedBuffer(buffer) { return (flags & JS_ARRAYBUFFER_DETACHED_FLAG) !== 0; } +function TypedArrayLengthMethod() { + return TypedArrayLength(this); +} + function GetAttachedArrayBuffer(tarray) { var buffer = ViewedArrayBufferIfReified(tarray); if (IsDetachedBuffer(buffer)) @@ -42,6 +46,10 @@ function GetAttachedArrayBuffer(tarray) { return buffer; } +function GetAttachedArrayBufferMethod() { + return GetAttachedArrayBuffer(this); +} + // A function which ensures that the argument is either a typed array or a // cross-compartment wrapper for a typed array and that the typed array involved // has an attached array buffer. If one of those conditions doesn't hold (wrong @@ -54,10 +62,7 @@ function IsTypedArrayEnsuringArrayBuffer(arg) { return true; } - // This is a bit hacky but gets the job done: the first `arg` is used to - // test for a wrapped typed array, the second as an argument to - // GetAttachedArrayBuffer. - callFunction(CallTypedArrayMethodIfWrapped, arg, arg, "GetAttachedArrayBuffer"); + callFunction(CallTypedArrayMethodIfWrapped, arg, "GetAttachedArrayBufferMethod"); return false; } @@ -98,8 +103,8 @@ function TypedArrayCreateWithLength(constructor, length) { if (isTypedArray) { len = TypedArrayLength(newTypedArray); } else { - len = callFunction(CallTypedArrayMethodIfWrapped, newTypedArray, newTypedArray, - "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, newTypedArray, + "TypedArrayLengthMethod"); } if (len < length) @@ -259,15 +264,14 @@ function TypedArrayEvery(callbackfn/*, thisArg*/) { // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O); - // If we got here, `this` is either a typed array or a cross-compartment - // wrapper for one. + // If we got here, `this` is either a typed array or a wrapper for one. // Steps 3-5. var len; if (isTypedArray) len = TypedArrayLength(O); else - len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayLengthMethod"); // Step 6. if (arguments.length === 0) @@ -348,15 +352,14 @@ function TypedArrayFilter(callbackfn/*, thisArg*/) { // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O); - // If we got here, `this` is either a typed array or a cross-compartment - // wrapper for one. + // If we got here, `this` is either a typed array or a wrapper for one. // Step 3. var len; if (isTypedArray) len = TypedArrayLength(O); else - len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayLengthMethod"); // Step 4. if (arguments.length === 0) @@ -410,15 +413,14 @@ function TypedArrayFind(predicate/*, thisArg*/) { // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O); - // If we got here, `this` is either a typed array or a cross-compartment - // wrapper for one. + // If we got here, `this` is either a typed array or a wrapper for one. // Steps 3-5. var len; if (isTypedArray) len = TypedArrayLength(O); else - len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayLengthMethod"); // Step 6. if (arguments.length === 0) @@ -452,15 +454,14 @@ function TypedArrayFindIndex(predicate/*, thisArg*/) { // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O); - // If we got here, `this` is either a typed array or a cross-compartment - // wrapper for one. + // If we got here, `this` is either a typed array or a wrapper for one. // Steps 3-5. var len; if (isTypedArray) len = TypedArrayLength(O); else - len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayLengthMethod"); // Step 6. if (arguments.length === 0) @@ -492,15 +493,14 @@ function TypedArrayForEach(callbackfn/*, thisArg*/) { // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O); - // If we got here, `this` is either a typed array or a cross-compartment - // wrapper for one. + // If we got here, `this` is either a typed array or a wrapper for one. // Step 3-4. var len; if (isTypedArray) len = TypedArrayLength(O); else - len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayLengthMethod"); // Step 5. if (arguments.length === 0) @@ -686,15 +686,14 @@ function TypedArrayMap(callbackfn/*, thisArg*/) { // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O); - // If we got here, `this` is either a typed array or a cross-compartment - // wrapper for one. + // If we got here, `this` is either a typed array or a wrapper for one. // Step 3. var len; if (isTypedArray) len = TypedArrayLength(O); else - len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayLengthMethod"); // Step 4. if (arguments.length === 0) @@ -730,15 +729,14 @@ function TypedArrayReduce(callbackfn/*, initialValue*/) { // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O); - // If we got here, `this` is either a typed array or a cross-compartment - // wrapper for one. + // If we got here, `this` is either a typed array or a wrapper for one. // Steps 3-5. var len; if (isTypedArray) len = TypedArrayLength(O); else - len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayLengthMethod"); // Step 6. if (arguments.length === 0) @@ -776,15 +774,14 @@ function TypedArrayReduceRight(callbackfn/*, initialValue*/) { // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O); - // If we got here, `this` is either a typed array or a cross-compartment - // wrapper for one. + // If we got here, `this` is either a typed array or a wrapper for one. // Steps 3-5. var len; if (isTypedArray) len = TypedArrayLength(O); else - len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayLengthMethod"); // Step 6. if (arguments.length === 0) @@ -1034,15 +1031,14 @@ function TypedArraySome(callbackfn/*, thisArg*/) { // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O); - // If we got here, `this` is either a typed array or a cross-compartment - // wrapper for one. + // If we got here, `this` is either a typed array or a wrapper for one. // Steps 3-5. var len; if (isTypedArray) len = TypedArrayLength(O); else - len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayLengthMethod"); // Step 6. if (arguments.length === 0) @@ -1137,7 +1133,7 @@ function TypedArraySort(comparefn) { if (isTypedArray) { buffer = GetAttachedArrayBuffer(obj); } else { - buffer = callFunction(CallTypedArrayMethodIfWrapped, obj, obj, "GetAttachedArrayBuffer"); + buffer = callFunction(CallTypedArrayMethodIfWrapped, obj, "GetAttachedArrayBufferMethod"); } // Step 3. @@ -1145,7 +1141,7 @@ function TypedArraySort(comparefn) { if (isTypedArray) { len = TypedArrayLength(obj); } else { - len = callFunction(CallTypedArrayMethodIfWrapped, obj, obj, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, obj, "TypedArrayLengthMethod"); } if (comparefn === undefined) { @@ -1181,8 +1177,8 @@ function TypedArraySort(comparefn) { if (isTypedArray) { buffer = GetAttachedArrayBuffer(obj); } else { - buffer = callFunction(CallTypedArrayMethodIfWrapped, obj, obj, - "GetAttachedArrayBuffer"); + buffer = callFunction(CallTypedArrayMethodIfWrapped, obj, + "GetAttachedArrayBufferMethod"); } } var bufferDetached; @@ -1217,15 +1213,14 @@ function TypedArrayToLocaleString(locales = undefined, options = undefined) { // We want to make sure that we have an attached buffer, per spec prose. var isTypedArray = IsTypedArrayEnsuringArrayBuffer(array); - // If we got here, `this` is either a typed array or a cross-compartment - // wrapper for one. + // If we got here, `this` is either a typed array or a wrapper for one. // Step 2. var len; if (isTypedArray) len = TypedArrayLength(array); else - len = callFunction(CallTypedArrayMethodIfWrapped, array, array, "TypedArrayLength"); + len = callFunction(CallTypedArrayMethodIfWrapped, array, "TypedArrayLengthMethod"); // Step 4. if (len === 0) diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index d4c758b6c..3fbfdaa1a 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -7,6 +7,7 @@ #include "frontend/BytecodeCompiler.h" #include "mozilla/IntegerPrintfMacros.h" +#include "mozilla/Maybe.h" #include "jscntxt.h" #include "jsscript.h" @@ -28,6 +29,7 @@ using namespace js; using namespace js::frontend; using mozilla::Maybe; +using mozilla::Nothing; class MOZ_STACK_CLASS AutoCompilationTraceLogger { @@ -57,24 +59,24 @@ class MOZ_STACK_CLASS BytecodeCompiler // Call setters for optional arguments. void maybeSetSourceCompressor(SourceCompressionTask* sourceCompressor); - void setSourceArgumentsNotIncluded(); JSScript* compileGlobalScript(ScopeKind scopeKind); JSScript* compileEvalScript(HandleObject environment, HandleScope enclosingScope); ModuleObject* compileModule(); - bool compileFunctionBody(MutableHandleFunction fun, Handle<PropertyNameVector> formals, - GeneratorKind generatorKind, FunctionAsyncKind asyncKind); + bool compileStandaloneFunction(MutableHandleFunction fun, GeneratorKind generatorKind, + FunctionAsyncKind asyncKind, + Maybe<uint32_t> parameterListEnd); ScriptSourceObject* sourceObjectPtr() const; private: JSScript* compileScript(HandleObject environment, SharedContext* sc); bool checkLength(); - bool createScriptSource(); + bool createScriptSource(Maybe<uint32_t> parameterListEnd); bool maybeCompressSource(); bool canLazilyParse(); bool createParser(); - bool createSourceAndParser(); + bool createSourceAndParser(Maybe<uint32_t> parameterListEnd = Nothing()); bool createScript(); bool emplaceEmitter(Maybe<BytecodeEmitter>& emitter, SharedContext* sharedContext); bool handleParseFailure(const Directives& newDirectives); @@ -90,7 +92,6 @@ class MOZ_STACK_CLASS BytecodeCompiler SourceBufferHolder& sourceBuffer; RootedScope enclosingScope; - bool sourceArgumentsNotIncluded; RootedScriptSource sourceObject; ScriptSource* scriptSource; @@ -130,7 +131,6 @@ BytecodeCompiler::BytecodeCompiler(ExclusiveContext* cx, options(options), sourceBuffer(sourceBuffer), enclosingScope(cx, enclosingScope), - sourceArgumentsNotIncluded(false), sourceObject(cx), scriptSource(nullptr), sourceCompressor(nullptr), @@ -147,12 +147,6 @@ BytecodeCompiler::maybeSetSourceCompressor(SourceCompressionTask* sourceCompress this->sourceCompressor = sourceCompressor; } -void -BytecodeCompiler::setSourceArgumentsNotIncluded() -{ - sourceArgumentsNotIncluded = true; -} - bool BytecodeCompiler::checkLength() { @@ -169,12 +163,12 @@ BytecodeCompiler::checkLength() } bool -BytecodeCompiler::createScriptSource() +BytecodeCompiler::createScriptSource(Maybe<uint32_t> parameterListEnd) { if (!checkLength()) return false; - sourceObject = CreateScriptSourceObject(cx, options); + sourceObject = CreateScriptSourceObject(cx, options, parameterListEnd); if (!sourceObject) return false; @@ -193,9 +187,7 @@ BytecodeCompiler::maybeCompressSource() if (!cx->compartment()->behaviors().discardSource()) { if (options.sourceIsLazy) { scriptSource->setSourceRetrievable(); - } else if (!scriptSource->setSourceCopy(cx, sourceBuffer, sourceArgumentsNotIncluded, - sourceCompressor)) - { + } else if (!scriptSource->setSourceCopy(cx, sourceBuffer, sourceCompressor)) { return false; } } @@ -242,9 +234,9 @@ BytecodeCompiler::createParser() } bool -BytecodeCompiler::createSourceAndParser() +BytecodeCompiler::createSourceAndParser(Maybe<uint32_t> parameterListEnd /* = Nothing() */) { - return createScriptSource() && + return createScriptSource(parameterListEnd) && maybeCompressSource() && createParser(); } @@ -432,18 +424,19 @@ BytecodeCompiler::compileModule() return module; } +// Compile a standalone JS function, which might appear as the value of an +// event handler attribute in an HTML <INPUT> tag, or in a Function() +// constructor. bool -BytecodeCompiler::compileFunctionBody(MutableHandleFunction fun, - Handle<PropertyNameVector> formals, - GeneratorKind generatorKind, - FunctionAsyncKind asyncKind) +BytecodeCompiler::compileStandaloneFunction(MutableHandleFunction fun, + GeneratorKind generatorKind, + FunctionAsyncKind asyncKind, + Maybe<uint32_t> parameterListEnd) { MOZ_ASSERT(fun); MOZ_ASSERT(fun->isTenured()); - fun->setArgCount(formals.length()); - - if (!createSourceAndParser()) + if (!createSourceAndParser(parameterListEnd)) return false; // Speculatively parse using the default directives implied by the context. @@ -454,8 +447,8 @@ BytecodeCompiler::compileFunctionBody(MutableHandleFunction fun, ParseNode* fn; do { Directives newDirectives = directives; - fn = parser->standaloneFunctionBody(fun, enclosingScope, formals, generatorKind, asyncKind, - directives, &newDirectives); + fn = parser->standaloneFunction(fun, enclosingScope, parameterListEnd, generatorKind, + asyncKind, directives, &newDirectives); if (!fn && !handleParseFailure(newDirectives)) return false; } while (!fn); @@ -492,14 +485,15 @@ BytecodeCompiler::sourceObjectPtr() const } ScriptSourceObject* -frontend::CreateScriptSourceObject(ExclusiveContext* cx, const ReadOnlyCompileOptions& options) +frontend::CreateScriptSourceObject(ExclusiveContext* cx, const ReadOnlyCompileOptions& options, + Maybe<uint32_t> parameterListEnd /* = Nothing() */) { ScriptSource* ss = cx->new_<ScriptSource>(); if (!ss) return nullptr; ScriptSourceHolder ssHolder(ss); - if (!ss->initFromOptions(cx, options)) + if (!ss->initFromOptions(cx, options, parameterListEnd)) return nullptr; RootedScriptSource sso(cx, ScriptSourceObject::create(cx, ss)); @@ -676,63 +670,44 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha return bce.emitFunctionScript(pn->pn_body); } -// Compile a JS function body, which might appear as the value of an event -// handler attribute in an HTML <INPUT> tag, or in a Function() constructor. -static bool -CompileFunctionBody(JSContext* cx, MutableHandleFunction fun, const ReadOnlyCompileOptions& options, - Handle<PropertyNameVector> formals, SourceBufferHolder& srcBuf, - HandleScope enclosingScope, GeneratorKind generatorKind, - FunctionAsyncKind asyncKind) +bool +frontend::CompileStandaloneFunction(JSContext* cx, MutableHandleFunction fun, + const ReadOnlyCompileOptions& options, + JS::SourceBufferHolder& srcBuf, + Maybe<uint32_t> parameterListEnd, + HandleScope enclosingScope /* = nullptr */) { - MOZ_ASSERT(!options.isRunOnce); - - // FIXME: make Function pass in two strings and parse them as arguments and - // ProgramElements respectively. + RootedScope scope(cx, enclosingScope); + if (!scope) + scope = &cx->global()->emptyGlobalScope(); - BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, enclosingScope, + BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, scope, TraceLogger_ParserCompileFunction); - compiler.setSourceArgumentsNotIncluded(); - return compiler.compileFunctionBody(fun, formals, generatorKind, asyncKind); + return compiler.compileStandaloneFunction(fun, NotGenerator, SyncFunction, parameterListEnd); } bool -frontend::CompileFunctionBody(JSContext* cx, MutableHandleFunction fun, - const ReadOnlyCompileOptions& options, - Handle<PropertyNameVector> formals, JS::SourceBufferHolder& srcBuf, - HandleScope enclosingScope) -{ - return CompileFunctionBody(cx, fun, options, formals, srcBuf, enclosingScope, NotGenerator, - SyncFunction); -} - -bool -frontend::CompileFunctionBody(JSContext* cx, MutableHandleFunction fun, - const ReadOnlyCompileOptions& options, - Handle<PropertyNameVector> formals, JS::SourceBufferHolder& srcBuf) +frontend::CompileStandaloneGenerator(JSContext* cx, MutableHandleFunction fun, + const ReadOnlyCompileOptions& options, + JS::SourceBufferHolder& srcBuf, + Maybe<uint32_t> parameterListEnd) { RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope()); - return CompileFunctionBody(cx, fun, options, formals, srcBuf, emptyGlobalScope, - NotGenerator, SyncFunction); -} -bool -frontend::CompileStarGeneratorBody(JSContext* cx, MutableHandleFunction fun, - const ReadOnlyCompileOptions& options, - Handle<PropertyNameVector> formals, - JS::SourceBufferHolder& srcBuf) -{ - RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope()); - return CompileFunctionBody(cx, fun, options, formals, srcBuf, emptyGlobalScope, - StarGenerator, SyncFunction); + BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope, + TraceLogger_ParserCompileFunction); + return compiler.compileStandaloneFunction(fun, StarGenerator, SyncFunction, parameterListEnd); } bool -frontend::CompileAsyncFunctionBody(JSContext* cx, MutableHandleFunction fun, - const ReadOnlyCompileOptions& options, - Handle<PropertyNameVector> formals, - JS::SourceBufferHolder& srcBuf) +frontend::CompileStandaloneAsyncFunction(JSContext* cx, MutableHandleFunction fun, + const ReadOnlyCompileOptions& options, + JS::SourceBufferHolder& srcBuf, + Maybe<uint32_t> parameterListEnd) { RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope()); - return CompileFunctionBody(cx, fun, options, formals, srcBuf, emptyGlobalScope, - StarGenerator, AsyncFunction); + + 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 1d86f1160..72e967639 100644 --- a/js/src/frontend/BytecodeCompiler.h +++ b/js/src/frontend/BytecodeCompiler.h @@ -7,6 +7,8 @@ #ifndef frontend_BytecodeCompiler_h #define frontend_BytecodeCompiler_h +#include "mozilla/Maybe.h" + #include "NamespaceImports.h" #include "vm/Scope.h" @@ -51,22 +53,36 @@ CompileModule(ExclusiveContext* cx, const ReadOnlyCompileOptions& options, MOZ_MUST_USE bool CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length); +// +// Compile a single function. The source in srcBuf must match the ECMA-262 +// FunctionExpression production. +// +// If nonzero, parameterListEnd is the offset within srcBuf where the parameter +// list is expected to end. During parsing, if we find that it ends anywhere +// else, it's a SyntaxError. This is used to implement the Function constructor; +// it's how we detect that these weird cases are SyntaxErrors: +// +// Function("/*", "*/x) {") +// Function("x){ if (3", "return x;}") +// MOZ_MUST_USE bool -CompileFunctionBody(JSContext* cx, MutableHandleFunction fun, - const ReadOnlyCompileOptions& options, - Handle<PropertyNameVector> formals, JS::SourceBufferHolder& srcBuf, - HandleScope enclosingScope); +CompileStandaloneFunction(JSContext* cx, MutableHandleFunction fun, + const ReadOnlyCompileOptions& options, + JS::SourceBufferHolder& srcBuf, + mozilla::Maybe<uint32_t> parameterListEnd, + HandleScope enclosingScope = nullptr); -// As above, but defaults to the global lexical scope as the enclosing scope. MOZ_MUST_USE bool -CompileFunctionBody(JSContext* cx, MutableHandleFunction fun, - const ReadOnlyCompileOptions& options, - Handle<PropertyNameVector> formals, JS::SourceBufferHolder& srcBuf); +CompileStandaloneGenerator(JSContext* cx, MutableHandleFunction fun, + const ReadOnlyCompileOptions& options, + JS::SourceBufferHolder& srcBuf, + mozilla::Maybe<uint32_t> parameterListEnd); MOZ_MUST_USE bool -CompileStarGeneratorBody(JSContext* cx, MutableHandleFunction fun, - const ReadOnlyCompileOptions& options, - Handle<PropertyNameVector> formals, JS::SourceBufferHolder& srcBuf); +CompileStandaloneAsyncFunction(JSContext* cx, MutableHandleFunction fun, + const ReadOnlyCompileOptions& options, + JS::SourceBufferHolder& srcBuf, + mozilla::Maybe<uint32_t> parameterListEnd); MOZ_MUST_USE bool CompileAsyncFunctionBody(JSContext* cx, MutableHandleFunction fun, @@ -74,7 +90,8 @@ CompileAsyncFunctionBody(JSContext* cx, MutableHandleFunction fun, Handle<PropertyNameVector> formals, JS::SourceBufferHolder& srcBuf); ScriptSourceObject* -CreateScriptSourceObject(ExclusiveContext* cx, const ReadOnlyCompileOptions& options); +CreateScriptSourceObject(ExclusiveContext* cx, const ReadOnlyCompileOptions& options, + mozilla::Maybe<uint32_t> parameterListEnd = mozilla::Nothing()); /* * True if str consists of an IdentifierStart character, followed by one or diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 49fef2bf9..f42546eb5 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -2245,13 +2245,13 @@ GetYieldHandling(GeneratorKind generatorKind, FunctionAsyncKind asyncKind) template <> ParseNode* -Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, - HandleScope enclosingScope, - Handle<PropertyNameVector> formals, - GeneratorKind generatorKind, - FunctionAsyncKind asyncKind, - Directives inheritedDirectives, - Directives* newDirectives) +Parser<FullParseHandler>::standaloneFunction(HandleFunction fun, + HandleScope enclosingScope, + Maybe<uint32_t> parameterListEnd, + GeneratorKind generatorKind, + FunctionAsyncKind asyncKind, + Directives inheritedDirectives, + Directives* newDirectives) { MOZ_ASSERT(checkOptionsCalled); @@ -2274,25 +2274,14 @@ Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, if (!funpc.init()) return null(); funpc.setIsStandaloneFunctionBody(); - funpc.functionScope().useAsVarScope(&funpc); - - if (formals.length() >= ARGNO_LIMIT) { - report(ParseError, false, null(), JSMSG_TOO_MANY_FUN_ARGS); - return null(); - } - - bool duplicatedParam = false; - for (uint32_t i = 0; i < formals.length(); i++) { - if (!notePositionalFormalParameter(fn, formals[i], false, &duplicatedParam)) - return null(); - } - funbox->hasDuplicateParameters = duplicatedParam; YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind); AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, asyncKind == AsyncFunction); - ParseNode* pn = functionBody(InAllowed, yieldHandling, Statement, StatementListBody); - if (!pn) + if (!functionFormalParametersAndBody(InAllowed, yieldHandling, fn, Statement, + parameterListEnd)) + { return null(); + } TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::Operand)) @@ -2303,15 +2292,7 @@ Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, return null(); } - if (!FoldConstants(context, &pn, this)) - return null(); - - fn->pn_pos.end = pos().end; - - MOZ_ASSERT(fn->pn_body->isKind(PNK_PARAMSBODY)); - fn->pn_body->append(pn); - - if (!finishFunction()) + if (!FoldConstants(context, &fn, this)) return null(); return fn; @@ -3415,7 +3396,8 @@ template <typename ParseHandler> bool Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling, YieldHandling yieldHandling, - Node pn, FunctionSyntaxKind kind) + Node pn, FunctionSyntaxKind kind, + Maybe<uint32_t> parameterListEnd /* = Nothing() */) { // Given a properly initialized parse context, try to parse an actual // function without concern for conversion to strict mode, use of lazy @@ -3447,6 +3429,13 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling, } } + // When parsing something for new Function() we have to make sure to + // only treat a certain part of the source as a parameter list. + if (parameterListEnd.isSome() && parameterListEnd.value() != pos().begin) { + report(ParseError, false, null(), JSMSG_UNEXPECTED_PARAMLIST_END); + return false; + } + // Parse the function body. FunctionBodyType bodyType = StatementListBody; TokenKind tt; @@ -3502,7 +3491,7 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling, report(ParseError, false, null(), JSMSG_CURLY_AFTER_BODY); return false; } - funbox->bufEnd = pos().begin + 1; + funbox->bufEnd = pos().end; } else { #if !JS_HAS_EXPR_CLOSURES MOZ_ASSERT(kind == Arrow); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 0ad4d56a0..b58b021cd 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -1020,12 +1020,12 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter // Parse a module. Node moduleBody(ModuleSharedContext* modulesc); - // Parse a function, given only its body. Used for the Function and - // Generator constructors. - Node standaloneFunctionBody(HandleFunction fun, HandleScope enclosingScope, - Handle<PropertyNameVector> formals, - GeneratorKind generatorKind, FunctionAsyncKind asyncKind, - Directives inheritedDirectives, Directives* newDirectives); + // Parse a function, used for the Function, GeneratorFunction, and + // AsyncFunction constructors. + Node standaloneFunction(HandleFunction fun, HandleScope enclosingScope, + mozilla::Maybe<uint32_t> parameterListEnd, + GeneratorKind generatorKind, FunctionAsyncKind asyncKind, + Directives inheritedDirectives, Directives* newDirectives); // Parse a function, given only its arguments and body. Used for lazily // parsed functions. @@ -1041,7 +1041,9 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter // Parse a function's formal parameters and its body assuming its function // ParseContext is already on the stack. bool functionFormalParametersAndBody(InHandling inHandling, YieldHandling yieldHandling, - Node pn, FunctionSyntaxKind kind); + Node pn, FunctionSyntaxKind kind, + mozilla::Maybe<uint32_t> parameterListEnd = mozilla::Nothing()); + // Determine whether |yield| is a valid name in the current context, or // whether it's prohibited due to strictness, JS version, or occurrence diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index c166ed414..179a7c244 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -118,13 +118,57 @@ IsIdentifier(const CharT* chars, size_t length) return true; } +static uint32_t +GetSingleCodePoint(const char16_t** p, const char16_t* end) +{ + uint32_t codePoint; + if (MOZ_UNLIKELY(unicode::IsLeadSurrogate(**p)) && *p + 1 < end) { + char16_t lead = **p; + char16_t maybeTrail = *(*p + 1); + if (unicode::IsTrailSurrogate(maybeTrail)) { + *p += 2; + return unicode::UTF16Decode(lead, maybeTrail); + } + } + + codePoint = **p; + (*p)++; + return codePoint; +} + +static bool +IsIdentifierMaybeNonBMP(const char16_t* chars, size_t length) +{ + if (IsIdentifier(chars, length)) + return true; + + if (length == 0) + return false; + + const char16_t* p = chars; + const char16_t* end = chars + length; + uint32_t codePoint; + + codePoint = GetSingleCodePoint(&p, end); + if (!unicode::IsIdentifierStart(codePoint)) + return false; + + while (p < end) { + codePoint = GetSingleCodePoint(&p, end); + if (!unicode::IsIdentifierPart(codePoint)) + return false; + } + + return true; +} + bool frontend::IsIdentifier(JSLinearString* str) { JS::AutoCheckCannotGC nogc; return str->hasLatin1Chars() ? ::IsIdentifier(str->latin1Chars(nogc), str->length()) - : ::IsIdentifier(str->twoByteChars(nogc), str->length()); + : ::IsIdentifierMaybeNonBMP(str->twoByteChars(nogc), str->length()); } bool @@ -993,6 +1037,21 @@ IsTokenSane(Token* tp) #endif bool +TokenStream::matchTrailForLeadSurrogate(char16_t lead, char16_t* trail, uint32_t* codePoint) +{ + int32_t maybeTrail = getCharIgnoreEOL(); + if (!unicode::IsTrailSurrogate(maybeTrail)) { + ungetCharIgnoreEOL(maybeTrail); + return false; + } + + if (trail) + *trail = maybeTrail; + *codePoint = unicode::UTF16Decode(lead, maybeTrail); + return true; +} + +bool TokenStream::putIdentInTokenbuf(const char16_t* identStart) { int32_t c; @@ -1003,11 +1062,39 @@ TokenStream::putIdentInTokenbuf(const char16_t* identStart) tokenbuf.clear(); for (;;) { c = getCharIgnoreEOL(); + + if (MOZ_UNLIKELY(unicode::IsLeadSurrogate(c))) { + char16_t trail; + uint32_t codePoint; + if (matchTrailForLeadSurrogate(c, &trail, &codePoint)) { + if (!unicode::IsIdentifierPart(codePoint)) + break; + + if (!tokenbuf.append(c) || !tokenbuf.append(trail)) { + userbuf.setAddressOfNextRawChar(tmp); + return false; + } + continue; + } + } + if (!unicode::IsIdentifierPart(char16_t(c))) { if (c != '\\' || !matchUnicodeEscapeIdent(&qc)) break; + + if (MOZ_UNLIKELY(unicode::IsSupplementary(qc))) { + char16_t lead, trail; + unicode::UTF16Encode(qc, &lead, &trail); + if (!tokenbuf.append(lead) || !tokenbuf.append(trail)) { + userbuf.setAddressOfNextRawChar(tmp); + return false; + } + continue; + } + c = qc; } + if (!tokenbuf.append(c)) { userbuf.setAddressOfNextRawChar(tmp); return false; @@ -1168,12 +1255,23 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) static_assert('_' < 128, "IdentifierStart contains '_', but as !IsUnicodeIDStart('_'), " "ensure that '_' is never handled here"); - if (unicode::IsUnicodeIDStart(c)) { + if (unicode::IsUnicodeIDStart(char16_t(c))) { identStart = userbuf.addressOfNextRawChar() - 1; hadUnicodeEscape = false; goto identifier; } + if (MOZ_UNLIKELY(unicode::IsLeadSurrogate(c))) { + uint32_t codePoint; + if (matchTrailForLeadSurrogate(c, nullptr, &codePoint) && + unicode::IsUnicodeIDStart(codePoint)) + { + identStart = userbuf.addressOfNextRawChar() - 2; + hadUnicodeEscape = false; + goto identifier; + } + } + goto badchar; } @@ -1224,6 +1322,17 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) c = getCharIgnoreEOL(); if (c == EOF) break; + + if (MOZ_UNLIKELY(unicode::IsLeadSurrogate(c))) { + uint32_t codePoint; + if (matchTrailForLeadSurrogate(c, nullptr, &codePoint)) { + if (!unicode::IsIdentifierPart(codePoint)) + break; + + continue; + } + } + if (!unicode::IsIdentifierPart(char16_t(c))) { if (c != '\\' || !matchUnicodeEscapeIdent(&qc)) break; @@ -1318,9 +1427,21 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) } ungetCharIgnoreEOL(c); - if (c != EOF && unicode::IsIdentifierStart(char16_t(c))) { - reportError(JSMSG_IDSTART_AFTER_NUMBER); - goto error; + if (c != EOF) { + if (unicode::IsIdentifierStart(char16_t(c))) { + reportError(JSMSG_IDSTART_AFTER_NUMBER); + goto error; + } + + if (MOZ_UNLIKELY(unicode::IsLeadSurrogate(c))) { + uint32_t codePoint; + if (matchTrailForLeadSurrogate(c, nullptr, &codePoint) && + unicode::IsIdentifierStart(codePoint)) + { + reportError(JSMSG_IDSTART_AFTER_NUMBER); + goto error; + } + } } // Unlike identifiers and strings, numbers cannot contain escaped @@ -1425,9 +1546,21 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) } ungetCharIgnoreEOL(c); - if (c != EOF && unicode::IsIdentifierStart(char16_t(c))) { - reportError(JSMSG_IDSTART_AFTER_NUMBER); - goto error; + if (c != EOF) { + if (unicode::IsIdentifierStart(char16_t(c))) { + reportError(JSMSG_IDSTART_AFTER_NUMBER); + goto error; + } + + if (MOZ_UNLIKELY(unicode::IsLeadSurrogate(c))) { + uint32_t codePoint; + if (matchTrailForLeadSurrogate(c, nullptr, &codePoint) && + unicode::IsIdentifierStart(codePoint)) + { + reportError(JSMSG_IDSTART_AFTER_NUMBER); + goto error; + } + } } double dval; diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index 29dcead62..5d6b4b795 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -952,6 +952,7 @@ class MOZ_STACK_CLASS TokenStream uint32_t peekExtendedUnicodeEscape(uint32_t* codePoint); uint32_t matchUnicodeEscapeIdStart(uint32_t* codePoint); bool matchUnicodeEscapeIdent(uint32_t* codePoint); + bool matchTrailForLeadSurrogate(char16_t lead, char16_t* trail, uint32_t* codePoint); bool peekChars(int n, char16_t* cp); MOZ_MUST_USE bool getDirectives(bool isMultiline, bool shouldWarnDeprecated); diff --git a/js/src/jit-test/tests/basic/destructuring-default.js b/js/src/jit-test/tests/basic/destructuring-default.js index 168977e80..49a908b8a 100644 --- a/js/src/jit-test/tests/basic/destructuring-default.js +++ b/js/src/jit-test/tests/basic/destructuring-default.js @@ -84,10 +84,7 @@ function testArgumentFunction(pattern, input) { 'return [a, b, c, d, e, f];' )(input); } -// XXX: ES6 requires the `Function` constructor to accept arbitrary -// `BindingElement`s as formal parameters. See Bug 1037939. -// Once fixed, please update the assertions below. -assertThrowsInstanceOf(() => testAll(testArgumentFunction), SyntaxError); +testAll(testArgumentFunction); function testThrow(pattern, input) { return new Function('input', diff --git a/js/src/jit-test/tests/basic/destructuring-rest.js b/js/src/jit-test/tests/basic/destructuring-rest.js index f53f07e03..fcb7b79bb 100644 --- a/js/src/jit-test/tests/basic/destructuring-rest.js +++ b/js/src/jit-test/tests/basic/destructuring-rest.js @@ -132,10 +132,9 @@ function testArgumentFunction(pattern, input, binding) { 'return ' + binding )(input); } -// XXX: ES6 requires the `Function` constructor to accept arbitrary -// `BindingElement`s as formal parameters. See Bug 1037939. -// Once fixed, please update the assertions below. -assertThrowsInstanceOf(() => testDeclaration(testArgumentFunction), SyntaxError); +// ES6 requires the `Function` constructor to accept arbitrary +// `BindingElement`s as formal parameters. +testDeclaration(testArgumentFunction); function testThrow(pattern, input, binding) { binding = binding || 'rest'; diff --git a/js/src/jit-test/tests/debug/Script-gc-02.js b/js/src/jit-test/tests/debug/Script-gc-02.js index 33d33dfc1..04dd4b220 100644 --- a/js/src/jit-test/tests/debug/Script-gc-02.js +++ b/js/src/jit-test/tests/debug/Script-gc-02.js @@ -10,5 +10,5 @@ assertEq(arr.length, 10); gc(); for (var i = 0; i < arr.length; i++) - assertEq(arr[i].lineCount, 1); + assertEq(arr[i].lineCount, 3); diff --git a/js/src/jit-test/tests/debug/Script-gc-03.js b/js/src/jit-test/tests/debug/Script-gc-03.js index b2cb70232..30c3e8dbc 100644 --- a/js/src/jit-test/tests/debug/Script-gc-03.js +++ b/js/src/jit-test/tests/debug/Script-gc-03.js @@ -10,6 +10,6 @@ assertEq(arr.length, 100); gc(g); for (var i = 0; i < arr.length; i++) - assertEq(arr[i].lineCount, 1); + assertEq(arr[i].lineCount, 3); gc(); diff --git a/js/src/jit-test/tests/debug/Script-sourceStart-04.js b/js/src/jit-test/tests/debug/Script-sourceStart-04.js index c12e669bf..2aa382b7b 100644 --- a/js/src/jit-test/tests/debug/Script-sourceStart-04.js +++ b/js/src/jit-test/tests/debug/Script-sourceStart-04.js @@ -20,6 +20,6 @@ function test(string, range) { } test("eval('2 * 3')", [0, 5]); -test("new Function('2 * 3')", [0, 5]); -test("new Function('x', 'x * x')", [0, 5]); +test("new Function('2 * 3')", [0, 12]); +test("new Function('x', 'x * x')", [0, 13]); assertEq(count, 6); diff --git a/js/src/jit-test/tests/proxy/testWrapWithProtoIter.js b/js/src/jit-test/tests/proxy/testWrapWithProtoIter.js new file mode 100644 index 000000000..c6854b206 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testWrapWithProtoIter.js @@ -0,0 +1 @@ +[...wrapWithProto(new Int8Array(), new Int8Array())] diff --git a/js/src/jit-test/tests/proxy/testWrapWithProtoTypedArray.js b/js/src/jit-test/tests/proxy/testWrapWithProtoTypedArray.js new file mode 100644 index 000000000..1b805d30a --- /dev/null +++ b/js/src/jit-test/tests/proxy/testWrapWithProtoTypedArray.js @@ -0,0 +1,19 @@ +let a = wrapWithProto(new Int8Array([1, 3, 5, 6, 9]), new Int8Array()); + +assertEq([...a].toString(), "1,3,5,6,9"); +assertEq(a.every(e => e < 100), true); +assertEq(a.filter(e => e % 2 == 1).toString(), "1,3,5,9"); +assertEq(a.find(e => e > 3), 5); +assertEq(a.findIndex(e => e % 2 == 0), 3); +assertEq(a.map(e => e * 10).toString(), "10,30,50,60,90"); +assertEq(a.reduce((a, b) => a + b, ""), "13569"); +assertEq(a.reduceRight((acc, e) => "(" + e + acc + ")", ""), "(1(3(5(6(9)))))"); +assertEq(a.some(e => e % 2 == 0), true); + +let s = ""; +assertEq(a.forEach(e => s += e), undefined); +assertEq(s, "13569"); + +a.sort((a, b) => b - a); +assertEq(a.toString(), "9,6,5,3,1"); + diff --git a/js/src/js.msg b/js/src/js.msg index 2a818056f..8766bfbf5 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -329,6 +329,7 @@ MSG_DEF(JSMSG_TOO_MANY_LOCALS, 0, JSEXN_SYNTAXERR, "too many local varia MSG_DEF(JSMSG_TOO_MANY_YIELDS, 0, JSEXN_SYNTAXERR, "too many yield expressions") MSG_DEF(JSMSG_TOUGH_BREAK, 0, JSEXN_SYNTAXERR, "unlabeled break must be inside loop or switch") MSG_DEF(JSMSG_UNEXPECTED_TOKEN, 2, JSEXN_SYNTAXERR, "expected {0}, got {1}") +MSG_DEF(JSMSG_UNEXPECTED_PARAMLIST_END,0, JSEXN_SYNTAXERR, "unexpected end of function parameter list") MSG_DEF(JSMSG_UNNAMED_CLASS_STMT, 0, JSEXN_SYNTAXERR, "class statement requires a name") MSG_DEF(JSMSG_UNNAMED_FUNCTION_STMT, 0, JSEXN_SYNTAXERR, "function statement requires a name") MSG_DEF(JSMSG_UNTERMINATED_COMMENT, 0, JSEXN_SYNTAXERR, "unterminated comment") diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index ee9c61059..e6fc1f98b 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -11,6 +11,7 @@ #include "jsapi.h" #include "mozilla/FloatingPoint.h" +#include "mozilla/Maybe.h" #include "mozilla/PodOperations.h" #include "mozilla/Sprintf.h" @@ -107,6 +108,7 @@ using namespace js::gc; using mozilla::Maybe; using mozilla::PodCopy; using mozilla::PodZero; +using mozilla::Some; using JS::AutoGCRooter; using JS::ToInt32; @@ -4232,15 +4234,15 @@ JS_GetFunctionScript(JSContext* cx, HandleFunction fun) } /* - * enclosingScope is a static enclosing scope, if any (e.g. a WithScope). If - * the enclosing scope is the global scope, this must be null. + * enclosingScope is a scope, if any (e.g. a WithScope). If the scope is the + * global scope, this must be null. * - * enclosingDynamicScope is a dynamic scope to use, if it's not the global. + * enclosingEnv is an environment to use, if it's not the global. */ static bool CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, - const char* name, unsigned nargs, const char* const* argnames, - SourceBufferHolder& srcBuf, + const char* name, + SourceBufferHolder& srcBuf, uint32_t parameterListEnd, HandleObject enclosingEnv, HandleScope enclosingScope, MutableHandleFunction fun) { @@ -4256,13 +4258,6 @@ CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, return false; } - Rooted<PropertyNameVector> formals(cx, PropertyNameVector(cx)); - for (unsigned i = 0; i < nargs; i++) { - RootedAtom argAtom(cx, Atomize(cx, argnames[i], strlen(argnames[i]))); - if (!argAtom || !formals.append(argAtom->asPropertyName())) - return false; - } - fun.set(NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL, funAtom, /* proto = */ nullptr, gc::AllocKind::FUNCTION, TenuredObject, @@ -4275,7 +4270,45 @@ CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, MOZ_ASSERT_IF(!IsGlobalLexicalEnvironment(enclosingEnv), enclosingScope->hasOnChain(ScopeKind::NonSyntactic)); - if (!frontend::CompileFunctionBody(cx, fun, optionsArg, formals, srcBuf, enclosingScope)) + if (!frontend::CompileStandaloneFunction(cx, fun, optionsArg, srcBuf, + Some(parameterListEnd), enclosingScope)) + { + return false; + } + + return true; +} + +static MOZ_MUST_USE bool +BuildFunctionString(unsigned nargs, const char* const* argnames, + const SourceBufferHolder& srcBuf, StringBuffer* out, + uint32_t* parameterListEnd) +{ + MOZ_ASSERT(out); + MOZ_ASSERT(parameterListEnd); + + if (!out->ensureTwoByteChars()) + return false; + if (!out->append("(")) + return false; + for (unsigned i = 0; i < nargs; i++) { + if (i != 0) { + if (!out->append(", ")) + return false; + } + if (!out->append(argnames[i], strlen(argnames[i]))) + return false; + } + + // Remember the position of ")". + *parameterListEnd = out->length(); + MOZ_ASSERT(FunctionConstructorMedialSigils[0] == ')'); + + if (!out->append(FunctionConstructorMedialSigils)) + return false; + if (!out->append(srcBuf.get(), srcBuf.length())) + return false; + if (!out->append(FunctionConstructorFinalBrace)) return false; return true; @@ -4291,7 +4324,16 @@ JS::CompileFunction(JSContext* cx, AutoObjectVector& envChain, RootedScope scope(cx); if (!CreateNonSyntacticEnvironmentChain(cx, envChain, &env, &scope)) return false; - return CompileFunction(cx, options, name, nargs, argnames, srcBuf, env, scope, fun); + + uint32_t parameterListEnd; + StringBuffer funStr(cx); + if (!BuildFunctionString(nargs, argnames, srcBuf, &funStr, ¶meterListEnd)) + return false; + + size_t newLen = funStr.length(); + SourceBufferHolder newSrcBuf(funStr.stealChars(), newLen, SourceBufferHolder::GiveOwnership); + + return CompileFunction(cx, options, name, newSrcBuf, parameterListEnd, env, scope, fun); } JS_PUBLIC_API(bool) diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index c952441ad..1e1b76d5d 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -12,6 +12,7 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/CheckedInt.h" +#include "mozilla/Maybe.h" #include "mozilla/PodOperations.h" #include "mozilla/Range.h" @@ -60,8 +61,10 @@ using namespace js::gc; using namespace js::frontend; using mozilla::ArrayLength; +using mozilla::Maybe; using mozilla::PodCopy; using mozilla::RangedPtr; +using mozilla::Some; static bool fun_enumerate(JSContext* cx, HandleObject obj) @@ -985,19 +988,19 @@ js::FindBody(JSContext* cx, HandleFunction fun, HandleLinearString src, size_t* } JSString* -js::FunctionToString(JSContext* cx, HandleFunction fun, bool lambdaParen) +js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint) { if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx)) return nullptr; if (IsAsmJSModule(fun)) - return AsmJSModuleToString(cx, fun, !lambdaParen); + return AsmJSModuleToString(cx, fun, !prettyPrint); if (IsAsmJSFunction(fun)) return AsmJSFunctionToString(cx, fun); if (IsWrappedAsyncFunction(fun)) { RootedFunction unwrapped(cx, GetUnwrappedAsyncFunction(fun)); - return FunctionToString(cx, unwrapped, lambdaParen); + return FunctionToString(cx, unwrapped, prettyPrint); } StringBuffer out(cx); @@ -1023,11 +1026,10 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool lambdaParen) bool funIsMethodOrNonArrowLambda = (fun->isLambda() && !fun->isArrow()) || fun->isMethod() || fun->isGetter() || fun->isSetter(); + bool haveSource = fun->isInterpreted() && !fun->isSelfHostedBuiltin(); // If we're not in pretty mode, put parentheses around lambda functions and methods. - if (fun->isInterpreted() && !lambdaParen && funIsMethodOrNonArrowLambda && - !fun->isSelfHostedBuiltin()) - { + if (haveSource && !prettyPrint && funIsMethodOrNonArrowLambda) { if (!out.append("(")) return nullptr; } @@ -1045,7 +1047,6 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool lambdaParen) return nullptr; } - bool haveSource = fun->isInterpreted() && !fun->isSelfHostedBuiltin(); if (haveSource && !script->scriptSource()->hasSourceData() && !JSScript::loadSource(cx, script->scriptSource(), &haveSource)) { @@ -1056,54 +1057,10 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool lambdaParen) if (!src) return nullptr; - // The source data for functions created by calling the Function - // constructor is only the function's body. This depends on the fact, - // asserted below, that in Function("function f() {}"), the inner - // function's sourceStart points to the '(', not the 'f'. - bool funCon = !fun->isArrow() && - script->sourceStart() == 0 && - script->sourceEnd() == script->scriptSource()->length() && - script->scriptSource()->argumentsNotIncluded(); - - // Functions created with the constructor can't be arrow functions or - // expression closures. - MOZ_ASSERT_IF(funCon, !fun->isArrow()); - MOZ_ASSERT_IF(funCon, !fun->isExprBody()); - MOZ_ASSERT_IF(!funCon && !fun->isArrow(), - src->length() > 0 && src->latin1OrTwoByteChar(0) == '('); - - bool buildBody = funCon; - if (buildBody) { - // This function was created with the Function constructor. We don't - // have source for the arguments, so we have to generate that. Part - // of bug 755821 should be cobbling the arguments passed into the - // Function constructor into the source string. - if (!out.append("(")) - return nullptr; - - // Fish out the argument names. - MOZ_ASSERT(script->numArgs() == fun->nargs()); - - BindingIter bi(script); - for (unsigned i = 0; i < fun->nargs(); i++, bi++) { - MOZ_ASSERT(bi.argumentSlot() == i); - if (i && !out.append(", ")) - return nullptr; - if (i == unsigned(fun->nargs() - 1) && fun->hasRest() && !out.append("...")) - return nullptr; - if (!out.append(bi.name())) - return nullptr; - } - if (!out.append(") {\n")) - return nullptr; - } if (!out.append(src)) return nullptr; - if (buildBody) { - if (!out.append("\n}")) - return nullptr; - } - if (!lambdaParen && funIsMethodOrNonArrowLambda) { + + if (!prettyPrint && funIsMethodOrNonArrowLambda) { if (!out.append(")")) return nullptr; } @@ -1114,8 +1071,6 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool lambdaParen) { return nullptr; } - if (!lambdaParen && fun->isLambda() && !fun->isArrow() && !out.append(")")) - return nullptr; } else { MOZ_ASSERT(!fun->isExprBody()); @@ -1657,17 +1612,6 @@ fun_isGenerator(JSContext* cx, unsigned argc, Value* vp) return true; } -/* - * Report "malformed formal parameter" iff no illegal char or similar scanner - * error was already reported. - */ -static bool -OnBadFormal(JSContext* cx) -{ - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_FORMAL); - return false; -} - const JSFunctionSpec js::function_methods[] = { #if JS_HAS_TOSOURCE JS_FN(js_toSource_str, fun_toSource, 0,0), @@ -1681,13 +1625,12 @@ const JSFunctionSpec js::function_methods[] = { JS_FS_END }; +// ES 2017 draft rev 0f10dba4ad18de92d47d421f378233a2eae8f077 19.2.1.1.1. static bool -FunctionConstructor(JSContext* cx, unsigned argc, Value* vp, GeneratorKind generatorKind, +FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generatorKind, FunctionAsyncKind asyncKind) { - CallArgs args = CallArgsFromVp(argc, vp); - - /* Block this call if security callbacks forbid it. */ + // Block this call if security callbacks forbid it. Rooted<GlobalObject*> global(cx, &args.callee().global()); if (!GlobalObject::isRuntimeCodeGenEnabled(cx, global)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_FUNCTION); @@ -1719,82 +1662,65 @@ FunctionConstructor(JSContext* cx, unsigned argc, Value* vp, GeneratorKind gener introducerFilename = maybeScript->scriptSource()->introducerFilename(); CompileOptions options(cx); + // Use line 0 to make the function body starts from line 1. options.setMutedErrors(mutedErrors) - .setFileAndLine(filename, 1) + .setFileAndLine(filename, 0) .setNoScriptRval(false) .setIntroductionInfo(introducerFilename, introductionType, lineno, maybeScript, pcOffset); - Vector<char16_t> paramStr(cx); - RootedString bodyText(cx); + StringBuffer sb(cx); - if (args.length() == 0) { - bodyText = cx->names().empty; - } else { - // Collect the function-argument arguments into one string, separated - // by commas, then make a tokenstream from that string, and scan it to - // get the arguments. We need to throw the full scanner at the - // problem because the argument string may contain comments, newlines, - // destructuring arguments, and similar manner of insanities. ("I have - // a feeling we're not in simple-comma-separated-parameters land any - // more, Toto....") - // - // XXX It'd be better if the parser provided utility methods to parse - // an argument list, and to parse a function body given a parameter - // list. But our parser provides no such pleasant interface now. - unsigned n = args.length() - 1; + if (!sb.append('(')) + return false; - // Convert the parameters-related arguments to strings, and determine - // the length of the string containing the overall parameter list. - mozilla::CheckedInt<uint32_t> paramStrLen = 0; + if (args.length() > 1) { RootedString str(cx); + + // Steps 5-6, 9. + unsigned n = args.length() - 1; + for (unsigned i = 0; i < n; i++) { + // Steps 9.a-b, 9.d.i-ii. str = ToString<CanGC>(cx, args[i]); if (!str) return false; - args[i].setString(str); - paramStrLen += str->length(); - } - - // Tack in space for any combining commas. - if (n > 0) - paramStrLen += n - 1; + // Steps 9.b, 9.d.iii. + if (!sb.append(str)) + return false; - // Check for integer and string-size overflow. - if (!paramStrLen.isValid() || paramStrLen.value() > JSString::MAX_LENGTH) { - ReportAllocationOverflow(cx); - return false; + if (i < args.length() - 2) { + // Step 9.d.iii. + if (!sb.append(", ")) + return false; + } } + } - uint32_t paramsLen = paramStrLen.value(); + // Remember the position of ")". + Maybe<uint32_t> parameterListEnd = Some(uint32_t(sb.length())); + MOZ_ASSERT(FunctionConstructorMedialSigils[0] == ')'); - // Fill a vector with the comma-joined arguments. Careful! This - // string is *not* null-terminated! - MOZ_ASSERT(paramStr.length() == 0); - if (!paramStr.growBy(paramsLen)) { - ReportOutOfMemory(cx); - return false; - } - - char16_t* cp = paramStr.begin(); - for (unsigned i = 0; i < n; i++) { - JSLinearString* argLinear = args[i].toString()->ensureLinear(cx); - if (!argLinear) - return false; + if (!sb.append(FunctionConstructorMedialSigils)) + return false; - CopyChars(cp, *argLinear); - cp += argLinear->length(); + if (args.length() > 0) { + // Steps 7-8, 10. + RootedString body(cx, ToString<CanGC>(cx, args[args.length() - 1])); + if (!body || !sb.append(body)) + return false; + } - if (i + 1 < n) - *cp++ = ','; - } + if (!sb.append(FunctionConstructorFinalBrace)) + return false; - MOZ_ASSERT(cp == paramStr.end()); + // The parser only accepts two byte strings. + if (!sb.ensureTwoByteChars()) + return false; - bodyText = ToString(cx, args[n]); - if (!bodyText) - return false; - } + RootedString functionText(cx, sb.finishString()); + if (!functionText) + return false; /* * NB: (new Function) is not lexically closed by its caller, it's just an @@ -1803,18 +1729,23 @@ FunctionConstructor(JSContext* cx, unsigned argc, Value* vp, GeneratorKind gener * and so would a call to f from another top-level's script or function. */ RootedAtom anonymousAtom(cx, cx->names().anonymous); + + // Step 24. RootedObject proto(cx); - if (isStarGenerator) { - // Unwrapped function of async function should use GeneratorFunction, - // while wrapped function isn't generator. + if (!isAsync) { + if (!GetPrototypeFromCallableConstructor(cx, args, &proto)) + return false; + } + + // Step 4.d, use %Generator% as the fallback prototype. + // Also use %Generator% for the unwrapped function of async functions. + if (!proto && isStarGenerator) { proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, global); if (!proto) return false; - } else { - if (!GetPrototypeFromCallableConstructor(cx, args, &proto)) - return false; } + // Step 25-32 (reordered). RootedObject globalLexical(cx, &global->lexicalEnvironment()); AllocKind allocKind = isAsync ? AllocKind::FUNCTION_EXTENDED : AllocKind::FUNCTION; RootedFunction fun(cx, NewFunctionWithProto(cx, nullptr, 0, @@ -1827,81 +1758,11 @@ FunctionConstructor(JSContext* cx, unsigned argc, Value* vp, GeneratorKind gener if (!JSFunction::setTypeForScriptedFunction(cx, fun)) return false; + // Steps 2.a-b, 3.a-b, 4.a-b, 11-23. AutoStableStringChars stableChars(cx); - if (!stableChars.initTwoByte(cx, bodyText)) + if (!stableChars.initTwoByte(cx, functionText)) return false; - bool hasRest = false; - - Rooted<PropertyNameVector> formals(cx, PropertyNameVector(cx)); - if (args.length() > 1) { - // Initialize a tokenstream to parse the new function's arguments. No - // StrictModeGetter is needed because this TokenStream won't report any - // strict mode errors. Strict mode errors that might be reported here - // (duplicate argument names, etc.) will be detected when we compile - // the function body. - // - // XXX Bug! We have to parse the body first to determine strictness. - // We have to know strictness to parse arguments correctly, in case - // arguments contains a strict mode violation. And we should be - // using full-fledged arguments parsing here, in order to handle - // destructuring and other exotic syntaxes. - AutoKeepAtoms keepAtoms(cx->perThreadData); - TokenStream ts(cx, options, paramStr.begin(), paramStr.length(), - /* strictModeGetter = */ nullptr); - bool yieldIsValidName = ts.versionNumber() < JSVERSION_1_7 && !isStarGenerator; - - // The argument string may be empty or contain no tokens. - TokenKind tt; - if (!ts.getToken(&tt)) - return false; - if (tt != TOK_EOF) { - while (true) { - // Check that it's a name. - if (hasRest) { - ts.reportError(JSMSG_PARAMETER_AFTER_REST); - return false; - } - - if (tt == TOK_YIELD && yieldIsValidName) - tt = TOK_NAME; - - if (tt != TOK_NAME) { - if (tt == TOK_TRIPLEDOT) { - hasRest = true; - if (!ts.getToken(&tt)) - return false; - if (tt == TOK_YIELD && yieldIsValidName) - tt = TOK_NAME; - if (tt != TOK_NAME) { - ts.reportError(JSMSG_NO_REST_NAME); - return false; - } - } else { - return OnBadFormal(cx); - } - } - - if (!formals.append(ts.currentName())) - return false; - - // Get the next token. Stop on end of stream. Otherwise - // insist on a comma, get another name, and iterate. - if (!ts.getToken(&tt)) - return false; - if (tt == TOK_EOF) - break; - if (tt != TOK_COMMA) - return OnBadFormal(cx); - if (!ts.getToken(&tt)) - return false; - } - } - } - - if (hasRest) - fun->setHasRest(); - mozilla::Range<const char16_t> chars = stableChars.twoByteRange(); SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller() ? SourceBufferHolder::GiveOwnership @@ -1909,11 +1770,13 @@ FunctionConstructor(JSContext* cx, unsigned argc, Value* vp, GeneratorKind gener bool ok; SourceBufferHolder srcBuf(chars.begin().get(), chars.length(), ownership); if (isAsync) - ok = frontend::CompileAsyncFunctionBody(cx, &fun, options, formals, srcBuf); + ok = frontend::CompileStandaloneAsyncFunction(cx, &fun, options, srcBuf, parameterListEnd); else if (isStarGenerator) - ok = frontend::CompileStarGeneratorBody(cx, &fun, options, formals, srcBuf); + ok = frontend::CompileStandaloneGenerator(cx, &fun, options, srcBuf, parameterListEnd); else - ok = frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf); + ok = frontend::CompileStandaloneFunction(cx, &fun, options, srcBuf, parameterListEnd); + + // Step 33. args.rval().setObject(*fun); return ok; } @@ -1921,24 +1784,47 @@ FunctionConstructor(JSContext* cx, unsigned argc, Value* vp, GeneratorKind gener bool js::Function(JSContext* cx, unsigned argc, Value* vp) { - return FunctionConstructor(cx, argc, vp, NotGenerator, SyncFunction); + CallArgs args = CallArgsFromVp(argc, vp); + return FunctionConstructor(cx, args, NotGenerator, SyncFunction); } bool js::Generator(JSContext* cx, unsigned argc, Value* vp) { - return FunctionConstructor(cx, argc, vp, StarGenerator, SyncFunction); + CallArgs args = CallArgsFromVp(argc, vp); + return FunctionConstructor(cx, args, StarGenerator, SyncFunction); } bool js::AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - if (!FunctionConstructor(cx, argc, vp, StarGenerator, AsyncFunction)) + + // 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; + // ES2017, draft rev 0f10dba4ad18de92d47d421f378233a2eae8f077 + // 19.2.1.1.1 Runtime Semantics: CreateDynamicFunction, step 24. + RootedObject proto(cx); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + // 19.2.1.1.1, step 4.d, use %AsyncFunctionPrototype% as the fallback. + if (!proto) { + proto = GlobalObject::getOrCreateAsyncFunctionPrototype(cx, cx->global()); + if (!proto) + return false; + } + RootedFunction unwrapped(cx, &args.rval().toObject().as<JSFunction>()); - RootedObject wrapped(cx, WrapAsyncFunction(cx, unwrapped)); + RootedObject wrapped(cx, WrapAsyncFunctionWithProto(cx, unwrapped, proto)); if (!wrapped) return false; diff --git a/js/src/jsfun.h b/js/src/jsfun.h index ef4a603d0..88af5c22d 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -28,6 +28,9 @@ static const uint32_t JSSLOT_BOUND_FUNCTION_TARGET = 2; static const uint32_t JSSLOT_BOUND_FUNCTION_THIS = 3; static const uint32_t JSSLOT_BOUND_FUNCTION_ARGS = 4; +static const char FunctionConstructorMedialSigils[] = ") {\n"; +static const char FunctionConstructorFinalBrace[] = "\n}"; + class JSFunction : public js::NativeObject { public: @@ -816,7 +819,7 @@ JSFunction::getExtendedSlot(size_t which) const namespace js { -JSString* FunctionToString(JSContext* cx, HandleFunction fun, bool lambdaParen); +JSString* FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPring); template<XDRMode mode> bool diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 10821f26a..b568b4b30 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -1689,6 +1689,16 @@ ScriptSource::substringDontDeflate(JSContext* cx, size_t start, size_t stop) return NewStringCopyNDontDeflate<CanGC>(cx, chars, len); } +JSFlatString* +ScriptSource::functionBodyString(JSContext* cx) +{ + MOZ_ASSERT(isFunctionBody()); + + size_t start = parameterListEnd_ + (sizeof(FunctionConstructorMedialSigils) - 1); + size_t stop = length() - (sizeof(FunctionConstructorFinalBrace) - 1); + return substring(cx, start, stop); +} + MOZ_MUST_USE bool ScriptSource::setSource(ExclusiveContext* cx, mozilla::UniquePtr<char16_t[], JS::FreePolicy>&& source, @@ -1740,10 +1750,9 @@ ScriptSource::setCompressedSource(SharedImmutableString&& raw, size_t uncompress bool ScriptSource::setSourceCopy(ExclusiveContext* cx, SourceBufferHolder& srcBuf, - bool argumentsNotIncluded, SourceCompressionTask* task) + SourceCompressionTask* task) { MOZ_ASSERT(!hasSourceData()); - argumentsNotIncluded_ = argumentsNotIncluded; auto& cache = cx->zone()->runtimeFromAnyThread()->sharedImmutableStrings(); auto deduped = cache.getOrCreate(srcBuf.get(), srcBuf.length(), [&]() { @@ -1940,16 +1949,6 @@ ScriptSource::performXDR(XDRState<mode>* xdr) if (!xdr->codeUint32(&compressedLength)) return false; - { - uint8_t argumentsNotIncluded; - if (mode == XDR_ENCODE) - argumentsNotIncluded = argumentsNotIncluded_; - if (!xdr->codeUint8(&argumentsNotIncluded)) - return false; - if (mode == XDR_DECODE) - argumentsNotIncluded_ = argumentsNotIncluded; - } - size_t byteLen = compressedLength ? compressedLength : (len * sizeof(char16_t)); if (mode == XDR_DECODE) { uint8_t* p = xdr->cx()->template pod_malloc<uint8_t>(Max<size_t>(byteLen, 1)); @@ -2074,7 +2073,8 @@ FormatIntroducedFilename(ExclusiveContext* cx, const char* filename, unsigned li } bool -ScriptSource::initFromOptions(ExclusiveContext* cx, const ReadOnlyCompileOptions& options) +ScriptSource::initFromOptions(ExclusiveContext* cx, const ReadOnlyCompileOptions& options, + Maybe<uint32_t> parameterListEnd) { MOZ_ASSERT(!filename_); MOZ_ASSERT(!introducerFilename_); @@ -2083,6 +2083,7 @@ ScriptSource::initFromOptions(ExclusiveContext* cx, const ReadOnlyCompileOptions introductionType_ = options.introductionType; setIntroductionOffset(options.introductionOffset); + parameterListEnd_ = parameterListEnd.isSome() ? parameterListEnd.value() : 0; if (options.hasIntroductionInfo) { MOZ_ASSERT(options.introductionType != nullptr); diff --git a/js/src/jsscript.h b/js/src/jsscript.h index bc8bda83d..ffd4b1e2e 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -10,6 +10,7 @@ #define jsscript_h #include "mozilla/Atomics.h" +#include "mozilla/Maybe.h" #include "mozilla/MemoryReporting.h" #include "mozilla/PodOperations.h" #include "mozilla/Variant.h" @@ -399,6 +400,11 @@ class ScriptSource // scripts. uint32_t introductionOffset_; + // If this source is for Function constructor, the position of ")" after + // parameter list in the source. This is used to get function body. + // 0 for other cases. + uint32_t parameterListEnd_; + // If this ScriptSource was generated by a code-introduction mechanism such // as |eval| or |new Function|, the debugger needs access to the "raw" // filename of the top-level script that contains the eval-ing code. To @@ -428,7 +434,6 @@ class ScriptSource // demand. If sourceRetrievable_ and hasSourceData() are false, it is not // possible to get source at all. bool sourceRetrievable_:1; - bool argumentsNotIncluded_:1; bool hasIntroductionOffset_:1; const char16_t* chunkChars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder, @@ -443,10 +448,10 @@ class ScriptSource sourceMapURL_(nullptr), mutedErrors_(false), introductionOffset_(0), + parameterListEnd_(0), introducerFilename_(nullptr), introductionType_(nullptr), sourceRetrievable_(false), - argumentsNotIncluded_(false), hasIntroductionOffset_(false) { } @@ -461,10 +466,10 @@ class ScriptSource if (--refs == 0) js_delete(this); } - bool initFromOptions(ExclusiveContext* cx, const ReadOnlyCompileOptions& options); + bool initFromOptions(ExclusiveContext* cx, const ReadOnlyCompileOptions& options, + mozilla::Maybe<uint32_t> parameterListEnd = mozilla::Nothing()); bool setSourceCopy(ExclusiveContext* cx, JS::SourceBufferHolder& srcBuf, - bool argumentsNotIncluded, SourceCompressionTask* tok); void setSourceRetrievable() { sourceRetrievable_ = true; } bool sourceRetrievable() const { return sourceRetrievable_; } @@ -492,11 +497,6 @@ class ScriptSource return data.match(LengthMatcher()); } - bool argumentsNotIncluded() const { - MOZ_ASSERT(hasSourceData()); - return argumentsNotIncluded_; - } - // Return a string containing the chars starting at |begin| and ending at // |begin + len|. const char16_t* chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& asp, @@ -504,6 +504,12 @@ class ScriptSource JSFlatString* substring(JSContext* cx, size_t start, size_t stop); JSFlatString* substringDontDeflate(JSContext* cx, size_t start, size_t stop); + + bool isFunctionBody() { + return parameterListEnd_ != 0; + } + JSFlatString* functionBodyString(JSContext* cx); + void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ScriptSourceInfo* info) const; @@ -567,6 +573,10 @@ class ScriptSource introductionOffset_ = offset; hasIntroductionOffset_ = true; } + + uint32_t parameterListEnd() const { + return parameterListEnd_; + } }; class ScriptSourceHolder diff --git a/js/src/tests/Intl/Collator/construct-newtarget.js b/js/src/tests/Intl/Collator/construct-newtarget.js new file mode 100644 index 000000000..5db1abf37 --- /dev/null +++ b/js/src/tests/Intl/Collator/construct-newtarget.js @@ -0,0 +1,81 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +/* 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/. */ + + +// Test subclassing %Intl.Collator% works correctly. +class MyCollator extends Intl.Collator {} + +var obj = new MyCollator(); +assertEq(obj instanceof MyCollator, true); +assertEq(obj instanceof Intl.Collator, true); +assertEq(Object.getPrototypeOf(obj), MyCollator.prototype); + +obj = Reflect.construct(MyCollator, []); +assertEq(obj instanceof MyCollator, true); +assertEq(obj instanceof Intl.Collator, true); +assertEq(Object.getPrototypeOf(obj), MyCollator.prototype); + +obj = Reflect.construct(MyCollator, [], MyCollator); +assertEq(obj instanceof MyCollator, true); +assertEq(obj instanceof Intl.Collator, true); +assertEq(Object.getPrototypeOf(obj), MyCollator.prototype); + +obj = Reflect.construct(MyCollator, [], Intl.Collator); +assertEq(obj instanceof MyCollator, false); +assertEq(obj instanceof Intl.Collator, true); +assertEq(Object.getPrototypeOf(obj), Intl.Collator.prototype); + + +// Set a different constructor as NewTarget. +obj = Reflect.construct(MyCollator, [], Array); +assertEq(obj instanceof MyCollator, false); +assertEq(obj instanceof Intl.Collator, false); +assertEq(obj instanceof Array, true); +assertEq(Object.getPrototypeOf(obj), Array.prototype); + +obj = Reflect.construct(Intl.Collator, [], Array); +assertEq(obj instanceof Intl.Collator, false); +assertEq(obj instanceof Array, true); +assertEq(Object.getPrototypeOf(obj), Array.prototype); + + +// The prototype defaults to %CollatorPrototype% if null. +function NewTargetNullPrototype() {} +NewTargetNullPrototype.prototype = null; + +obj = Reflect.construct(Intl.Collator, [], NewTargetNullPrototype); +assertEq(obj instanceof Intl.Collator, true); +assertEq(Object.getPrototypeOf(obj), Intl.Collator.prototype); + +obj = Reflect.construct(MyCollator, [], NewTargetNullPrototype); +assertEq(obj instanceof MyCollator, false); +assertEq(obj instanceof Intl.Collator, true); +assertEq(Object.getPrototypeOf(obj), Intl.Collator.prototype); + + +// "prototype" property is retrieved exactly once. +var trapLog = [], getLog = []; +var ProxiedConstructor = new Proxy(Intl.Collator, new Proxy({ + get(target, propertyKey, receiver) { + getLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +}, { + get(target, propertyKey, receiver) { + trapLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +})); + +obj = Reflect.construct(Intl.Collator, [], ProxiedConstructor); +assertEqArray(trapLog, ["get"]); +assertEqArray(getLog, ["prototype"]); +assertEq(obj instanceof Intl.Collator, true); +assertEq(Object.getPrototypeOf(obj), Intl.Collator.prototype); + + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/Intl/DateTimeFormat/construct-newtarget.js b/js/src/tests/Intl/DateTimeFormat/construct-newtarget.js new file mode 100644 index 000000000..bbc08c79a --- /dev/null +++ b/js/src/tests/Intl/DateTimeFormat/construct-newtarget.js @@ -0,0 +1,81 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +/* 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/. */ + + +// Test subclassing %Intl.DateTimeFormat% works correctly. +class MyDateTimeFormat extends Intl.DateTimeFormat {} + +var obj = new MyDateTimeFormat(); +assertEq(obj instanceof MyDateTimeFormat, true); +assertEq(obj instanceof Intl.DateTimeFormat, true); +assertEq(Object.getPrototypeOf(obj), MyDateTimeFormat.prototype); + +obj = Reflect.construct(MyDateTimeFormat, []); +assertEq(obj instanceof MyDateTimeFormat, true); +assertEq(obj instanceof Intl.DateTimeFormat, true); +assertEq(Object.getPrototypeOf(obj), MyDateTimeFormat.prototype); + +obj = Reflect.construct(MyDateTimeFormat, [], MyDateTimeFormat); +assertEq(obj instanceof MyDateTimeFormat, true); +assertEq(obj instanceof Intl.DateTimeFormat, true); +assertEq(Object.getPrototypeOf(obj), MyDateTimeFormat.prototype); + +obj = Reflect.construct(MyDateTimeFormat, [], Intl.DateTimeFormat); +assertEq(obj instanceof MyDateTimeFormat, false); +assertEq(obj instanceof Intl.DateTimeFormat, true); +assertEq(Object.getPrototypeOf(obj), Intl.DateTimeFormat.prototype); + + +// Set a different constructor as NewTarget. +obj = Reflect.construct(MyDateTimeFormat, [], Array); +assertEq(obj instanceof MyDateTimeFormat, false); +assertEq(obj instanceof Intl.DateTimeFormat, false); +assertEq(obj instanceof Array, true); +assertEq(Object.getPrototypeOf(obj), Array.prototype); + +obj = Reflect.construct(Intl.DateTimeFormat, [], Array); +assertEq(obj instanceof Intl.DateTimeFormat, false); +assertEq(obj instanceof Array, true); +assertEq(Object.getPrototypeOf(obj), Array.prototype); + + +// The prototype defaults to %DateTimeFormatPrototype% if null. +function NewTargetNullPrototype() {} +NewTargetNullPrototype.prototype = null; + +obj = Reflect.construct(Intl.DateTimeFormat, [], NewTargetNullPrototype); +assertEq(obj instanceof Intl.DateTimeFormat, true); +assertEq(Object.getPrototypeOf(obj), Intl.DateTimeFormat.prototype); + +obj = Reflect.construct(MyDateTimeFormat, [], NewTargetNullPrototype); +assertEq(obj instanceof MyDateTimeFormat, false); +assertEq(obj instanceof Intl.DateTimeFormat, true); +assertEq(Object.getPrototypeOf(obj), Intl.DateTimeFormat.prototype); + + +// "prototype" property is retrieved exactly once. +var trapLog = [], getLog = []; +var ProxiedConstructor = new Proxy(Intl.DateTimeFormat, new Proxy({ + get(target, propertyKey, receiver) { + getLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +}, { + get(target, propertyKey, receiver) { + trapLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +})); + +obj = Reflect.construct(Intl.DateTimeFormat, [], ProxiedConstructor); +assertEqArray(trapLog, ["get"]); +assertEqArray(getLog, ["prototype"]); +assertEq(obj instanceof Intl.DateTimeFormat, true); +assertEq(Object.getPrototypeOf(obj), Intl.DateTimeFormat.prototype); + + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/Intl/NumberFormat/construct-newtarget.js b/js/src/tests/Intl/NumberFormat/construct-newtarget.js new file mode 100644 index 000000000..0053b2737 --- /dev/null +++ b/js/src/tests/Intl/NumberFormat/construct-newtarget.js @@ -0,0 +1,81 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +/* 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/. */ + + +// Test subclassing %Intl.NumberFormat% works correctly. +class MyNumberFormat extends Intl.NumberFormat {} + +var obj = new MyNumberFormat(); +assertEq(obj instanceof MyNumberFormat, true); +assertEq(obj instanceof Intl.NumberFormat, true); +assertEq(Object.getPrototypeOf(obj), MyNumberFormat.prototype); + +obj = Reflect.construct(MyNumberFormat, []); +assertEq(obj instanceof MyNumberFormat, true); +assertEq(obj instanceof Intl.NumberFormat, true); +assertEq(Object.getPrototypeOf(obj), MyNumberFormat.prototype); + +obj = Reflect.construct(MyNumberFormat, [], MyNumberFormat); +assertEq(obj instanceof MyNumberFormat, true); +assertEq(obj instanceof Intl.NumberFormat, true); +assertEq(Object.getPrototypeOf(obj), MyNumberFormat.prototype); + +obj = Reflect.construct(MyNumberFormat, [], Intl.NumberFormat); +assertEq(obj instanceof MyNumberFormat, false); +assertEq(obj instanceof Intl.NumberFormat, true); +assertEq(Object.getPrototypeOf(obj), Intl.NumberFormat.prototype); + + +// Set a different constructor as NewTarget. +obj = Reflect.construct(MyNumberFormat, [], Array); +assertEq(obj instanceof MyNumberFormat, false); +assertEq(obj instanceof Intl.NumberFormat, false); +assertEq(obj instanceof Array, true); +assertEq(Object.getPrototypeOf(obj), Array.prototype); + +obj = Reflect.construct(Intl.NumberFormat, [], Array); +assertEq(obj instanceof Intl.NumberFormat, false); +assertEq(obj instanceof Array, true); +assertEq(Object.getPrototypeOf(obj), Array.prototype); + + +// The prototype defaults to %NumberFormatPrototype% if null. +function NewTargetNullPrototype() {} +NewTargetNullPrototype.prototype = null; + +obj = Reflect.construct(Intl.NumberFormat, [], NewTargetNullPrototype); +assertEq(obj instanceof Intl.NumberFormat, true); +assertEq(Object.getPrototypeOf(obj), Intl.NumberFormat.prototype); + +obj = Reflect.construct(MyNumberFormat, [], NewTargetNullPrototype); +assertEq(obj instanceof MyNumberFormat, false); +assertEq(obj instanceof Intl.NumberFormat, true); +assertEq(Object.getPrototypeOf(obj), Intl.NumberFormat.prototype); + + +// "prototype" property is retrieved exactly once. +var trapLog = [], getLog = []; +var ProxiedConstructor = new Proxy(Intl.NumberFormat, new Proxy({ + get(target, propertyKey, receiver) { + getLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +}, { + get(target, propertyKey, receiver) { + trapLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +})); + +obj = Reflect.construct(Intl.NumberFormat, [], ProxiedConstructor); +assertEqArray(trapLog, ["get"]); +assertEqArray(getLog, ["prototype"]); +assertEq(obj instanceof Intl.NumberFormat, true); +assertEq(Object.getPrototypeOf(obj), Intl.NumberFormat.prototype); + + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_2017/AsyncFunctions/construct-newtarget.js b/js/src/tests/ecma_2017/AsyncFunctions/construct-newtarget.js new file mode 100644 index 000000000..7d75d0c93 --- /dev/null +++ b/js/src/tests/ecma_2017/AsyncFunctions/construct-newtarget.js @@ -0,0 +1,79 @@ +/* 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/. */ + +const AsyncFunction = async function(){}.constructor; + + +// Test subclassing %AsyncFunction% works correctly. +class MyAsync extends AsyncFunction {} + +var fn = new MyAsync(); +assertEq(fn instanceof MyAsync, true); +assertEq(fn instanceof AsyncFunction, true); +assertEq(Object.getPrototypeOf(fn), MyAsync.prototype); + +fn = Reflect.construct(MyAsync, []); +assertEq(fn instanceof MyAsync, true); +assertEq(fn instanceof AsyncFunction, true); +assertEq(Object.getPrototypeOf(fn), MyAsync.prototype); + +fn = Reflect.construct(MyAsync, [], MyAsync); +assertEq(fn instanceof MyAsync, true); +assertEq(fn instanceof AsyncFunction, true); +assertEq(Object.getPrototypeOf(fn), MyAsync.prototype); + +fn = Reflect.construct(MyAsync, [], AsyncFunction); +assertEq(fn instanceof MyAsync, false); +assertEq(fn instanceof AsyncFunction, true); +assertEq(Object.getPrototypeOf(fn), AsyncFunction.prototype); + + +// Set a different constructor as NewTarget. +fn = Reflect.construct(MyAsync, [], Array); +assertEq(fn instanceof MyAsync, false); +assertEq(fn instanceof AsyncFunction, false); +assertEq(Object.getPrototypeOf(fn), Array.prototype); + +fn = Reflect.construct(AsyncFunction, [], Array); +assertEq(fn instanceof AsyncFunction, false); +assertEq(Object.getPrototypeOf(fn), Array.prototype); + + +// The prototype defaults to %AsyncFunctionPrototype% if null. +function NewTargetNullPrototype() {} +NewTargetNullPrototype.prototype = null; + +fn = Reflect.construct(AsyncFunction, [], NewTargetNullPrototype); +assertEq(fn instanceof AsyncFunction, true); +assertEq(Object.getPrototypeOf(fn), AsyncFunction.prototype); + +fn = Reflect.construct(MyAsync, [], NewTargetNullPrototype); +assertEq(fn instanceof MyAsync, false); +assertEq(fn instanceof AsyncFunction, true); +assertEq(Object.getPrototypeOf(fn), AsyncFunction.prototype); + + +// "prototype" property is retrieved exactly once. +var trapLog = [], getLog = []; +var ProxiedConstructor = new Proxy(AsyncFunction, new Proxy({ + get(target, propertyKey, receiver) { + getLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +}, { + get(target, propertyKey, receiver) { + trapLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +})); + +fn = Reflect.construct(AsyncFunction, [], ProxiedConstructor); +assertEqArray(trapLog, ["get"]); +assertEqArray(getLog, ["prototype"]); +assertEq(fn instanceof AsyncFunction, true); +assertEq(Object.getPrototypeOf(fn), AsyncFunction.prototype); + + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_2017/AsyncFunctions/subclass.js b/js/src/tests/ecma_2017/AsyncFunctions/subclass.js new file mode 100644 index 000000000..da20ab19b --- /dev/null +++ b/js/src/tests/ecma_2017/AsyncFunctions/subclass.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!xulRuntime.shell) -- needs drainJobQueue +/* 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/. */ + +const AsyncFunction = async function(){}.constructor; + +class MyAsync extends AsyncFunction {} + +// MyGen inherits from %AsyncFunction%. +assertEq(Object.getPrototypeOf(MyAsync), AsyncFunction); + +// MyGen.prototype inherits from %AsyncFunctionPrototype%. +assertEq(Object.getPrototypeOf(MyAsync.prototype), AsyncFunction.prototype); + +var fn = new MyAsync("return await 'ok';"); + +// fn inherits from MyAsync.prototype. +assertEq(Object.getPrototypeOf(fn), MyAsync.prototype); + +// Ensure the new async function can be executed. +var promise = fn(); + +// promise inherits from %Promise.prototype%. +assertEq(Object.getPrototypeOf(promise), Promise.prototype); + +// Computes the expected result. +assertEventuallyEq(promise, "ok"); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Function/invalid-parameter-list.js b/js/src/tests/ecma_6/Function/invalid-parameter-list.js new file mode 100644 index 000000000..8aae89ef1 --- /dev/null +++ b/js/src/tests/ecma_6/Function/invalid-parameter-list.js @@ -0,0 +1,27 @@ +// This constructor behaves like `Function` without checking +// if the parameter list end is at the expected position. +// We use this to make sure that the tests we use are otherwise +// syntactically correct. +function DumpFunction(...args) { + let code = "function anonymous("; + code += args.slice(0, -1).join(", "); + code += ") {\n"; + code += args[args.length -1]; + code += "\n}"; + eval(code); +} + +const tests = [ + ["/*", "*/) {"], + ["//", ") {"], + ["a = `", "` ) {"], + [") { var x = function (", "} "], + ["x = function (", "}) {"] +]; + +for (const test of tests) { + DumpFunction(...test); + assertThrowsInstanceOf(() => new Function(...test), SyntaxError); +} + +reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/ecma_6/Generators/construct-newtarget.js b/js/src/tests/ecma_6/Generators/construct-newtarget.js new file mode 100644 index 000000000..f2087852b --- /dev/null +++ b/js/src/tests/ecma_6/Generators/construct-newtarget.js @@ -0,0 +1,79 @@ +/* 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/. */ + +const GeneratorFunction = function*(){}.constructor; + + +// Test subclassing %GeneratorFunction% works correctly. +class MyGenerator extends GeneratorFunction {} + +var fn = new MyGenerator(); +assertEq(fn instanceof MyGenerator, true); +assertEq(fn instanceof GeneratorFunction, true); +assertEq(Object.getPrototypeOf(fn), MyGenerator.prototype); + +fn = Reflect.construct(MyGenerator, []); +assertEq(fn instanceof MyGenerator, true); +assertEq(fn instanceof GeneratorFunction, true); +assertEq(Object.getPrototypeOf(fn), MyGenerator.prototype); + +fn = Reflect.construct(MyGenerator, [], MyGenerator); +assertEq(fn instanceof MyGenerator, true); +assertEq(fn instanceof GeneratorFunction, true); +assertEq(Object.getPrototypeOf(fn), MyGenerator.prototype); + +fn = Reflect.construct(MyGenerator, [], GeneratorFunction); +assertEq(fn instanceof MyGenerator, false); +assertEq(fn instanceof GeneratorFunction, true); +assertEq(Object.getPrototypeOf(fn), GeneratorFunction.prototype); + + +// Set a different constructor as NewTarget. +fn = Reflect.construct(MyGenerator, [], Array); +assertEq(fn instanceof MyGenerator, false); +assertEq(fn instanceof GeneratorFunction, false); +assertEq(Object.getPrototypeOf(fn), Array.prototype); + +fn = Reflect.construct(GeneratorFunction, [], Array); +assertEq(fn instanceof GeneratorFunction, false); +assertEq(Object.getPrototypeOf(fn), Array.prototype); + + +// The prototype defaults to %GeneratorFunctionPrototype% if null. +function NewTargetNullPrototype() {} +NewTargetNullPrototype.prototype = null; + +fn = Reflect.construct(GeneratorFunction, [], NewTargetNullPrototype); +assertEq(fn instanceof GeneratorFunction, true); +assertEq(Object.getPrototypeOf(fn), GeneratorFunction.prototype); + +fn = Reflect.construct(MyGenerator, [], NewTargetNullPrototype); +assertEq(fn instanceof MyGenerator, false); +assertEq(fn instanceof GeneratorFunction, true); +assertEq(Object.getPrototypeOf(fn), GeneratorFunction.prototype); + + +// "prototype" property is retrieved exactly once. +var trapLog = [], getLog = []; +var ProxiedConstructor = new Proxy(GeneratorFunction, new Proxy({ + get(target, propertyKey, receiver) { + getLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +}, { + get(target, propertyKey, receiver) { + trapLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +})); + +fn = Reflect.construct(GeneratorFunction, [], ProxiedConstructor); +assertEqArray(trapLog, ["get"]); +assertEqArray(getLog, ["prototype"]); +assertEq(fn instanceof GeneratorFunction, true); +assertEq(Object.getPrototypeOf(fn), GeneratorFunction.prototype); + + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Generators/subclass.js b/js/src/tests/ecma_6/Generators/subclass.js new file mode 100644 index 000000000..f93f4df4d --- /dev/null +++ b/js/src/tests/ecma_6/Generators/subclass.js @@ -0,0 +1,33 @@ +/* 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/. */ + +const GeneratorFunction = function*(){}.constructor; + +class MyGen extends GeneratorFunction {} + +// MyGen inherits from %GeneratorFunction%. +assertEq(Object.getPrototypeOf(MyGen), GeneratorFunction); + +// MyGen.prototype inherits from %Generator%. +assertEq(Object.getPrototypeOf(MyGen.prototype), GeneratorFunction.prototype); + +var fn = new MyGen("yield* [1, 2, 3]"); + +// fn inherits from MyGen.prototype. +assertEq(Object.getPrototypeOf(fn), MyGen.prototype); + +// fn.prototype inherits from %GeneratorPrototype%. +assertEq(Object.getPrototypeOf(fn.prototype), GeneratorFunction.prototype.prototype); + +// Ensure the new generator function can be executed. +var it = fn(); + +// it inherits from fn.prototype. +assertEq(Object.getPrototypeOf(it), fn.prototype); + +// Computes the expected result. +assertEqArray([...it], [1, 2, 3]); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Syntax/identifiers-with-extended-unicode-escape.js b/js/src/tests/ecma_6/Syntax/identifiers-with-extended-unicode-escape.js index 8e0a05fb5..e4b5f4602 100644 --- a/js/src/tests/ecma_6/Syntax/identifiers-with-extended-unicode-escape.js +++ b/js/src/tests/ecma_6/Syntax/identifiers-with-extended-unicode-escape.js @@ -98,7 +98,7 @@ const otherIdContinue = [ 0x19DA, // NEW TAI LUE THAM DIGIT ONE, Gc=No ]; -for (let ident of [...idStart, ...otherIdStart]) { +for (let ident of [...idStart, ...otherIdStart, ...idStartSupplemental]) { for (let count of leadingZeros) { let zeros = "0".repeat(count); eval(` @@ -108,20 +108,13 @@ for (let ident of [...idStart, ...otherIdStart]) { } } -// Move this to the loop above when Bug 1197230 is fixed. -for (let ident of [...idStartSupplemental]) { - for (let zeros of leadingZeros) { - assertThrowsInstanceOf(() => eval(`\\u{${zeros}${ident.toString(16)}}`), SyntaxError); - } -} - for (let ident of [...idContinue, ...idContinueSupplemental, ...otherIdContinue]) { for (let zeros of leadingZeros) { assertThrowsInstanceOf(() => eval(`\\u{${zeros}${ident.toString(16)}}`), SyntaxError); } } -for (let ident of [...idStart, ...otherIdStart, ...idContinue, ...otherIdContinue]) { +for (let ident of [...idStart, ...otherIdStart, ...idContinue, ...otherIdContinue, ...idStartSupplemental, ...idContinueSupplemental]) { for (let zeros of leadingZeros) { eval(` let A\\u{${zeros}${ident.toString(16)}} = 123; @@ -130,13 +123,6 @@ for (let ident of [...idStart, ...otherIdStart, ...idContinue, ...otherIdContinu } } -// Move this to the loop above when Bug 1197230 is fixed. -for (let ident of [...idStartSupplemental, ...idContinueSupplemental]) { - for (let zeros of leadingZeros) { - assertThrowsInstanceOf(() => eval(`\\u{${zeros}${ident.toString(16)}}`), SyntaxError); - } -} - const notIdentifiers = [ 0x0000, // NULL, Gc=Cc diff --git a/js/src/vm/AsyncFunction.cpp b/js/src/vm/AsyncFunction.cpp index bd0b4f32a..1e0c7d7c2 100644 --- a/js/src/vm/AsyncFunction.cpp +++ b/js/src/vm/AsyncFunction.cpp @@ -107,18 +107,15 @@ WrappedAsyncFunction(JSContext* cx, unsigned argc, Value* vp) // the async function's body, replacing `await` with `yield`. `wrapped` is a // function that is visible to the outside, and handles yielded values. JSObject* -js::WrapAsyncFunction(JSContext* cx, HandleFunction unwrapped) +js::WrapAsyncFunctionWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto) { MOZ_ASSERT(unwrapped->isStarGenerator()); + MOZ_ASSERT(proto, "We need an explicit prototype to avoid the default" + "%FunctionPrototype% fallback in NewFunctionWithProto()."); // Create a new function with AsyncFunctionPrototype, reusing the name and // the length of `unwrapped`. - // Step 1. - RootedObject proto(cx, GlobalObject::getOrCreateAsyncFunctionPrototype(cx, cx->global())); - if (!proto) - return nullptr; - RootedAtom funName(cx, unwrapped->name()); uint16_t length; if (!unwrapped->getLength(cx, &length)) @@ -141,6 +138,16 @@ js::WrapAsyncFunction(JSContext* cx, HandleFunction unwrapped) return wrapped; } +JSObject* +js::WrapAsyncFunction(JSContext* cx, HandleFunction unwrapped) +{ + RootedObject proto(cx, GlobalObject::getOrCreateAsyncFunctionPrototype(cx, cx->global())); + if (!proto) + return nullptr; + + return WrapAsyncFunctionWithProto(cx, unwrapped, proto); +} + enum class ResumeKind { Normal, Throw diff --git a/js/src/vm/AsyncFunction.h b/js/src/vm/AsyncFunction.h index ddf81a177..d7f2c1311 100644 --- a/js/src/vm/AsyncFunction.h +++ b/js/src/vm/AsyncFunction.h @@ -22,6 +22,9 @@ bool IsWrappedAsyncFunction(JSFunction* fun); JSObject* +WrapAsyncFunctionWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto); + +JSObject* WrapAsyncFunction(JSContext* cx, HandleFunction unwrapped); MOZ_MUST_USE bool diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index b6bc7d62a..4d181545f 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -6890,8 +6890,13 @@ class DebuggerSourceGetTextMatcher bool hasSourceData = ss->hasSourceData(); if (!ss->hasSourceData() && !JSScript::loadSource(cx_, ss, &hasSourceData)) return nullptr; - return hasSourceData ? ss->substring(cx_, 0, ss->length()) - : NewStringCopyZ<CanGC>(cx_, "[no source]"); + if (!hasSourceData) + return NewStringCopyZ<CanGC>(cx_, "[no source]"); + + if (ss->isFunctionBody()) + return ss->functionBodyString(cx_); + + return ss->substring(cx_, 0, ss->length()); } ReturnType match(Handle<WasmInstanceObject*> wasmInstance) { diff --git a/js/src/vm/Unicode.cpp b/js/src/vm/Unicode.cpp index f4acf8f31..82541c231 100644 --- a/js/src/vm/Unicode.cpp +++ b/js/src/vm/Unicode.cpp @@ -1748,3 +1748,882 @@ const uint8_t unicode::folding_index2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; +bool +js::unicode::IsIdentifierStartNonBMP(uint32_t codePoint) +{ + if (codePoint >= 0x10000 && codePoint <= 0x1000b) + return true; + if (codePoint >= 0x1000d && codePoint <= 0x10026) + return true; + if (codePoint >= 0x10028 && codePoint <= 0x1003a) + return true; + if (codePoint >= 0x1003c && codePoint <= 0x1003d) + return true; + if (codePoint >= 0x1003f && codePoint <= 0x1004d) + return true; + if (codePoint >= 0x10050 && codePoint <= 0x1005d) + return true; + if (codePoint >= 0x10080 && codePoint <= 0x100fa) + return true; + if (codePoint >= 0x10140 && codePoint <= 0x10174) + return true; + if (codePoint >= 0x10280 && codePoint <= 0x1029c) + return true; + if (codePoint >= 0x102a0 && codePoint <= 0x102d0) + return true; + if (codePoint >= 0x10300 && codePoint <= 0x1031f) + return true; + if (codePoint >= 0x10330 && codePoint <= 0x1034a) + return true; + if (codePoint >= 0x10350 && codePoint <= 0x10375) + return true; + if (codePoint >= 0x10380 && codePoint <= 0x1039d) + return true; + if (codePoint >= 0x103a0 && codePoint <= 0x103c3) + return true; + if (codePoint >= 0x103c8 && codePoint <= 0x103cf) + return true; + if (codePoint >= 0x103d1 && codePoint <= 0x103d5) + return true; + if (codePoint >= 0x10400 && codePoint <= 0x1049d) + return true; + if (codePoint >= 0x104b0 && codePoint <= 0x104d3) + return true; + if (codePoint >= 0x104d8 && codePoint <= 0x104fb) + return true; + if (codePoint >= 0x10500 && codePoint <= 0x10527) + return true; + if (codePoint >= 0x10530 && codePoint <= 0x10563) + return true; + if (codePoint >= 0x10600 && codePoint <= 0x10736) + return true; + if (codePoint >= 0x10740 && codePoint <= 0x10755) + return true; + if (codePoint >= 0x10760 && codePoint <= 0x10767) + return true; + if (codePoint >= 0x10800 && codePoint <= 0x10805) + return true; + if (codePoint >= 0x10808 && codePoint <= 0x10808) + return true; + if (codePoint >= 0x1080a && codePoint <= 0x10835) + return true; + if (codePoint >= 0x10837 && codePoint <= 0x10838) + return true; + if (codePoint >= 0x1083c && codePoint <= 0x1083c) + return true; + if (codePoint >= 0x1083f && codePoint <= 0x10855) + return true; + if (codePoint >= 0x10860 && codePoint <= 0x10876) + return true; + if (codePoint >= 0x10880 && codePoint <= 0x1089e) + return true; + if (codePoint >= 0x108e0 && codePoint <= 0x108f2) + return true; + if (codePoint >= 0x108f4 && codePoint <= 0x108f5) + return true; + if (codePoint >= 0x10900 && codePoint <= 0x10915) + return true; + if (codePoint >= 0x10920 && codePoint <= 0x10939) + return true; + if (codePoint >= 0x10980 && codePoint <= 0x109b7) + return true; + if (codePoint >= 0x109be && codePoint <= 0x109bf) + return true; + if (codePoint >= 0x10a00 && codePoint <= 0x10a00) + return true; + if (codePoint >= 0x10a10 && codePoint <= 0x10a13) + return true; + if (codePoint >= 0x10a15 && codePoint <= 0x10a17) + return true; + if (codePoint >= 0x10a19 && codePoint <= 0x10a33) + return true; + if (codePoint >= 0x10a60 && codePoint <= 0x10a7c) + return true; + if (codePoint >= 0x10a80 && codePoint <= 0x10a9c) + return true; + if (codePoint >= 0x10ac0 && codePoint <= 0x10ac7) + return true; + if (codePoint >= 0x10ac9 && codePoint <= 0x10ae4) + return true; + if (codePoint >= 0x10b00 && codePoint <= 0x10b35) + return true; + if (codePoint >= 0x10b40 && codePoint <= 0x10b55) + return true; + if (codePoint >= 0x10b60 && codePoint <= 0x10b72) + return true; + if (codePoint >= 0x10b80 && codePoint <= 0x10b91) + return true; + if (codePoint >= 0x10c00 && codePoint <= 0x10c48) + return true; + if (codePoint >= 0x10c80 && codePoint <= 0x10cb2) + return true; + if (codePoint >= 0x10cc0 && codePoint <= 0x10cf2) + return true; + if (codePoint >= 0x11003 && codePoint <= 0x11037) + return true; + if (codePoint >= 0x11083 && codePoint <= 0x110af) + return true; + if (codePoint >= 0x110d0 && codePoint <= 0x110e8) + return true; + if (codePoint >= 0x11103 && codePoint <= 0x11126) + return true; + if (codePoint >= 0x11150 && codePoint <= 0x11172) + return true; + if (codePoint >= 0x11176 && codePoint <= 0x11176) + return true; + if (codePoint >= 0x11183 && codePoint <= 0x111b2) + return true; + if (codePoint >= 0x111c1 && codePoint <= 0x111c4) + return true; + if (codePoint >= 0x111da && codePoint <= 0x111da) + return true; + if (codePoint >= 0x111dc && codePoint <= 0x111dc) + return true; + if (codePoint >= 0x11200 && codePoint <= 0x11211) + return true; + if (codePoint >= 0x11213 && codePoint <= 0x1122b) + return true; + if (codePoint >= 0x11280 && codePoint <= 0x11286) + return true; + if (codePoint >= 0x11288 && codePoint <= 0x11288) + return true; + if (codePoint >= 0x1128a && codePoint <= 0x1128d) + return true; + if (codePoint >= 0x1128f && codePoint <= 0x1129d) + return true; + if (codePoint >= 0x1129f && codePoint <= 0x112a8) + return true; + if (codePoint >= 0x112b0 && codePoint <= 0x112de) + return true; + if (codePoint >= 0x11305 && codePoint <= 0x1130c) + return true; + if (codePoint >= 0x1130f && codePoint <= 0x11310) + return true; + if (codePoint >= 0x11313 && codePoint <= 0x11328) + return true; + if (codePoint >= 0x1132a && codePoint <= 0x11330) + return true; + if (codePoint >= 0x11332 && codePoint <= 0x11333) + return true; + if (codePoint >= 0x11335 && codePoint <= 0x11339) + return true; + if (codePoint >= 0x1133d && codePoint <= 0x1133d) + return true; + if (codePoint >= 0x11350 && codePoint <= 0x11350) + return true; + if (codePoint >= 0x1135d && codePoint <= 0x11361) + return true; + if (codePoint >= 0x11400 && codePoint <= 0x11434) + return true; + if (codePoint >= 0x11447 && codePoint <= 0x1144a) + return true; + if (codePoint >= 0x11480 && codePoint <= 0x114af) + return true; + if (codePoint >= 0x114c4 && codePoint <= 0x114c5) + return true; + if (codePoint >= 0x114c7 && codePoint <= 0x114c7) + return true; + if (codePoint >= 0x11580 && codePoint <= 0x115ae) + return true; + if (codePoint >= 0x115d8 && codePoint <= 0x115db) + return true; + if (codePoint >= 0x11600 && codePoint <= 0x1162f) + return true; + if (codePoint >= 0x11644 && codePoint <= 0x11644) + return true; + if (codePoint >= 0x11680 && codePoint <= 0x116aa) + return true; + if (codePoint >= 0x11700 && codePoint <= 0x11719) + return true; + if (codePoint >= 0x118a0 && codePoint <= 0x118df) + return true; + if (codePoint >= 0x118ff && codePoint <= 0x118ff) + return true; + if (codePoint >= 0x11ac0 && codePoint <= 0x11af8) + return true; + if (codePoint >= 0x11c00 && codePoint <= 0x11c08) + return true; + if (codePoint >= 0x11c0a && codePoint <= 0x11c2e) + return true; + if (codePoint >= 0x11c40 && codePoint <= 0x11c40) + return true; + if (codePoint >= 0x11c72 && codePoint <= 0x11c8f) + return true; + if (codePoint >= 0x12000 && codePoint <= 0x12399) + return true; + if (codePoint >= 0x12400 && codePoint <= 0x1246e) + return true; + if (codePoint >= 0x12480 && codePoint <= 0x12543) + return true; + if (codePoint >= 0x13000 && codePoint <= 0x1342e) + return true; + if (codePoint >= 0x14400 && codePoint <= 0x14646) + return true; + if (codePoint >= 0x16800 && codePoint <= 0x16a38) + return true; + if (codePoint >= 0x16a40 && codePoint <= 0x16a5e) + return true; + if (codePoint >= 0x16ad0 && codePoint <= 0x16aed) + return true; + if (codePoint >= 0x16b00 && codePoint <= 0x16b2f) + return true; + if (codePoint >= 0x16b40 && codePoint <= 0x16b43) + return true; + if (codePoint >= 0x16b63 && codePoint <= 0x16b77) + return true; + if (codePoint >= 0x16b7d && codePoint <= 0x16b8f) + return true; + if (codePoint >= 0x16f00 && codePoint <= 0x16f44) + return true; + if (codePoint >= 0x16f50 && codePoint <= 0x16f50) + return true; + if (codePoint >= 0x16f93 && codePoint <= 0x16f9f) + return true; + if (codePoint >= 0x16fe0 && codePoint <= 0x16fe0) + return true; + if (codePoint >= 0x17000 && codePoint <= 0x187ec) + return true; + if (codePoint >= 0x18800 && codePoint <= 0x18af2) + return true; + if (codePoint >= 0x1b000 && codePoint <= 0x1b001) + return true; + if (codePoint >= 0x1bc00 && codePoint <= 0x1bc6a) + return true; + if (codePoint >= 0x1bc70 && codePoint <= 0x1bc7c) + return true; + if (codePoint >= 0x1bc80 && codePoint <= 0x1bc88) + return true; + if (codePoint >= 0x1bc90 && codePoint <= 0x1bc99) + return true; + if (codePoint >= 0x1d400 && codePoint <= 0x1d454) + return true; + if (codePoint >= 0x1d456 && codePoint <= 0x1d49c) + return true; + if (codePoint >= 0x1d49e && codePoint <= 0x1d49f) + return true; + if (codePoint >= 0x1d4a2 && codePoint <= 0x1d4a2) + return true; + if (codePoint >= 0x1d4a5 && codePoint <= 0x1d4a6) + return true; + if (codePoint >= 0x1d4a9 && codePoint <= 0x1d4ac) + return true; + if (codePoint >= 0x1d4ae && codePoint <= 0x1d4b9) + return true; + if (codePoint >= 0x1d4bb && codePoint <= 0x1d4bb) + return true; + if (codePoint >= 0x1d4bd && codePoint <= 0x1d4c3) + return true; + if (codePoint >= 0x1d4c5 && codePoint <= 0x1d505) + return true; + if (codePoint >= 0x1d507 && codePoint <= 0x1d50a) + return true; + if (codePoint >= 0x1d50d && codePoint <= 0x1d514) + return true; + if (codePoint >= 0x1d516 && codePoint <= 0x1d51c) + return true; + if (codePoint >= 0x1d51e && codePoint <= 0x1d539) + return true; + if (codePoint >= 0x1d53b && codePoint <= 0x1d53e) + return true; + if (codePoint >= 0x1d540 && codePoint <= 0x1d544) + return true; + if (codePoint >= 0x1d546 && codePoint <= 0x1d546) + return true; + if (codePoint >= 0x1d54a && codePoint <= 0x1d550) + return true; + if (codePoint >= 0x1d552 && codePoint <= 0x1d6a5) + return true; + if (codePoint >= 0x1d6a8 && codePoint <= 0x1d6c0) + return true; + if (codePoint >= 0x1d6c2 && codePoint <= 0x1d6da) + return true; + if (codePoint >= 0x1d6dc && codePoint <= 0x1d6fa) + return true; + if (codePoint >= 0x1d6fc && codePoint <= 0x1d714) + return true; + if (codePoint >= 0x1d716 && codePoint <= 0x1d734) + return true; + if (codePoint >= 0x1d736 && codePoint <= 0x1d74e) + return true; + if (codePoint >= 0x1d750 && codePoint <= 0x1d76e) + return true; + if (codePoint >= 0x1d770 && codePoint <= 0x1d788) + return true; + if (codePoint >= 0x1d78a && codePoint <= 0x1d7a8) + return true; + if (codePoint >= 0x1d7aa && codePoint <= 0x1d7c2) + return true; + if (codePoint >= 0x1d7c4 && codePoint <= 0x1d7cb) + return true; + if (codePoint >= 0x1e800 && codePoint <= 0x1e8c4) + return true; + if (codePoint >= 0x1e900 && codePoint <= 0x1e943) + return true; + if (codePoint >= 0x1ee00 && codePoint <= 0x1ee03) + return true; + if (codePoint >= 0x1ee05 && codePoint <= 0x1ee1f) + return true; + if (codePoint >= 0x1ee21 && codePoint <= 0x1ee22) + return true; + if (codePoint >= 0x1ee24 && codePoint <= 0x1ee24) + return true; + if (codePoint >= 0x1ee27 && codePoint <= 0x1ee27) + return true; + if (codePoint >= 0x1ee29 && codePoint <= 0x1ee32) + return true; + if (codePoint >= 0x1ee34 && codePoint <= 0x1ee37) + return true; + if (codePoint >= 0x1ee39 && codePoint <= 0x1ee39) + return true; + if (codePoint >= 0x1ee3b && codePoint <= 0x1ee3b) + return true; + if (codePoint >= 0x1ee42 && codePoint <= 0x1ee42) + return true; + if (codePoint >= 0x1ee47 && codePoint <= 0x1ee47) + return true; + if (codePoint >= 0x1ee49 && codePoint <= 0x1ee49) + return true; + if (codePoint >= 0x1ee4b && codePoint <= 0x1ee4b) + return true; + if (codePoint >= 0x1ee4d && codePoint <= 0x1ee4f) + return true; + if (codePoint >= 0x1ee51 && codePoint <= 0x1ee52) + return true; + if (codePoint >= 0x1ee54 && codePoint <= 0x1ee54) + return true; + if (codePoint >= 0x1ee57 && codePoint <= 0x1ee57) + return true; + if (codePoint >= 0x1ee59 && codePoint <= 0x1ee59) + return true; + if (codePoint >= 0x1ee5b && codePoint <= 0x1ee5b) + return true; + if (codePoint >= 0x1ee5d && codePoint <= 0x1ee5d) + return true; + if (codePoint >= 0x1ee5f && codePoint <= 0x1ee5f) + return true; + if (codePoint >= 0x1ee61 && codePoint <= 0x1ee62) + return true; + if (codePoint >= 0x1ee64 && codePoint <= 0x1ee64) + return true; + if (codePoint >= 0x1ee67 && codePoint <= 0x1ee6a) + return true; + if (codePoint >= 0x1ee6c && codePoint <= 0x1ee72) + return true; + if (codePoint >= 0x1ee74 && codePoint <= 0x1ee77) + return true; + if (codePoint >= 0x1ee79 && codePoint <= 0x1ee7c) + return true; + if (codePoint >= 0x1ee7e && codePoint <= 0x1ee7e) + return true; + if (codePoint >= 0x1ee80 && codePoint <= 0x1ee89) + return true; + if (codePoint >= 0x1ee8b && codePoint <= 0x1ee9b) + return true; + if (codePoint >= 0x1eea1 && codePoint <= 0x1eea3) + return true; + if (codePoint >= 0x1eea5 && codePoint <= 0x1eea9) + return true; + if (codePoint >= 0x1eeab && codePoint <= 0x1eebb) + return true; + if (codePoint >= 0x20000 && codePoint <= 0x2a6d6) + return true; + if (codePoint >= 0x2a700 && codePoint <= 0x2b734) + return true; + if (codePoint >= 0x2b740 && codePoint <= 0x2b81d) + return true; + if (codePoint >= 0x2b820 && codePoint <= 0x2cea1) + return true; + if (codePoint >= 0x2f800 && codePoint <= 0x2fa1d) + return true; + return false; +} + +bool +js::unicode::IsIdentifierPartNonBMP(uint32_t codePoint) +{ + if (codePoint >= 0x10000 && codePoint <= 0x1000b) + return true; + if (codePoint >= 0x1000d && codePoint <= 0x10026) + return true; + if (codePoint >= 0x10028 && codePoint <= 0x1003a) + return true; + if (codePoint >= 0x1003c && codePoint <= 0x1003d) + return true; + if (codePoint >= 0x1003f && codePoint <= 0x1004d) + return true; + if (codePoint >= 0x10050 && codePoint <= 0x1005d) + return true; + if (codePoint >= 0x10080 && codePoint <= 0x100fa) + return true; + if (codePoint >= 0x10140 && codePoint <= 0x10174) + return true; + if (codePoint >= 0x101fd && codePoint <= 0x101fd) + return true; + if (codePoint >= 0x10280 && codePoint <= 0x1029c) + return true; + if (codePoint >= 0x102a0 && codePoint <= 0x102d0) + return true; + if (codePoint >= 0x102e0 && codePoint <= 0x102e0) + return true; + if (codePoint >= 0x10300 && codePoint <= 0x1031f) + return true; + if (codePoint >= 0x10330 && codePoint <= 0x1034a) + return true; + if (codePoint >= 0x10350 && codePoint <= 0x1037a) + return true; + if (codePoint >= 0x10380 && codePoint <= 0x1039d) + return true; + if (codePoint >= 0x103a0 && codePoint <= 0x103c3) + return true; + if (codePoint >= 0x103c8 && codePoint <= 0x103cf) + return true; + if (codePoint >= 0x103d1 && codePoint <= 0x103d5) + return true; + if (codePoint >= 0x10400 && codePoint <= 0x1049d) + return true; + if (codePoint >= 0x104a0 && codePoint <= 0x104a9) + return true; + if (codePoint >= 0x104b0 && codePoint <= 0x104d3) + return true; + if (codePoint >= 0x104d8 && codePoint <= 0x104fb) + return true; + if (codePoint >= 0x10500 && codePoint <= 0x10527) + return true; + if (codePoint >= 0x10530 && codePoint <= 0x10563) + return true; + if (codePoint >= 0x10600 && codePoint <= 0x10736) + return true; + if (codePoint >= 0x10740 && codePoint <= 0x10755) + return true; + if (codePoint >= 0x10760 && codePoint <= 0x10767) + return true; + if (codePoint >= 0x10800 && codePoint <= 0x10805) + return true; + if (codePoint >= 0x10808 && codePoint <= 0x10808) + return true; + if (codePoint >= 0x1080a && codePoint <= 0x10835) + return true; + if (codePoint >= 0x10837 && codePoint <= 0x10838) + return true; + if (codePoint >= 0x1083c && codePoint <= 0x1083c) + return true; + if (codePoint >= 0x1083f && codePoint <= 0x10855) + return true; + if (codePoint >= 0x10860 && codePoint <= 0x10876) + return true; + if (codePoint >= 0x10880 && codePoint <= 0x1089e) + return true; + if (codePoint >= 0x108e0 && codePoint <= 0x108f2) + return true; + if (codePoint >= 0x108f4 && codePoint <= 0x108f5) + return true; + if (codePoint >= 0x10900 && codePoint <= 0x10915) + return true; + if (codePoint >= 0x10920 && codePoint <= 0x10939) + return true; + if (codePoint >= 0x10980 && codePoint <= 0x109b7) + return true; + if (codePoint >= 0x109be && codePoint <= 0x109bf) + return true; + if (codePoint >= 0x10a00 && codePoint <= 0x10a03) + return true; + if (codePoint >= 0x10a05 && codePoint <= 0x10a06) + return true; + if (codePoint >= 0x10a0c && codePoint <= 0x10a13) + return true; + if (codePoint >= 0x10a15 && codePoint <= 0x10a17) + return true; + if (codePoint >= 0x10a19 && codePoint <= 0x10a33) + return true; + if (codePoint >= 0x10a38 && codePoint <= 0x10a3a) + return true; + if (codePoint >= 0x10a3f && codePoint <= 0x10a3f) + return true; + if (codePoint >= 0x10a60 && codePoint <= 0x10a7c) + return true; + if (codePoint >= 0x10a80 && codePoint <= 0x10a9c) + return true; + if (codePoint >= 0x10ac0 && codePoint <= 0x10ac7) + return true; + if (codePoint >= 0x10ac9 && codePoint <= 0x10ae6) + return true; + if (codePoint >= 0x10b00 && codePoint <= 0x10b35) + return true; + if (codePoint >= 0x10b40 && codePoint <= 0x10b55) + return true; + if (codePoint >= 0x10b60 && codePoint <= 0x10b72) + return true; + if (codePoint >= 0x10b80 && codePoint <= 0x10b91) + return true; + if (codePoint >= 0x10c00 && codePoint <= 0x10c48) + return true; + if (codePoint >= 0x10c80 && codePoint <= 0x10cb2) + return true; + if (codePoint >= 0x10cc0 && codePoint <= 0x10cf2) + return true; + if (codePoint >= 0x11000 && codePoint <= 0x11046) + return true; + if (codePoint >= 0x11066 && codePoint <= 0x1106f) + return true; + if (codePoint >= 0x1107f && codePoint <= 0x110ba) + return true; + if (codePoint >= 0x110d0 && codePoint <= 0x110e8) + return true; + if (codePoint >= 0x110f0 && codePoint <= 0x110f9) + return true; + if (codePoint >= 0x11100 && codePoint <= 0x11134) + return true; + if (codePoint >= 0x11136 && codePoint <= 0x1113f) + return true; + if (codePoint >= 0x11150 && codePoint <= 0x11173) + return true; + if (codePoint >= 0x11176 && codePoint <= 0x11176) + return true; + if (codePoint >= 0x11180 && codePoint <= 0x111c4) + return true; + if (codePoint >= 0x111ca && codePoint <= 0x111cc) + return true; + if (codePoint >= 0x111d0 && codePoint <= 0x111da) + return true; + if (codePoint >= 0x111dc && codePoint <= 0x111dc) + return true; + if (codePoint >= 0x11200 && codePoint <= 0x11211) + return true; + if (codePoint >= 0x11213 && codePoint <= 0x11237) + return true; + if (codePoint >= 0x1123e && codePoint <= 0x1123e) + return true; + if (codePoint >= 0x11280 && codePoint <= 0x11286) + return true; + if (codePoint >= 0x11288 && codePoint <= 0x11288) + return true; + if (codePoint >= 0x1128a && codePoint <= 0x1128d) + return true; + if (codePoint >= 0x1128f && codePoint <= 0x1129d) + return true; + if (codePoint >= 0x1129f && codePoint <= 0x112a8) + return true; + if (codePoint >= 0x112b0 && codePoint <= 0x112ea) + return true; + if (codePoint >= 0x112f0 && codePoint <= 0x112f9) + return true; + if (codePoint >= 0x11300 && codePoint <= 0x11303) + return true; + if (codePoint >= 0x11305 && codePoint <= 0x1130c) + return true; + if (codePoint >= 0x1130f && codePoint <= 0x11310) + return true; + if (codePoint >= 0x11313 && codePoint <= 0x11328) + return true; + if (codePoint >= 0x1132a && codePoint <= 0x11330) + return true; + if (codePoint >= 0x11332 && codePoint <= 0x11333) + return true; + if (codePoint >= 0x11335 && codePoint <= 0x11339) + return true; + if (codePoint >= 0x1133c && codePoint <= 0x11344) + return true; + if (codePoint >= 0x11347 && codePoint <= 0x11348) + return true; + if (codePoint >= 0x1134b && codePoint <= 0x1134d) + return true; + if (codePoint >= 0x11350 && codePoint <= 0x11350) + return true; + if (codePoint >= 0x11357 && codePoint <= 0x11357) + return true; + if (codePoint >= 0x1135d && codePoint <= 0x11363) + return true; + if (codePoint >= 0x11366 && codePoint <= 0x1136c) + return true; + if (codePoint >= 0x11370 && codePoint <= 0x11374) + return true; + if (codePoint >= 0x11400 && codePoint <= 0x1144a) + return true; + if (codePoint >= 0x11450 && codePoint <= 0x11459) + return true; + if (codePoint >= 0x11480 && codePoint <= 0x114c5) + return true; + if (codePoint >= 0x114c7 && codePoint <= 0x114c7) + return true; + if (codePoint >= 0x114d0 && codePoint <= 0x114d9) + return true; + if (codePoint >= 0x11580 && codePoint <= 0x115b5) + return true; + if (codePoint >= 0x115b8 && codePoint <= 0x115c0) + return true; + if (codePoint >= 0x115d8 && codePoint <= 0x115dd) + return true; + if (codePoint >= 0x11600 && codePoint <= 0x11640) + return true; + if (codePoint >= 0x11644 && codePoint <= 0x11644) + return true; + if (codePoint >= 0x11650 && codePoint <= 0x11659) + return true; + if (codePoint >= 0x11680 && codePoint <= 0x116b7) + return true; + if (codePoint >= 0x116c0 && codePoint <= 0x116c9) + return true; + if (codePoint >= 0x11700 && codePoint <= 0x11719) + return true; + if (codePoint >= 0x1171d && codePoint <= 0x1172b) + return true; + if (codePoint >= 0x11730 && codePoint <= 0x11739) + return true; + if (codePoint >= 0x118a0 && codePoint <= 0x118e9) + return true; + if (codePoint >= 0x118ff && codePoint <= 0x118ff) + return true; + if (codePoint >= 0x11ac0 && codePoint <= 0x11af8) + return true; + if (codePoint >= 0x11c00 && codePoint <= 0x11c08) + return true; + if (codePoint >= 0x11c0a && codePoint <= 0x11c36) + return true; + if (codePoint >= 0x11c38 && codePoint <= 0x11c40) + return true; + if (codePoint >= 0x11c50 && codePoint <= 0x11c59) + return true; + if (codePoint >= 0x11c72 && codePoint <= 0x11c8f) + return true; + if (codePoint >= 0x11c92 && codePoint <= 0x11ca7) + return true; + if (codePoint >= 0x11ca9 && codePoint <= 0x11cb6) + return true; + if (codePoint >= 0x12000 && codePoint <= 0x12399) + return true; + if (codePoint >= 0x12400 && codePoint <= 0x1246e) + return true; + if (codePoint >= 0x12480 && codePoint <= 0x12543) + return true; + if (codePoint >= 0x13000 && codePoint <= 0x1342e) + return true; + if (codePoint >= 0x14400 && codePoint <= 0x14646) + return true; + if (codePoint >= 0x16800 && codePoint <= 0x16a38) + return true; + if (codePoint >= 0x16a40 && codePoint <= 0x16a5e) + return true; + if (codePoint >= 0x16a60 && codePoint <= 0x16a69) + return true; + if (codePoint >= 0x16ad0 && codePoint <= 0x16aed) + return true; + if (codePoint >= 0x16af0 && codePoint <= 0x16af4) + return true; + if (codePoint >= 0x16b00 && codePoint <= 0x16b36) + return true; + if (codePoint >= 0x16b40 && codePoint <= 0x16b43) + return true; + if (codePoint >= 0x16b50 && codePoint <= 0x16b59) + return true; + if (codePoint >= 0x16b63 && codePoint <= 0x16b77) + return true; + if (codePoint >= 0x16b7d && codePoint <= 0x16b8f) + return true; + if (codePoint >= 0x16f00 && codePoint <= 0x16f44) + return true; + if (codePoint >= 0x16f50 && codePoint <= 0x16f7e) + return true; + if (codePoint >= 0x16f8f && codePoint <= 0x16f9f) + return true; + if (codePoint >= 0x16fe0 && codePoint <= 0x16fe0) + return true; + if (codePoint >= 0x17000 && codePoint <= 0x187ec) + return true; + if (codePoint >= 0x18800 && codePoint <= 0x18af2) + return true; + if (codePoint >= 0x1b000 && codePoint <= 0x1b001) + return true; + if (codePoint >= 0x1bc00 && codePoint <= 0x1bc6a) + return true; + if (codePoint >= 0x1bc70 && codePoint <= 0x1bc7c) + return true; + if (codePoint >= 0x1bc80 && codePoint <= 0x1bc88) + return true; + if (codePoint >= 0x1bc90 && codePoint <= 0x1bc99) + return true; + if (codePoint >= 0x1bc9d && codePoint <= 0x1bc9e) + return true; + if (codePoint >= 0x1d165 && codePoint <= 0x1d169) + return true; + if (codePoint >= 0x1d16d && codePoint <= 0x1d172) + return true; + if (codePoint >= 0x1d17b && codePoint <= 0x1d182) + return true; + if (codePoint >= 0x1d185 && codePoint <= 0x1d18b) + return true; + if (codePoint >= 0x1d1aa && codePoint <= 0x1d1ad) + return true; + if (codePoint >= 0x1d242 && codePoint <= 0x1d244) + return true; + if (codePoint >= 0x1d400 && codePoint <= 0x1d454) + return true; + if (codePoint >= 0x1d456 && codePoint <= 0x1d49c) + return true; + if (codePoint >= 0x1d49e && codePoint <= 0x1d49f) + return true; + if (codePoint >= 0x1d4a2 && codePoint <= 0x1d4a2) + return true; + if (codePoint >= 0x1d4a5 && codePoint <= 0x1d4a6) + return true; + if (codePoint >= 0x1d4a9 && codePoint <= 0x1d4ac) + return true; + if (codePoint >= 0x1d4ae && codePoint <= 0x1d4b9) + return true; + if (codePoint >= 0x1d4bb && codePoint <= 0x1d4bb) + return true; + if (codePoint >= 0x1d4bd && codePoint <= 0x1d4c3) + return true; + if (codePoint >= 0x1d4c5 && codePoint <= 0x1d505) + return true; + if (codePoint >= 0x1d507 && codePoint <= 0x1d50a) + return true; + if (codePoint >= 0x1d50d && codePoint <= 0x1d514) + return true; + if (codePoint >= 0x1d516 && codePoint <= 0x1d51c) + return true; + if (codePoint >= 0x1d51e && codePoint <= 0x1d539) + return true; + if (codePoint >= 0x1d53b && codePoint <= 0x1d53e) + return true; + if (codePoint >= 0x1d540 && codePoint <= 0x1d544) + return true; + if (codePoint >= 0x1d546 && codePoint <= 0x1d546) + return true; + if (codePoint >= 0x1d54a && codePoint <= 0x1d550) + return true; + if (codePoint >= 0x1d552 && codePoint <= 0x1d6a5) + return true; + if (codePoint >= 0x1d6a8 && codePoint <= 0x1d6c0) + return true; + if (codePoint >= 0x1d6c2 && codePoint <= 0x1d6da) + return true; + if (codePoint >= 0x1d6dc && codePoint <= 0x1d6fa) + return true; + if (codePoint >= 0x1d6fc && codePoint <= 0x1d714) + return true; + if (codePoint >= 0x1d716 && codePoint <= 0x1d734) + return true; + if (codePoint >= 0x1d736 && codePoint <= 0x1d74e) + return true; + if (codePoint >= 0x1d750 && codePoint <= 0x1d76e) + return true; + if (codePoint >= 0x1d770 && codePoint <= 0x1d788) + return true; + if (codePoint >= 0x1d78a && codePoint <= 0x1d7a8) + return true; + if (codePoint >= 0x1d7aa && codePoint <= 0x1d7c2) + return true; + if (codePoint >= 0x1d7c4 && codePoint <= 0x1d7cb) + return true; + if (codePoint >= 0x1d7ce && codePoint <= 0x1d7ff) + return true; + if (codePoint >= 0x1da00 && codePoint <= 0x1da36) + return true; + if (codePoint >= 0x1da3b && codePoint <= 0x1da6c) + return true; + if (codePoint >= 0x1da75 && codePoint <= 0x1da75) + return true; + if (codePoint >= 0x1da84 && codePoint <= 0x1da84) + return true; + if (codePoint >= 0x1da9b && codePoint <= 0x1da9f) + return true; + if (codePoint >= 0x1daa1 && codePoint <= 0x1daaf) + return true; + if (codePoint >= 0x1e000 && codePoint <= 0x1e006) + return true; + if (codePoint >= 0x1e008 && codePoint <= 0x1e018) + return true; + if (codePoint >= 0x1e01b && codePoint <= 0x1e021) + return true; + if (codePoint >= 0x1e023 && codePoint <= 0x1e024) + return true; + if (codePoint >= 0x1e026 && codePoint <= 0x1e02a) + return true; + if (codePoint >= 0x1e800 && codePoint <= 0x1e8c4) + return true; + if (codePoint >= 0x1e8d0 && codePoint <= 0x1e8d6) + return true; + if (codePoint >= 0x1e900 && codePoint <= 0x1e94a) + return true; + if (codePoint >= 0x1e950 && codePoint <= 0x1e959) + return true; + if (codePoint >= 0x1ee00 && codePoint <= 0x1ee03) + return true; + if (codePoint >= 0x1ee05 && codePoint <= 0x1ee1f) + return true; + if (codePoint >= 0x1ee21 && codePoint <= 0x1ee22) + return true; + if (codePoint >= 0x1ee24 && codePoint <= 0x1ee24) + return true; + if (codePoint >= 0x1ee27 && codePoint <= 0x1ee27) + return true; + if (codePoint >= 0x1ee29 && codePoint <= 0x1ee32) + return true; + if (codePoint >= 0x1ee34 && codePoint <= 0x1ee37) + return true; + if (codePoint >= 0x1ee39 && codePoint <= 0x1ee39) + return true; + if (codePoint >= 0x1ee3b && codePoint <= 0x1ee3b) + return true; + if (codePoint >= 0x1ee42 && codePoint <= 0x1ee42) + return true; + if (codePoint >= 0x1ee47 && codePoint <= 0x1ee47) + return true; + if (codePoint >= 0x1ee49 && codePoint <= 0x1ee49) + return true; + if (codePoint >= 0x1ee4b && codePoint <= 0x1ee4b) + return true; + if (codePoint >= 0x1ee4d && codePoint <= 0x1ee4f) + return true; + if (codePoint >= 0x1ee51 && codePoint <= 0x1ee52) + return true; + if (codePoint >= 0x1ee54 && codePoint <= 0x1ee54) + return true; + if (codePoint >= 0x1ee57 && codePoint <= 0x1ee57) + return true; + if (codePoint >= 0x1ee59 && codePoint <= 0x1ee59) + return true; + if (codePoint >= 0x1ee5b && codePoint <= 0x1ee5b) + return true; + if (codePoint >= 0x1ee5d && codePoint <= 0x1ee5d) + return true; + if (codePoint >= 0x1ee5f && codePoint <= 0x1ee5f) + return true; + if (codePoint >= 0x1ee61 && codePoint <= 0x1ee62) + return true; + if (codePoint >= 0x1ee64 && codePoint <= 0x1ee64) + return true; + if (codePoint >= 0x1ee67 && codePoint <= 0x1ee6a) + return true; + if (codePoint >= 0x1ee6c && codePoint <= 0x1ee72) + return true; + if (codePoint >= 0x1ee74 && codePoint <= 0x1ee77) + return true; + if (codePoint >= 0x1ee79 && codePoint <= 0x1ee7c) + return true; + if (codePoint >= 0x1ee7e && codePoint <= 0x1ee7e) + return true; + if (codePoint >= 0x1ee80 && codePoint <= 0x1ee89) + return true; + if (codePoint >= 0x1ee8b && codePoint <= 0x1ee9b) + return true; + if (codePoint >= 0x1eea1 && codePoint <= 0x1eea3) + return true; + if (codePoint >= 0x1eea5 && codePoint <= 0x1eea9) + return true; + if (codePoint >= 0x1eeab && codePoint <= 0x1eebb) + return true; + if (codePoint >= 0x20000 && codePoint <= 0x2a6d6) + return true; + if (codePoint >= 0x2a700 && codePoint <= 0x2b734) + return true; + if (codePoint >= 0x2b740 && codePoint <= 0x2b81d) + return true; + if (codePoint >= 0x2b820 && codePoint <= 0x2cea1) + return true; + if (codePoint >= 0x2f800 && codePoint <= 0x2fa1d) + return true; + if (codePoint >= 0xe0100 && codePoint <= 0xe01ef) + return true; + return false; +} diff --git a/js/src/vm/Unicode.h b/js/src/vm/Unicode.h index 8b538d06d..bdac848fb 100644 --- a/js/src/vm/Unicode.h +++ b/js/src/vm/Unicode.h @@ -143,11 +143,15 @@ IsIdentifierStart(char16_t ch) return CharInfo(ch).isUnicodeIDStart(); } +bool +IsIdentifierStartNonBMP(uint32_t codePoint); + inline bool IsIdentifierStart(uint32_t codePoint) { - // TODO: Supplemental code points not yet supported (bug 1197230). - return codePoint <= UTF16Max && IsIdentifierStart(char16_t(codePoint)); + if (MOZ_UNLIKELY(codePoint > UTF16Max)) + return IsIdentifierStartNonBMP(codePoint); + return IsIdentifierStart(char16_t(codePoint)); } inline bool @@ -170,11 +174,16 @@ IsIdentifierPart(char16_t ch) return CharInfo(ch).isUnicodeIDContinue(); } + +bool +IsIdentifierPartNonBMP(uint32_t codePoint); + inline bool IsIdentifierPart(uint32_t codePoint) { - // TODO: Supplemental code points not yet supported (bug 1197230). - return codePoint <= UTF16Max && IsIdentifierPart(char16_t(codePoint)); + if (MOZ_UNLIKELY(codePoint > UTF16Max)) + return IsIdentifierPartNonBMP(codePoint); + return IsIdentifierPart(char16_t(codePoint)); } inline bool @@ -183,6 +192,17 @@ IsUnicodeIDStart(char16_t ch) return CharInfo(ch).isUnicodeIDStart(); } +bool +IsUnicodeIDStartNonBMP(uint32_t codePoint); + +inline bool +IsUnicodeIDStart(uint32_t codePoint) +{ + if (MOZ_UNLIKELY(codePoint > UTF16Max)) + return IsIdentifierStartNonBMP(codePoint); + return IsUnicodeIDStart(char16_t(codePoint)); +} + inline bool IsSpace(char16_t ch) { diff --git a/js/src/vm/UnicodeNonBMP.h b/js/src/vm/UnicodeNonBMP.h index 6cc64cde4..f99e227cd 100644 --- a/js/src/vm/UnicodeNonBMP.h +++ b/js/src/vm/UnicodeNonBMP.h @@ -10,6 +10,16 @@ #ifndef vm_UnicodeNonBMP_h #define vm_UnicodeNonBMP_h +// |macro| receives the following arguments +// macro(FROM, TO, LEAD, TRAIL_FROM, TRAIL_TO, DIFF) +// FROM: code point where the range starts +// TO: code point where the range ends +// LEAD: common lead surrogate of FROM and TO +// TRAIL_FROM: trail surrogate of FROM +// TRAIL_FROM: trail surrogate of TO +// DIFF: the difference between the code point in the range and +// converted code point + #define FOR_EACH_NON_BMP_LOWERCASE(macro) \ macro(0x10400, 0x10427, 0xd801, 0xdc00, 0xdc27, 40) \ macro(0x104b0, 0x104d3, 0xd801, 0xdcb0, 0xdcd3, 40) \ diff --git a/js/src/vm/make_unicode.py b/js/src/vm/make_unicode.py index 73c090ac9..83f0d004b 100755 --- a/js/src/vm/make_unicode.py +++ b/js/src/vm/make_unicode.py @@ -155,37 +155,65 @@ def utf16_encode(code): return lead, trail def make_non_bmp_convert_macro(out_file, name, convert_map): + # Find continuous range in convert_map. convert_list = [] entry = None for code in sorted(convert_map.keys()): + lead, trail = utf16_encode(code) converted = convert_map[code] diff = converted - code - if entry and code == entry['code'] + entry['length'] and diff == entry['diff']: + if (entry and code == entry['code'] + entry['length'] and + diff == entry['diff'] and lead == entry['lead']): + entry['length'] += 1 continue - entry = { 'code': code, 'diff': diff, 'length': 1 } + entry = { + 'code': code, + 'diff': diff, + 'length': 1, + 'lead': lead, + 'trail': trail, + } convert_list.append(entry) + # Generate macro call for each range. lines = [] for entry in convert_list: from_code = entry['code'] to_code = entry['code'] + entry['length'] - 1 diff = entry['diff'] - from_lead, from_trail = utf16_encode(from_code) - to_lead, to_trail = utf16_encode(to_code) - - assert from_lead == to_lead + lead = entry['lead'] + from_trail = entry['trail'] + to_trail = entry['trail'] + entry['length'] - 1 lines.append(' macro(0x{:x}, 0x{:x}, 0x{:x}, 0x{:x}, 0x{:x}, {:d})'.format( - from_code, to_code, from_lead, from_trail, to_trail, diff)) + from_code, to_code, lead, from_trail, to_trail, diff)) out_file.write('#define FOR_EACH_NON_BMP_{}(macro) \\\n'.format(name)) out_file.write(' \\\n'.join(lines)) out_file.write('\n') +def for_each_non_bmp_group(group_set): + # Find continuous range in group_set. + group_list = [] + entry = None + for code in sorted(group_set.keys()): + if entry and code == entry['code'] + entry['length']: + entry['length'] += 1 + continue + + entry = { + 'code': code, + 'length': 1 + } + group_list.append(entry) + + for entry in group_list: + yield (entry['code'], entry['code'] + entry['length'] - 1) + def process_derived_core_properties(derived_core_properties): id_start = set() id_continue = set() @@ -214,6 +242,9 @@ def process_unicode_data(unicode_data, derived_core_properties): non_bmp_lower_map = {} non_bmp_upper_map = {} + non_bmp_id_start_set = {} + non_bmp_id_cont_set = {} + non_bmp_space_set = {} (id_start, id_continue) = process_derived_core_properties(derived_core_properties) @@ -246,6 +277,13 @@ def process_unicode_data(unicode_data, derived_core_properties): non_bmp_lower_map[code] = lower if code != upper: non_bmp_upper_map[code] = upper + if category == 'Zs': + non_bmp_space_set[code] = 1 + test_space_table.append(code) + if code in id_start: + non_bmp_id_start_set[code] = 1 + if code in id_continue: + non_bmp_id_cont_set[code] = 1 continue # we combine whitespace and lineterminators because in pratice we don't need them separated @@ -315,6 +353,8 @@ def process_unicode_data(unicode_data, derived_core_properties): table, index, same_upper_table, same_upper_index, non_bmp_lower_map, non_bmp_upper_map, + non_bmp_space_set, + non_bmp_id_start_set, non_bmp_id_cont_set, test_table, test_space_table, ) @@ -412,6 +452,16 @@ def make_non_bmp_file(version, #ifndef vm_UnicodeNonBMP_h #define vm_UnicodeNonBMP_h +// |macro| receives the following arguments +// macro(FROM, TO, LEAD, TRAIL_FROM, TRAIL_TO, DIFF) +// FROM: code point where the range starts +// TO: code point where the range ends +// LEAD: common lead surrogate of FROM and TO +// TRAIL_FROM: trail surrogate of FROM +// TRAIL_FROM: trail surrogate of TO +// DIFF: the difference between the code point in the range and +// converted code point + """) make_non_bmp_convert_macro(non_bmp_file, 'LOWERCASE', non_bmp_lower_map) @@ -525,7 +575,9 @@ if (typeof reportCompare === "function") def make_unicode_file(version, table, index, same_upper_table, same_upper_index, - folding_table, folding_index): + folding_table, folding_index, + non_bmp_space_set, + non_bmp_id_start_set, non_bmp_id_cont_set): index1, index2, shift = splitbins(index) # Don't forget to update CharInfo in Unicode.h if you need to change this @@ -682,6 +734,43 @@ def make_unicode_file(version, dump(folding_index2, 'folding_index2', data_file) data_file.write('\n') + # If the following assert fails, it means space character is added to + # non-BMP area. In that case the following code should be uncommented + # and the corresponding code should be added to frontend. + assert len(non_bmp_space_set.keys()) == 0 + + data_file.write("""\ +bool +js::unicode::IsIdentifierStartNonBMP(uint32_t codePoint) +{ +""") + + for (from_code, to_code) in for_each_non_bmp_group(non_bmp_id_start_set): + data_file.write("""\ + if (codePoint >= 0x{:x} && codePoint <= 0x{:x}) + return true; +""".format(from_code, to_code)) + + data_file.write("""\ + return false; +} + +bool +js::unicode::IsIdentifierPartNonBMP(uint32_t codePoint) +{ +""") + + for (from_code, to_code) in for_each_non_bmp_group(non_bmp_id_cont_set): + data_file.write("""\ + if (codePoint >= 0x{:x} && codePoint <= 0x{:x}) + return true; +""".format(from_code, to_code)) + + data_file.write("""\ + return false; +} +""") + def getsize(data): """ return smallest possible integer size for the given array """ maxdata = max(data) @@ -1000,6 +1089,8 @@ def update_unicode(args): table, index, same_upper_table, same_upper_index, non_bmp_lower_map, non_bmp_upper_map, + non_bmp_space_set, + non_bmp_id_start_set, non_bmp_id_cont_set, test_table, test_space_table ) = process_unicode_data(unicode_data, derived_core_properties) ( @@ -1012,7 +1103,9 @@ def update_unicode(args): make_unicode_file(unicode_version, table, index, same_upper_table, same_upper_index, - folding_table, folding_index) + folding_table, folding_index, + non_bmp_space_set, + non_bmp_id_start_set, non_bmp_id_cont_set) make_non_bmp_file(unicode_version, non_bmp_lower_map, non_bmp_upper_map, non_bmp_folding_map, non_bmp_rev_folding_map) diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index 34f5a8c0d..4dbc9b387 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -21,6 +21,7 @@ #include "mozilla/Attributes.h" #include "mozilla/Compression.h" #include "mozilla/MathAlgorithms.h" +#include "mozilla/Maybe.h" #include "jsmath.h" #include "jsprf.h" @@ -8033,21 +8034,6 @@ TryInstantiate(JSContext* cx, CallArgs args, Module& module, const AsmJSMetadata return true; } -static MOZ_MUST_USE bool -MaybeAppendUTF8Name(JSContext* cx, const char* utf8Chars, MutableHandle<PropertyNameVector> names) -{ - if (!utf8Chars) - return true; - - UTF8Chars utf8(utf8Chars, strlen(utf8Chars)); - - JSAtom* atom = AtomizeUTF8Chars(cx, utf8Chars, strlen(utf8Chars)); - if (!atom) - return false; - - return names.append(atom->asPropertyName()); -} - static bool HandleInstantiationFailure(JSContext* cx, CallArgs args, const AsmJSMetadata& metadata) { @@ -8068,8 +8054,8 @@ HandleInstantiationFailure(JSContext* cx, CallArgs args, const AsmJSMetadata& me return false; } - uint32_t begin = metadata.srcBodyStart; // starts right after 'use asm' - uint32_t end = metadata.srcEndBeforeCurly(); + uint32_t begin = metadata.srcStart; + uint32_t end = metadata.srcEndAfterCurly(); Rooted<JSFlatString*> src(cx, source->substringDontDeflate(cx, begin, end)); if (!src) return false; @@ -8080,18 +8066,11 @@ HandleInstantiationFailure(JSContext* cx, CallArgs args, const AsmJSMetadata& me if (!fun) return false; - Rooted<PropertyNameVector> formals(cx, PropertyNameVector(cx)); - if (!MaybeAppendUTF8Name(cx, metadata.globalArgumentName.get(), &formals)) - return false; - if (!MaybeAppendUTF8Name(cx, metadata.importArgumentName.get(), &formals)) - return false; - if (!MaybeAppendUTF8Name(cx, metadata.bufferArgumentName.get(), &formals)) - return false; - CompileOptions options(cx); options.setMutedErrors(source->mutedErrors()) .setFile(source->filename()) .setNoScriptRval(false); + options.asmJSOption = AsmJSOption::Disabled; // The exported function inherits an implicit strict context if the module // also inherited it somehow. @@ -8106,8 +8085,8 @@ HandleInstantiationFailure(JSContext* cx, CallArgs args, const AsmJSMetadata& me SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller() ? SourceBufferHolder::GiveOwnership : SourceBufferHolder::NoOwnership; - SourceBufferHolder srcBuf(chars, end - begin, ownership); - if (!frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf)) + SourceBufferHolder srcBuf(chars, stableChars.twoByteRange().length(), ownership); + if (!frontend::CompileStandaloneFunction(cx, &fun, options, srcBuf, Nothing())) return false; // Call the function we just recompiled. @@ -8849,23 +8828,6 @@ js::IsAsmJSModuleLoadedFromCache(JSContext* cx, unsigned argc, Value* vp) /*****************************************************************************/ // asm.js toString/toSource support -static MOZ_MUST_USE bool -MaybeAppendUTF8Chars(JSContext* cx, const char* sep, const char* utf8Chars, StringBuffer* sb) -{ - if (!utf8Chars) - return true; - - UTF8Chars utf8(utf8Chars, strlen(utf8Chars)); - - size_t length; - UniqueTwoByteChars twoByteChars(UTF8CharsToNewTwoByteCharsZ(cx, utf8, &length).get()); - if (!twoByteChars) - return false; - - return sb->append(sep, strlen(sep)) && - sb->append(twoByteChars.get(), length); -} - JSString* js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda) { @@ -8895,33 +8857,12 @@ js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda if (!out.append("() {\n [sourceless code]\n}")) return nullptr; } else { - // Whether the function has been created with a Function ctor - bool funCtor = begin == 0 && end == source->length() && source->argumentsNotIncluded(); - if (funCtor) { - // Functions created with the function constructor don't have arguments in their source. - if (!out.append("(")) - return nullptr; - - if (!MaybeAppendUTF8Chars(cx, "", metadata.globalArgumentName.get(), &out)) - return nullptr; - if (!MaybeAppendUTF8Chars(cx, ", ", metadata.importArgumentName.get(), &out)) - return nullptr; - if (!MaybeAppendUTF8Chars(cx, ", ", metadata.bufferArgumentName.get(), &out)) - return nullptr; - - if (!out.append(") {\n")) - return nullptr; - } - Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end)); if (!src) return nullptr; if (!out.append(src)) return nullptr; - - if (funCtor && !out.append("\n}")) - return nullptr; } if (addParenToLambda && fun->isLambda() && !out.append(")")) @@ -8959,10 +8900,6 @@ js::AsmJSFunctionToString(JSContext* cx, HandleFunction fun) if (!out.append("() {\n [sourceless code]\n}")) return nullptr; } else { - // asm.js functions cannot have been created with a Function constructor - // as they belong within a module. - MOZ_ASSERT(!(begin == 0 && end == source->length() && source->argumentsNotIncluded())); - Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end)); if (!src) return nullptr; diff --git a/js/xpconnect/loader/mozJSSubScriptLoader.cpp b/js/xpconnect/loader/mozJSSubScriptLoader.cpp index 824e9ab9e..9c8908ea4 100644 --- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp +++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp @@ -130,7 +130,9 @@ PrepareScript(nsIURI* uri, MutableHandleFunction function) { JS::CompileOptions options(cx); - options.setFileAndLine(uriStr, 1) + // Use line 0 to make the function body starts from line 1 when + // |reuseGlobal == true|. + options.setFileAndLine(uriStr, reuseGlobal ? 0 : 1) .setVersion(JSVERSION_LATEST); if (!charset.IsVoid()) { char16_t* scriptBuf = nullptr; |