summaryrefslogtreecommitdiffstats
path: root/js/src/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/builtin')
-rw-r--r--js/src/builtin/Array.js108
-rw-r--r--js/src/builtin/Object.cpp280
-rw-r--r--js/src/builtin/String.js12
-rw-r--r--js/src/builtin/SymbolObject.cpp29
-rw-r--r--js/src/builtin/SymbolObject.h4
-rw-r--r--js/src/builtin/TestingFunctions.cpp4
-rw-r--r--js/src/builtin/TypedObject.cpp10
7 files changed, 344 insertions, 103 deletions
diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js
index 30e6fb35f..05fc41bc1 100644
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -1073,6 +1073,114 @@ function ArrayConcat(arg1) {
return A;
}
+// https://tc39.github.io/proposal-flatMap/
+// January 4, 2019
+function ArrayFlatMap(mapperFunction/*, thisArg*/) {
+ // Step 1.
+ var O = ToObject(this);
+
+ // Step 2.
+ var sourceLen = ToLength(O.length);
+
+ // Step 3.
+ if (!IsCallable(mapperFunction))
+ ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, mapperFunction));
+
+ // Step 4.
+ var T = arguments.length > 1 ? arguments[1] : undefined;
+
+ // Step 5.
+ var A = ArraySpeciesCreate(O, 0);
+
+ // Step 6.
+ FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, T);
+
+ // Step 7.
+ return A;
+}
+
+// https://tc39.github.io/proposal-flatMap/
+// January 4, 2019
+function ArrayFlat(/* depth */) {
+ // Step 1.
+ var O = ToObject(this);
+
+ // Step 2.
+ var sourceLen = ToLength(O.length);
+
+ // Step 3.
+ var depthNum = 1;
+
+ // Step 4.
+ if (arguments.length > 0 && arguments[0] !== undefined)
+ depthNum = ToInteger(arguments[0]);
+
+ // Step 5.
+ var A = ArraySpeciesCreate(O, 0);
+
+ // Step 6.
+ FlattenIntoArray(A, O, sourceLen, 0, depthNum);
+
+ // Step 7.
+ return A;
+}
+
+// https://tc39.github.io/proposal-flatMap/
+// January 4, 2019
+function FlattenIntoArray(target, source, sourceLen, start, depth, mapperFunction, thisArg) {
+ // Step 1.
+ var targetIndex = start;
+
+ // Steps 2-3.
+ for (var sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) {
+ // Steps 3.a-c.
+ if (sourceIndex in source) {
+ // Step 3.c.i.
+ var element = source[sourceIndex];
+
+ if (mapperFunction) {
+ // Step 3.c.ii.1.
+ assert(arguments.length === 7, "thisArg is present");
+
+ // Step 3.c.ii.2.
+ element = callContentFunction(mapperFunction, thisArg, element, sourceIndex, source);
+ }
+
+ // Step 3.c.iii.
+ var shouldFlatten = false;
+
+ // Step 3.c.iv.
+ if (depth > 0) {
+ // Step 3.c.iv.1.
+ shouldFlatten = IsArray(element);
+ }
+
+ // Step 3.c.v.
+ if (shouldFlatten) {
+ // Step 3.c.v.1.
+ var elementLen = ToLength(element.length);
+
+ // Step 3.c.v.2.
+ // Recursive call to walk the depth.
+ targetIndex = FlattenIntoArray(target, element, elementLen, targetIndex, depth - 1);
+ } else {
+ // Step 3.c.vi.1.
+ if (targetIndex >= MAX_NUMERIC_INDEX)
+ ThrowTypeError(JSMSG_TOO_LONG_ARRAY);
+
+ // Step 3.c.vi.2.
+ _DefineDataProperty(target, targetIndex, element);
+
+ // Step 3.c.vi.3.
+ targetIndex++;
+ }
+ }
+ }
+
+ // Step 4.
+ return targetIndex;
+}
+
function ArrayStaticConcat(arr, arg1) {
if (arguments.length < 1)
ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.concat');
diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp
index cd4ac122c..389bb57db 100644
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -9,11 +9,13 @@
#include "mozilla/ArrayUtils.h"
#include "jscntxt.h"
+#include "jsstr.h"
#include "builtin/Eval.h"
#include "frontend/BytecodeCompiler.h"
#include "jit/InlinableNatives.h"
#include "js/UniquePtr.h"
+#include "vm/AsyncFunction.h"
#include "vm/StringBuffer.h"
#include "jsobjinlines.h"
@@ -124,6 +126,27 @@ obj_toSource(JSContext* cx, unsigned argc, Value* vp)
return true;
}
+template <typename CharT>
+static bool
+Consume(const CharT*& s, const CharT* e, const char *chars)
+{
+ size_t len = strlen(chars);
+ if (s + len >= e)
+ return false;
+ if (!EqualChars(s, chars, len))
+ return false;
+ s += len;
+ return true;
+}
+
+template <typename CharT>
+static void
+ConsumeSpaces(const CharT*& s, const CharT* e)
+{
+ while (*s == ' ' && s < e)
+ s++;
+}
+
/*
* Given a function source string, return the offset and length of the part
* between '(function $name' and ')'.
@@ -133,37 +156,53 @@ static bool
ArgsAndBodySubstring(mozilla::Range<const CharT> chars, size_t* outOffset, size_t* outLen)
{
const CharT* const start = chars.begin().get();
- const CharT* const end = chars.end().get();
const CharT* s = start;
+ const CharT* e = chars.end().get();
- uint8_t parenChomp = 0;
- if (s[0] == '(') {
- s++;
- parenChomp = 1;
- }
-
- /* Try to jump "function" keyword. */
- s = js_strchr_limit(s, ' ', end);
- if (!s)
+ if (s == e)
return false;
- /*
- * Jump over the function's name: it can't be encoded as part
- * of an ECMA getter or setter.
- */
- s = js_strchr_limit(s, '(', end);
- if (!s)
- return false;
+ // Remove enclosing parentheses.
+ if (*s == '(' && *(e - 1) == ')') {
+ s++;
+ e--;
+ }
- if (*s == ' ')
+ (void) Consume(s, e, "async");
+ ConsumeSpaces(s, e);
+ (void) (Consume(s, e, "function") || Consume(s, e, "get") || Consume(s, e, "set"));
+ ConsumeSpaces(s, e);
+ (void) Consume(s, e, "*");
+ ConsumeSpaces(s, e);
+
+ // Jump over the function's name.
+ if (Consume(s, e, "[")) {
+ s = js_strchr_limit(s, ']', e);
+ if (!s)
+ return false;
s++;
+ ConsumeSpaces(s, e);
+ if (*s != '(')
+ return false;
+ } else {
+ s = js_strchr_limit(s, '(', e);
+ if (!s)
+ return false;
+ }
*outOffset = s - start;
- *outLen = end - s - parenChomp;
+ *outLen = e - s;
MOZ_ASSERT(*outOffset + *outLen <= chars.length());
return true;
}
+enum class PropertyKind {
+ Getter,
+ Setter,
+ Method,
+ Normal
+};
+
JSString*
js::ObjectToSource(JSContext* cx, HandleObject obj)
{
@@ -182,59 +221,28 @@ js::ObjectToSource(JSContext* cx, HandleObject obj)
if (!buf.append('{'))
return nullptr;
- RootedValue v0(cx), v1(cx);
- MutableHandleValue val[2] = {&v0, &v1};
-
- RootedString str0(cx), str1(cx);
- MutableHandleString gsop[2] = {&str0, &str1};
-
AutoIdVector idv(cx);
if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_SYMBOLS, &idv))
return nullptr;
bool comma = false;
- for (size_t i = 0; i < idv.length(); ++i) {
- RootedId id(cx, idv[i]);
- Rooted<PropertyDescriptor> desc(cx);
- if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
- return nullptr;
-
- int valcnt = 0;
- if (desc.object()) {
- if (desc.isAccessorDescriptor()) {
- if (desc.hasGetterObject() && desc.getterObject()) {
- val[valcnt].setObject(*desc.getterObject());
- gsop[valcnt].set(cx->names().get);
- valcnt++;
- }
- if (desc.hasSetterObject() && desc.setterObject()) {
- val[valcnt].setObject(*desc.setterObject());
- gsop[valcnt].set(cx->names().set);
- valcnt++;
- }
- } else {
- valcnt = 1;
- val[0].set(desc.value());
- gsop[0].set(nullptr);
- }
- }
-
+ auto AddProperty = [cx, &comma, &buf](HandleId id, HandleValue val, PropertyKind kind) -> bool {
/* Convert id to a string. */
RootedString idstr(cx);
if (JSID_IS_SYMBOL(id)) {
RootedValue v(cx, SymbolValue(JSID_TO_SYMBOL(id)));
idstr = ValueToSource(cx, v);
if (!idstr)
- return nullptr;
+ return false;
} else {
RootedValue idv(cx, IdToValue(id));
idstr = ToString<CanGC>(cx, idv);
if (!idstr)
- return nullptr;
+ return false;
/*
- * If id is a string that's not an identifier, or if it's a negative
- * integer, then it must be quoted.
+ * If id is a string that's not an identifier, or if it's a
+ * negative integer, then it must be quoted.
*/
if (JSID_IS_ATOM(id)
? !IsIdentifier(JSID_TO_ATOM(id))
@@ -242,28 +250,65 @@ js::ObjectToSource(JSContext* cx, HandleObject obj)
{
idstr = QuoteString(cx, idstr, char16_t('\''));
if (!idstr)
- return nullptr;
+ return false;
}
}
- for (int j = 0; j < valcnt; j++) {
- /* Convert val[j] to its canonical source form. */
- JSString* valsource = ValueToSource(cx, val[j]);
- if (!valsource)
- return nullptr;
+ RootedString valsource(cx, ValueToSource(cx, val));
+ if (!valsource)
+ return false;
- RootedLinearString valstr(cx, valsource->ensureLinear(cx));
- if (!valstr)
- return nullptr;
+ RootedLinearString valstr(cx, valsource->ensureLinear(cx));
+ if (!valstr)
+ return false;
+
+ if (comma && !buf.append(", "))
+ return false;
+ comma = true;
+
+ size_t voffset, vlength;
+
+ // Methods and accessors can return exact syntax of source, that fits
+ // into property without adding property name or "get"/"set" prefix.
+ // Use the exact syntax when the following conditions are met:
+ //
+ // * It's a function object
+ // (exclude proxies)
+ // * Function's kind and property's kind are same
+ // (this can be false for dynamically defined properties)
+ // * Function has explicit name
+ // (this can be false for computed property and dynamically defined
+ // properties)
+ // * Function's name and property's name are same
+ // (this can be false for dynamically defined properties)
+ if (kind == PropertyKind::Getter || kind == PropertyKind::Setter ||
+ kind == PropertyKind::Method)
+ {
+ RootedFunction fun(cx);
+ if (val.toObject().is<JSFunction>()) {
+ fun = &val.toObject().as<JSFunction>();
+ // Method's case should be checked on caller.
+ if (((fun->isGetter() && kind == PropertyKind::Getter) ||
+ (fun->isSetter() && kind == PropertyKind::Setter) ||
+ kind == PropertyKind::Method) &&
+ fun->explicitName())
+ {
+ bool result;
+ if (!EqualStrings(cx, fun->explicitName(), idstr, &result))
+ return false;
- size_t voffset = 0;
- size_t vlength = valstr->length();
+ if (result) {
+ if (!buf.append(valstr))
+ return false;
+ return true;
+ }
+ }
+ }
- /*
- * Remove '(function ' from the beginning of valstr and ')' from the
- * end so that we can put "get" in front of the function definition.
- */
- if (gsop[j] && IsFunctionObject(val[j])) {
+ {
+ // When falling back try to generate a better string
+ // representation by skipping the prelude, and also removing
+ // the enclosing parentheses.
bool success;
JS::AutoCheckCannotGC nogc;
if (valstr->hasLatin1Chars())
@@ -271,29 +316,90 @@ js::ObjectToSource(JSContext* cx, HandleObject obj)
else
success = ArgsAndBodySubstring(valstr->twoByteRange(nogc), &voffset, &vlength);
if (!success)
- gsop[j].set(nullptr);
+ kind = PropertyKind::Normal;
}
- if (comma && !buf.append(", "))
- return nullptr;
- comma = true;
+ if (kind == PropertyKind::Getter) {
+ if (!buf.append("get "))
+ return false;
+ } else if (kind == PropertyKind::Setter) {
+ if (!buf.append("set "))
+ return false;
+ } else if (kind == PropertyKind::Method && fun) {
+ if (IsWrappedAsyncFunction(fun)) {
+ if (!buf.append("async "))
+ return false;
+ }
- if (gsop[j]) {
- if (!buf.append(gsop[j]) || !buf.append(' '))
- return nullptr;
+ if (fun->isStarGenerator()) {
+ if (!buf.append('*'))
+ return false;
+ }
}
- if (JSID_IS_SYMBOL(id) && !buf.append('['))
- return nullptr;
- if (!buf.append(idstr))
- return nullptr;
- if (JSID_IS_SYMBOL(id) && !buf.append(']'))
- return nullptr;
- if (!buf.append(gsop[j] ? ' ' : ':'))
- return nullptr;
+ }
+ bool needsBracket = JSID_IS_SYMBOL(id);
+ if (needsBracket && !buf.append('['))
+ return false;
+ if (!buf.append(idstr))
+ return false;
+ if (needsBracket && !buf.append(']'))
+ return false;
+
+ if (kind == PropertyKind::Getter || kind == PropertyKind::Setter ||
+ kind == PropertyKind::Method)
+ {
if (!buf.appendSubstring(valstr, voffset, vlength))
- return nullptr;
+ return false;
+ } else {
+ if (!buf.append(':'))
+ return false;
+ if (!buf.append(valstr))
+ return false;
+ }
+ return true;
+ };
+
+ RootedId id(cx);
+ Rooted<PropertyDescriptor> desc(cx);
+ RootedValue val(cx);
+ RootedFunction fun(cx);
+ for (size_t i = 0; i < idv.length(); ++i) {
+ id = idv[i];
+ if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
+ return nullptr;
+
+ if (!desc.object())
+ continue;
+
+ if (desc.isAccessorDescriptor()) {
+ if (desc.hasGetterObject() && desc.getterObject()) {
+ val.setObject(*desc.getterObject());
+ if (!AddProperty(id, val, PropertyKind::Getter))
+ return nullptr;
+ }
+ if (desc.hasSetterObject() && desc.setterObject()) {
+ val.setObject(*desc.setterObject());
+ if (!AddProperty(id, val, PropertyKind::Setter))
+ return nullptr;
+ }
+ continue;
}
+
+ val.set(desc.value());
+ if (IsFunctionObject(val, fun.address())) {
+ if (IsWrappedAsyncFunction(fun))
+ fun = GetUnwrappedAsyncFunction(fun);
+
+ if (fun->isMethod()) {
+ if (!AddProperty(id, val, PropertyKind::Method))
+ return nullptr;
+ continue;
+ }
+ }
+
+ if (!AddProperty(id, val, PropertyKind::Normal))
+ return nullptr;
}
if (!buf.append('}'))
diff --git a/js/src/builtin/String.js b/js/src/builtin/String.js
index e5b2ad552..f830b1aa2 100644
--- a/js/src/builtin/String.js
+++ b/js/src/builtin/String.js
@@ -828,16 +828,16 @@ function String_static_trim(string) {
return callFunction(std_String_trim, string);
}
-function String_static_trimLeft(string) {
+function String_static_trimStart(string) {
if (arguments.length < 1)
- ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.trimLeft');
- return callFunction(std_String_trimLeft, string);
+ ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.trimStart');
+ return callFunction(std_String_trimStart, string);
}
-function String_static_trimRight(string) {
+function String_static_trimEnd(string) {
if (arguments.length < 1)
- ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.trimRight');
- return callFunction(std_String_trimRight, string);
+ ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.trimEnd');
+ return callFunction(std_String_trimEnd, string);
}
function String_static_toLocaleLowerCase(string) {
diff --git a/js/src/builtin/SymbolObject.cpp b/js/src/builtin/SymbolObject.cpp
index cf48402d6..ae38d5371 100644
--- a/js/src/builtin/SymbolObject.cpp
+++ b/js/src/builtin/SymbolObject.cpp
@@ -33,6 +33,7 @@ SymbolObject::create(JSContext* cx, JS::HandleSymbol symbol)
}
const JSPropertySpec SymbolObject::properties[] = {
+ JS_PSG("description", descriptionGetter, 0),
JS_PS_END
};
@@ -227,6 +228,34 @@ SymbolObject::toPrimitive(JSContext* cx, unsigned argc, Value* vp)
return CallNonGenericMethod<IsSymbol, valueOf_impl>(cx, args);
}
+// ES2019 Stage 4 Draft / November 28, 2018
+// Symbol description accessor
+// See: https://tc39.github.io/proposal-Symbol-description/
+bool
+SymbolObject::descriptionGetter_impl(JSContext* cx, const CallArgs& args)
+{
+ // Get symbol object pointer.
+ HandleValue thisv = args.thisv();
+ MOZ_ASSERT(IsSymbol(thisv));
+ Rooted<Symbol*> sym(cx, thisv.isSymbol()
+ ? thisv.toSymbol()
+ : thisv.toObject().as<SymbolObject>().unbox());
+
+ // Return the symbol's description if present, otherwise return undefined.
+ if (JSString* str = sym->description())
+ args.rval().setString(str);
+ else
+ args.rval().setUndefined();
+ return true;
+}
+
+bool
+SymbolObject::descriptionGetter(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<IsSymbol, descriptionGetter_impl>(cx, args);
+}
+
JSObject*
js::InitSymbolClass(JSContext* cx, HandleObject obj)
{
diff --git a/js/src/builtin/SymbolObject.h b/js/src/builtin/SymbolObject.h
index 0f204b494..e10b42d53 100644
--- a/js/src/builtin/SymbolObject.h
+++ b/js/src/builtin/SymbolObject.h
@@ -52,6 +52,10 @@ class SymbolObject : public NativeObject
static MOZ_MUST_USE bool valueOf(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE bool toPrimitive(JSContext* cx, unsigned argc, Value* vp);
+ // Properties defined on Symbol.prototype.
+ static MOZ_MUST_USE bool descriptionGetter_impl(JSContext* cx, const CallArgs& args);
+ static MOZ_MUST_USE bool descriptionGetter(JSContext* cx, unsigned argc, Value *vp);
+
static const JSPropertySpec properties[];
static const JSFunctionSpec methods[];
static const JSFunctionSpec staticMethods[];
diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp
index 373b6c9ed..c896ce5d1 100644
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -240,11 +240,7 @@ GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp)
if (!JS_SetProperty(cx, info, "intl-api", value))
return false;
-#if defined(SOLARIS)
- value = BooleanValue(false);
-#else
value = BooleanValue(true);
-#endif
if (!JS_SetProperty(cx, info, "mapped-array-buffer", value))
return false;
diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp
index ae74f01bf..0dfc1123a 100644
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -652,7 +652,7 @@ ArrayMetaTypeDescr::create(JSContext* cx,
if (!CreateTraceList(cx, obj))
return nullptr;
- if (!cx->zone()->typeDescrObjects.put(obj)) {
+ if (!cx->zone()->addTypeDescrObject(cx, obj)) {
ReportOutOfMemory(cx);
return nullptr;
}
@@ -993,8 +993,8 @@ StructMetaTypeDescr::create(JSContext* cx,
if (!CreateTraceList(cx, descr))
return nullptr;
- if (!cx->zone()->typeDescrObjects.put(descr) ||
- !cx->zone()->typeDescrObjects.put(fieldTypeVec))
+ if (!cx->zone()->addTypeDescrObject(cx, descr) ||
+ !cx->zone()->addTypeDescrObject(cx, fieldTypeVec))
{
ReportOutOfMemory(cx);
return nullptr;
@@ -1165,10 +1165,8 @@ DefineSimpleTypeDescr(JSContext* cx,
if (!CreateTraceList(cx, descr))
return false;
- if (!cx->zone()->typeDescrObjects.put(descr)) {
- ReportOutOfMemory(cx);
+ if (!cx->zone()->addTypeDescrObject(cx, descr))
return false;
- }
return true;
}