summaryrefslogtreecommitdiffstats
path: root/js
diff options
context:
space:
mode:
Diffstat (limited to 'js')
-rw-r--r--js/ipc/JavaScriptParent.cpp1
-rw-r--r--js/public/CallArgs.h22
-rw-r--r--js/src/builtin/AtomicsObject.cpp25
-rw-r--r--js/src/builtin/AtomicsObject.h26
-rw-r--r--js/src/builtin/Intl.cpp31
-rw-r--r--js/src/builtin/IntlTimeZoneData.h2
-rw-r--r--js/src/builtin/MapObject.cpp4
-rw-r--r--js/src/builtin/ModuleObject.cpp10
-rw-r--r--js/src/builtin/ModuleObject.h2
-rw-r--r--js/src/builtin/Object.cpp18
-rw-r--r--js/src/builtin/Object.h3
-rw-r--r--js/src/builtin/Object.js6
-rw-r--r--js/src/builtin/Promise.cpp129
-rw-r--r--js/src/builtin/Promise.h8
-rw-r--r--js/src/builtin/Reflect.cpp3
-rw-r--r--js/src/builtin/ReflectParse.cpp31
-rw-r--r--js/src/builtin/RegExp.cpp18
-rw-r--r--js/src/builtin/RegExp.h2
-rw-r--r--js/src/builtin/SIMD.cpp22
-rw-r--r--js/src/builtin/SymbolObject.cpp8
-rw-r--r--js/src/builtin/TestingFunctions.cpp39
-rw-r--r--js/src/builtin/TypedObject.cpp19
-rw-r--r--js/src/builtin/Utilities.js63
-rw-r--r--js/src/builtin/WeakMapObject.cpp6
-rw-r--r--js/src/builtin/WeakSetObject.cpp7
-rw-r--r--js/src/builtin/WeakSetObject.h2
-rw-r--r--js/src/frontend/BytecodeCompiler.cpp25
-rw-r--r--js/src/frontend/BytecodeEmitter.cpp436
-rw-r--r--js/src/frontend/BytecodeEmitter.h38
-rw-r--r--js/src/frontend/FoldConstants.cpp11
-rw-r--r--js/src/frontend/FullParseHandler.h51
-rw-r--r--js/src/frontend/GenerateReservedWords.py213
-rw-r--r--js/src/frontend/NameAnalysisTypes.h14
-rw-r--r--js/src/frontend/NameFunctions.cpp6
-rw-r--r--js/src/frontend/ParseNode.cpp2
-rw-r--r--js/src/frontend/ParseNode.h23
-rw-r--r--js/src/frontend/Parser.cpp3145
-rw-r--r--js/src/frontend/Parser.h477
-rw-r--r--js/src/frontend/ReservedWords.h (renamed from js/src/vm/Keywords.h)49
-rw-r--r--js/src/frontend/SharedContext.h22
-rw-r--r--js/src/frontend/SourceNotes.h3
-rw-r--r--js/src/frontend/SyntaxParseHandler.h53
-rw-r--r--js/src/frontend/TokenKind.h99
-rw-r--r--js/src/frontend/TokenStream.cpp478
-rw-r--r--js/src/frontend/TokenStream.h254
-rw-r--r--js/src/gc/Marking.cpp2
-rw-r--r--js/src/irregexp/RegExpParser.cpp6
-rw-r--r--js/src/irregexp/RegExpParser.h2
-rw-r--r--js/src/jit-test/modules/export-default-async-asi.js2
-rw-r--r--js/src/jit-test/tests/asm.js/import-function-toPrimitive.js26
-rw-r--r--js/src/jit-test/tests/baseline/bug1344334.js14
-rw-r--r--js/src/jit-test/tests/basic/bug713226.js2
-rw-r--r--js/src/jit-test/tests/basic/hasnativemethodpure-optimization.js21
-rw-r--r--js/src/jit-test/tests/basic/testFunctionStatementAliasLocals.js4
-rw-r--r--js/src/jit-test/tests/basic/unboxed-object-clear-new-script.js49
-rw-r--r--js/src/jit-test/tests/basic/unboxed-object-convert-to-native.js47
-rw-r--r--js/src/jit-test/tests/basic/unboxed-object-getelem.js20
-rw-r--r--js/src/jit-test/tests/basic/unboxed-object-set-property.js31
-rw-r--r--js/src/jit-test/tests/basic/unboxed-property-enumeration.js24
-rw-r--r--js/src/jit-test/tests/class/bug1357506.js8
-rw-r--r--js/src/jit-test/tests/class/bug1359622.js4
-rw-r--r--js/src/jit-test/tests/debug/wasm-12.js26
-rw-r--r--js/src/jit-test/tests/ion/unboxed-objects-invalidate.js16
-rw-r--r--js/src/jit-test/tests/modules/export-declaration.js44
-rw-r--r--js/src/jit-test/tests/modules/function-redeclaration.js94
-rw-r--r--js/src/jit-test/tests/parser/arrow-rest.js6
-rw-r--r--js/src/jit-test/tests/parser/missing-closing-brace.js90
-rw-r--r--js/src/jit-test/tests/parser/redeclaration.js230
-rw-r--r--js/src/jit-test/tests/profiler/AutoEntryMonitor-01.js2
-rw-r--r--js/src/jit/AliasAnalysisShared.cpp4
-rw-r--r--js/src/jit/BaselineCacheIR.cpp68
-rw-r--r--js/src/jit/BaselineCompiler.cpp18
-rw-r--r--js/src/jit/BaselineCompiler.h2
-rw-r--r--js/src/jit/BaselineIC.cpp540
-rw-r--r--js/src/jit/BaselineIC.h90
-rw-r--r--js/src/jit/BaselineInspector.cpp48
-rw-r--r--js/src/jit/BaselineInspector.h2
-rw-r--r--js/src/jit/BaselineJIT.cpp2
-rw-r--r--js/src/jit/CacheIR.cpp79
-rw-r--r--js/src/jit/CacheIR.h25
-rw-r--r--js/src/jit/CodeGenerator.cpp454
-rw-r--r--js/src/jit/CodeGenerator.h9
-rw-r--r--js/src/jit/InlinableNatives.h1
-rw-r--r--js/src/jit/Ion.cpp2
-rw-r--r--js/src/jit/IonAnalysis.cpp6
-rw-r--r--js/src/jit/IonAnalysis.h2
-rw-r--r--js/src/jit/IonBuilder.cpp435
-rw-r--r--js/src/jit/IonBuilder.h43
-rw-r--r--js/src/jit/IonCaches.cpp473
-rw-r--r--js/src/jit/IonCaches.h12
-rw-r--r--js/src/jit/JitOptions.cpp3
-rw-r--r--js/src/jit/JitOptions.h3
-rw-r--r--js/src/jit/Lowering.cpp56
-rw-r--r--js/src/jit/Lowering.h5
-rw-r--r--js/src/jit/MCallOptimize.cpp147
-rw-r--r--js/src/jit/MIR.cpp101
-rw-r--r--js/src/jit/MIR.h257
-rw-r--r--js/src/jit/MOpcodes.h5
-rw-r--r--js/src/jit/MacroAssembler.cpp309
-rw-r--r--js/src/jit/MacroAssembler.h17
-rw-r--r--js/src/jit/OptimizationTracking.cpp4
-rw-r--r--js/src/jit/Recover.cpp38
-rw-r--r--js/src/jit/ScalarReplacement.cpp38
-rw-r--r--js/src/jit/SharedIC.cpp39
-rw-r--r--js/src/jit/VMFunctions.cpp51
-rw-r--r--js/src/jit/VMFunctions.h13
-rw-r--r--js/src/jit/arm64/Architecture-arm64.h10
-rw-r--r--js/src/jit/shared/LIR-shared.h121
-rw-r--r--js/src/jit/shared/LOpcodes-shared.h5
-rw-r--r--js/src/js.msg9
-rw-r--r--js/src/jsapi-tests/moz.build1
-rw-r--r--js/src/jsapi-tests/testFunctionBinding.cpp58
-rw-r--r--js/src/jsapi-tests/testUbiNode.cpp2
-rw-r--r--js/src/jsapi.cpp181
-rw-r--r--js/src/jsapi.h183
-rw-r--r--js/src/jsarray.cpp577
-rw-r--r--js/src/jsarray.h50
-rw-r--r--js/src/jsatom.cpp32
-rw-r--r--js/src/jsatom.h35
-rw-r--r--js/src/jsbool.cpp6
-rw-r--r--js/src/jscntxt.cpp227
-rw-r--r--js/src/jscntxt.h10
-rw-r--r--js/src/jscompartment.cpp15
-rw-r--r--js/src/jscompartment.h3
-rwxr-xr-xjs/src/jsdate.cpp2
-rw-r--r--js/src/jsexn.cpp126
-rw-r--r--js/src/jsexn.h6
-rw-r--r--js/src/jsfriendapi.cpp6
-rw-r--r--js/src/jsfriendapi.h6
-rw-r--r--js/src/jsfun.cpp207
-rw-r--r--js/src/jsfun.h38
-rw-r--r--js/src/jsfuninlines.h2
-rw-r--r--js/src/jsiter.cpp22
-rw-r--r--js/src/jsmath.cpp3
-rw-r--r--js/src/jsnum.cpp7
-rw-r--r--js/src/jsobj.cpp190
-rw-r--r--js/src/jsobj.h49
-rw-r--r--js/src/jsobjinlines.h76
-rw-r--r--js/src/json.cpp4
-rw-r--r--js/src/jsopcode.cpp15
-rw-r--r--js/src/jsscript.cpp100
-rw-r--r--js/src/jsscript.h102
-rw-r--r--js/src/jsscriptinlines.h12
-rw-r--r--js/src/jsstr.cpp31
-rw-r--r--js/src/jsstr.h2
-rw-r--r--js/src/jswatchpoint.cpp2
-rw-r--r--js/src/jswrapper.h2
-rw-r--r--js/src/moz.build11
-rw-r--r--js/src/proxy/CrossCompartmentWrapper.cpp4
-rw-r--r--js/src/proxy/Proxy.cpp6
-rw-r--r--js/src/proxy/Wrapper.cpp4
-rw-r--r--js/src/shell/js.cpp34
-rw-r--r--js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js5
-rw-r--r--js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js2
-rw-r--r--js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js2
-rw-r--r--js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js2
-rw-r--r--js/src/tests/ecma_2017/AsyncFunctions/await-error.js16
-rw-r--r--js/src/tests/ecma_2017/AsyncFunctions/await-in-arrow-parameters.js94
-rw-r--r--js/src/tests/ecma_2017/AsyncFunctions/forbidden-as-consequent.js14
-rw-r--r--js/src/tests/ecma_2017/AsyncFunctions/inner-caller.js26
-rw-r--r--js/src/tests/ecma_6/Class/parenExprToString.js8
-rw-r--r--js/src/tests/ecma_6/Comprehensions/for-reserved-word.js107
-rw-r--r--js/src/tests/ecma_6/Function/constructor-binding.js11
-rw-r--r--js/src/tests/ecma_6/Function/throw-type-error.js16
-rw-r--r--js/src/tests/ecma_6/Generators/forbidden-as-consequent.js11
-rw-r--r--js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-parameter.js21
-rw-r--r--js/src/tests/ecma_6/TemplateStrings/tagTempl.js171
-rw-r--r--js/src/tests/ecma_6/extensions/newer-type-functions-caller-arguments.js43
-rw-r--r--js/src/tests/ecma_7/AsyncFunctions/async-contains-unicode-escape.js54
-rw-r--r--js/src/tests/js1_8/regress/regress-467495-05.js2
-rw-r--r--js/src/tests/js1_8/regress/regress-467495-06.js2
-rw-r--r--js/src/tests/js1_8_5/reflect-parse/object-rest.js45
-rw-r--r--js/src/tests/js1_8_5/reflect-parse/object-spread.js29
-rw-r--r--js/src/tests/js1_8_5/reflect-parse/templateStrings.js2
-rw-r--r--js/src/tests/shell/futex.js8
-rw-r--r--js/src/tests/test262/built-ins/Object/getOwnPropertyDescriptors/shell.js27
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-1.js17
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-2.js19
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-3.js19
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-4.js19
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-1.js19
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-2.js24
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-3.js25
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-4.js25
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-1.js24
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-2.js25
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-3.js28
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-4.js25
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-5.js26
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-1.js21
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-2.js26
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-3.js27
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-4.js26
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-1.js25
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-2.js26
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-3.js25
-rwxr-xr-xjs/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-4.js26
-rw-r--r--js/src/tests/test262/language/arguments-object/mapped/shell.js0
-rw-r--r--js/src/tests/test262/language/arguments-object/shell.js0
-rw-r--r--js/src/tests/test262/language/shell.js0
-rw-r--r--js/src/tests/test262/shell.js28
-rw-r--r--js/src/vm/ArgumentsObject.cpp72
-rw-r--r--js/src/vm/ArgumentsObject.h3
-rw-r--r--js/src/vm/ArrayBufferObject.cpp4
-rw-r--r--js/src/vm/AsyncFunction.cpp2
-rw-r--r--js/src/vm/CommonPropertyNames.h38
-rw-r--r--js/src/vm/Debugger.cpp58
-rw-r--r--js/src/vm/Debugger.h3
-rw-r--r--js/src/vm/EnvironmentObject.cpp35
-rw-r--r--js/src/vm/EnvironmentObject.h3
-rw-r--r--js/src/vm/ErrorObject.cpp8
-rw-r--r--js/src/vm/ErrorReporting.cpp124
-rw-r--r--js/src/vm/ErrorReporting.h91
-rw-r--r--js/src/vm/GeneratorObject.cpp14
-rw-r--r--js/src/vm/GlobalObject.cpp46
-rw-r--r--js/src/vm/GlobalObject.h241
-rw-r--r--js/src/vm/HelperThreads.cpp2
-rw-r--r--js/src/vm/Interpreter-inl.h4
-rw-r--r--js/src/vm/Interpreter.cpp71
-rw-r--r--js/src/vm/JSONParser.cpp4
-rw-r--r--js/src/vm/NativeObject-inl.h32
-rw-r--r--js/src/vm/NativeObject.cpp56
-rw-r--r--js/src/vm/NativeObject.h63
-rw-r--r--js/src/vm/ObjectGroup-inl.h14
-rw-r--r--js/src/vm/ObjectGroup.cpp189
-rw-r--r--js/src/vm/ObjectGroup.h72
-rw-r--r--js/src/vm/Opcodes.h25
-rw-r--r--js/src/vm/ProxyObject.h2
-rw-r--r--js/src/vm/ReceiverGuard.cpp18
-rw-r--r--js/src/vm/RegExpObject.cpp34
-rw-r--r--js/src/vm/RegExpObject.h11
-rw-r--r--js/src/vm/Runtime.cpp2
-rw-r--r--js/src/vm/Scope.cpp8
-rw-r--r--js/src/vm/Scope.h11
-rw-r--r--js/src/vm/SelfHosting.cpp6
-rw-r--r--js/src/vm/Shape.cpp166
-rw-r--r--js/src/vm/Shape.h3
-rw-r--r--js/src/vm/SharedArrayObject.cpp3
-rw-r--r--js/src/vm/Stack-inl.h6
-rw-r--r--js/src/vm/Stack.cpp2
-rw-r--r--js/src/vm/Stack.h13
-rw-r--r--js/src/vm/StringObject-inl.h18
-rw-r--r--js/src/vm/StringObject.h2
-rw-r--r--js/src/vm/TypeInference-inl.h5
-rw-r--r--js/src/vm/TypeInference.cpp180
-rw-r--r--js/src/vm/TypeInference.h4
-rw-r--r--js/src/vm/TypedArrayObject.cpp11
-rw-r--r--js/src/vm/UnboxedObject-inl.h840
-rw-r--r--js/src/vm/UnboxedObject.cpp1752
-rw-r--r--js/src/vm/UnboxedObject.h524
-rw-r--r--js/src/wasm/AsmJS.cpp40
-rw-r--r--js/src/wasm/WasmJS.cpp14
-rw-r--r--js/xpconnect/idl/moz.build1
-rw-r--r--js/xpconnect/idl/nsIScriptError.idl122
-rw-r--r--js/xpconnect/src/XPCComponents.cpp2
-rw-r--r--js/xpconnect/src/XPCConvert.cpp2
-rw-r--r--js/xpconnect/src/XPCModule.h4
-rw-r--r--js/xpconnect/src/XPCWrappedJSClass.cpp1
-rw-r--r--js/xpconnect/src/XPCWrappedNativeInfo.cpp1
-rw-r--r--js/xpconnect/src/moz.build3
-rw-r--r--js/xpconnect/src/nsScriptError.cpp345
-rw-r--r--js/xpconnect/src/nsScriptErrorWithStack.cpp118
-rw-r--r--js/xpconnect/src/nsXPConnect.cpp142
-rw-r--r--js/xpconnect/src/xpcprivate.h72
-rw-r--r--js/xpconnect/src/xpcpublic.h58
-rw-r--r--js/xpconnect/tests/chrome/test_bug1041626.xul3
-rw-r--r--js/xpconnect/wrappers/WrapperFactory.cpp2
267 files changed, 8604 insertions, 11135 deletions
diff --git a/js/ipc/JavaScriptParent.cpp b/js/ipc/JavaScriptParent.cpp
index 6cf9e0591..aafd7f565 100644
--- a/js/ipc/JavaScriptParent.cpp
+++ b/js/ipc/JavaScriptParent.cpp
@@ -9,6 +9,7 @@
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/ScriptSettings.h"
#include "nsJSUtils.h"
+#include "nsIScriptError.h"
#include "jsfriendapi.h"
#include "jswrapper.h"
#include "js/Proxy.h"
diff --git a/js/public/CallArgs.h b/js/public/CallArgs.h
index a7774309a..aae7121ad 100644
--- a/js/public/CallArgs.h
+++ b/js/public/CallArgs.h
@@ -128,7 +128,10 @@ class MOZ_STACK_CLASS CallArgsBase : public WantUsedRval
protected:
Value* argv_;
unsigned argc_;
- bool constructing_;
+ bool constructing_:1;
+
+ // True if the caller does not use the return value.
+ bool ignoresReturnValue_:1;
public:
// CALLEE ACCESS
@@ -164,6 +167,10 @@ class MOZ_STACK_CLASS CallArgsBase : public WantUsedRval
return true;
}
+ bool ignoresReturnValue() const {
+ return ignoresReturnValue_;
+ }
+
MutableHandleValue newTarget() const {
MOZ_ASSERT(constructing_);
return MutableHandleValue::fromMarkedLocation(&this->argv_[argc_]);
@@ -280,14 +287,17 @@ class MOZ_STACK_CLASS CallArgs : public detail::CallArgsBase<detail::IncludeUsed
{
private:
friend CallArgs CallArgsFromVp(unsigned argc, Value* vp);
- friend CallArgs CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing);
+ friend CallArgs CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing,
+ bool ignoresReturnValue);
- static CallArgs create(unsigned argc, Value* argv, bool constructing) {
+ static CallArgs create(unsigned argc, Value* argv, bool constructing,
+ bool ignoresReturnValue = false) {
CallArgs args;
args.clearUsedRval();
args.argv_ = argv;
args.argc_ = argc;
args.constructing_ = constructing;
+ args.ignoresReturnValue_ = ignoresReturnValue;
#ifdef DEBUG
for (unsigned i = 0; i < argc; ++i)
MOZ_ASSERT_IF(argv[i].isGCThing(), !GCThingIsMarkedGray(GCCellPtr(argv[i])));
@@ -314,9 +324,11 @@ CallArgsFromVp(unsigned argc, Value* vp)
// eventually move it to an internal header. Embedders should use
// JS::CallArgsFromVp!
MOZ_ALWAYS_INLINE CallArgs
-CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing = false)
+CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing = false,
+ bool ignoresReturnValue = false)
{
- return CallArgs::create(stackSlots - constructing, sp - stackSlots, constructing);
+ return CallArgs::create(stackSlots - constructing, sp - stackSlots, constructing,
+ ignoresReturnValue);
}
} // namespace JS
diff --git a/js/src/builtin/AtomicsObject.cpp b/js/src/builtin/AtomicsObject.cpp
index 2551f3b7d..ceee83349 100644
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -834,7 +834,7 @@ js::atomics_wait(JSContext* cx, unsigned argc, Value* vp)
}
bool
-js::atomics_wake(JSContext* cx, unsigned argc, Value* vp)
+js::atomics_notify(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
HandleValue objv = args.get(0);
@@ -874,7 +874,7 @@ js::atomics_wake(JSContext* cx, unsigned argc, Value* vp)
iter = iter->lower_pri;
if (c->offset != offset || !c->rt->fx.isWaiting())
continue;
- c->rt->fx.wake(FutexRuntime::WakeExplicit);
+ c->rt->fx.notify(FutexRuntime::NotifyExplicit);
++woken;
--count;
} while (count > 0 && iter != waiters);
@@ -950,7 +950,7 @@ js::FutexRuntime::isWaiting()
// When a worker is awoken for an interrupt it goes into state
// WaitingNotifiedForInterrupt for a short time before it actually
// wakes up and goes into WaitingInterrupted. In those states the
- // worker is still waiting, and if an explicit wake arrives the
+ // worker is still waiting, and if an explicit notify arrives the
// worker transitions to Woken. See further comments in
// FutexRuntime::wait().
return state_ == Waiting || state_ == WaitingInterrupted || state_ == WaitingNotifiedForInterrupt;
@@ -1029,14 +1029,14 @@ js::FutexRuntime::wait(JSContext* cx, js::UniqueLock<js::Mutex>& locked,
// should be woken when the interrupt handler returns.
// To that end, we flag the thread as interrupted around
// the interrupt and check state_ when the interrupt
- // handler returns. A wake() call that reaches the
+ // handler returns. A notify() call that reaches the
// runtime during the interrupt sets state_ to Woken.
//
// - It is in principle possible for wait() to be
// reentered on the same thread/runtime and waiting on the
// same location and to yet again be interrupted and enter
// the interrupt handler. In this case, it is important
- // that when another agent wakes waiters, all waiters using
+ // that when another agent notifies waiters, all waiters using
// the same runtime on the same location are woken in LIFO
// order; FIFO may be the required order, but FIFO would
// fail to wake up the innermost call. Interrupts are
@@ -1073,25 +1073,25 @@ finished:
}
void
-js::FutexRuntime::wake(WakeReason reason)
+js::FutexRuntime::notify(NotifyReason reason)
{
MOZ_ASSERT(isWaiting());
- if ((state_ == WaitingInterrupted || state_ == WaitingNotifiedForInterrupt) && reason == WakeExplicit) {
+ if ((state_ == WaitingInterrupted || state_ == WaitingNotifiedForInterrupt) && reason == NotifyExplicit) {
state_ = Woken;
return;
}
switch (reason) {
- case WakeExplicit:
+ case NotifyExplicit:
state_ = Woken;
break;
- case WakeForJSInterrupt:
+ case NotifyForJSInterrupt:
if (state_ == WaitingNotifiedForInterrupt)
return;
state_ = WaitingNotifiedForInterrupt;
break;
default:
- MOZ_CRASH("bad WakeReason in FutexRuntime::wake()");
+ MOZ_CRASH("bad NotifyReason in FutexRuntime::notify()");
}
cond_->notify_all();
}
@@ -1108,7 +1108,8 @@ const JSFunctionSpec AtomicsMethods[] = {
JS_INLINABLE_FN("xor", atomics_xor, 3,0, AtomicsXor),
JS_INLINABLE_FN("isLockFree", atomics_isLockFree, 1,0, AtomicsIsLockFree),
JS_FN("wait", atomics_wait, 4,0),
- JS_FN("wake", atomics_wake, 3,0),
+ JS_FN("notify", atomics_notify, 3,0),
+ JS_FN("wake", atomics_notify, 3,0), //Legacy name
JS_FS_END
};
@@ -1116,7 +1117,7 @@ JSObject*
AtomicsObject::initClass(JSContext* cx, Handle<GlobalObject*> global)
{
// Create Atomics Object.
- RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
+ RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
if (!objProto)
return nullptr;
RootedObject Atomics(cx, NewObjectWithGivenProto(cx, &AtomicsObject::class_, objProto,
diff --git a/js/src/builtin/AtomicsObject.h b/js/src/builtin/AtomicsObject.h
index adb6fb986..6511dc8bf 100644
--- a/js/src/builtin/AtomicsObject.h
+++ b/js/src/builtin/AtomicsObject.h
@@ -36,7 +36,7 @@ MOZ_MUST_USE bool atomics_or(JSContext* cx, unsigned argc, Value* vp);
MOZ_MUST_USE bool atomics_xor(JSContext* cx, unsigned argc, Value* vp);
MOZ_MUST_USE bool atomics_isLockFree(JSContext* cx, unsigned argc, Value* vp);
MOZ_MUST_USE bool atomics_wait(JSContext* cx, unsigned argc, Value* vp);
-MOZ_MUST_USE bool atomics_wake(JSContext* cx, unsigned argc, Value* vp);
+MOZ_MUST_USE bool atomics_notify(JSContext* cx, unsigned argc, Value* vp);
/* asm.js callouts */
namespace wasm { class Instance; }
@@ -63,10 +63,10 @@ public:
MOZ_MUST_USE bool initInstance();
void destroyInstance();
- // Parameters to wake().
- enum WakeReason {
- WakeExplicit, // Being asked to wake up by another thread
- WakeForJSInterrupt // Interrupt requested
+ // Parameters to notify().
+ enum NotifyReason {
+ NotifyExplicit, // Being asked to wake up by another thread
+ NotifyForJSInterrupt // Interrupt requested
};
// Result code from wait().
@@ -83,29 +83,27 @@ public:
// times allowed; specify mozilla::Nothing() for an indefinite
// wait.
//
- // wait() will not wake up spuriously. It will return true and
- // set *result to a return code appropriate for
- // Atomics.wait() on success, and return false on error.
+ // wait() will not wake up spuriously.
MOZ_MUST_USE bool wait(JSContext* cx, js::UniqueLock<js::Mutex>& locked,
mozilla::Maybe<mozilla::TimeDuration>& timeout, WaitResult* result);
- // Wake the thread represented by this Runtime.
+ // Notify the thread represented by this Runtime.
//
// The futex lock must be held around this call. (The sleeping
- // thread will not wake up until the caller of Atomics.wake()
+ // thread will not wake up until the caller of Atomics.notify()
// releases the lock.)
//
// If the thread is not waiting then this method does nothing.
//
// If the thread is waiting in a call to wait() and the
- // reason is WakeExplicit then the wait() call will return
+ // reason is NotifyExplicit then the wait() call will return
// with Woken.
//
// If the thread is waiting in a call to wait() and the
- // reason is WakeForJSInterrupt then the wait() will return
+ // reason is NotifyForJSInterrupt then the wait() will return
// with WaitingNotifiedForInterrupt; in the latter case the caller
// of wait() must handle the interrupt.
- void wake(WakeReason reason);
+ void notify(NotifyReason reason);
bool isWaiting();
@@ -128,7 +126,7 @@ public:
// interrupt handler
WaitingInterrupted, // We are waiting, but have been interrupted
// and are running the interrupt handler
- Woken // Woken by a script call to Atomics.wake
+ Woken // Woken by a script call to Atomics.notify
};
// Condition variable that this runtime will wait on.
diff --git a/js/src/builtin/Intl.cpp b/js/src/builtin/Intl.cpp
index 71f7b58d4..0cd0c62dd 100644
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -270,7 +270,7 @@ Collator(JSContext* cx, const CallArgs& args, bool construct)
// See https://github.com/tc39/ecma402/issues/57
if (!construct) {
// ES Intl 1st ed., 10.1.2.1 step 3
- JSObject* intl = cx->global()->getOrCreateIntlObject(cx);
+ JSObject* intl = GlobalObject::getOrCreateIntlObject(cx, cx->global());
if (!intl)
return false;
RootedValue self(cx, args.thisv());
@@ -298,7 +298,7 @@ Collator(JSContext* cx, const CallArgs& args, bool construct)
return false;
if (!proto) {
- proto = cx->global()->getOrCreateCollatorPrototype(cx);
+ proto = GlobalObject::getOrCreateCollatorPrototype(cx, cx->global());
if (!proto)
return false;
}
@@ -358,11 +358,12 @@ collator_finalize(FreeOp* fop, JSObject* obj)
static JSObject*
CreateCollatorPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
{
- RootedFunction ctor(cx, global->createConstructor(cx, &Collator, cx->names().Collator, 0));
+ RootedFunction ctor(cx, GlobalObject::createConstructor(cx, &Collator, cx->names().Collator,
+ 0));
if (!ctor)
return nullptr;
- RootedNativeObject proto(cx, global->createBlankPrototype(cx, &CollatorClass));
+ RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global, &CollatorClass));
if (!proto)
return nullptr;
proto->setReservedSlot(UCOLLATOR_SLOT, PrivateValue(nullptr));
@@ -772,7 +773,7 @@ NumberFormat(JSContext* cx, const CallArgs& args, bool construct)
// See https://github.com/tc39/ecma402/issues/57
if (!construct) {
// ES Intl 1st ed., 11.1.2.1 step 3
- JSObject* intl = cx->global()->getOrCreateIntlObject(cx);
+ JSObject* intl = GlobalObject::getOrCreateIntlObject(cx, cx->global());
if (!intl)
return false;
RootedValue self(cx, args.thisv());
@@ -800,7 +801,7 @@ NumberFormat(JSContext* cx, const CallArgs& args, bool construct)
return false;
if (!proto) {
- proto = cx->global()->getOrCreateNumberFormatPrototype(cx);
+ proto = GlobalObject::getOrCreateNumberFormatPrototype(cx, cx->global());
if (!proto)
return false;
}
@@ -862,11 +863,12 @@ static JSObject*
CreateNumberFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
{
RootedFunction ctor(cx);
- ctor = global->createConstructor(cx, &NumberFormat, cx->names().NumberFormat, 0);
+ ctor = GlobalObject::createConstructor(cx, &NumberFormat, cx->names().NumberFormat, 0);
if (!ctor)
return nullptr;
- RootedNativeObject proto(cx, global->createBlankPrototype(cx, &NumberFormatClass));
+ RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global,
+ &NumberFormatClass));
if (!proto)
return nullptr;
proto->setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
@@ -1250,7 +1252,7 @@ DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct)
// See https://github.com/tc39/ecma402/issues/57
if (!construct) {
// ES Intl 1st ed., 12.1.2.1 step 3
- JSObject* intl = cx->global()->getOrCreateIntlObject(cx);
+ JSObject* intl = GlobalObject::getOrCreateIntlObject(cx, cx->global());
if (!intl)
return false;
RootedValue self(cx, args.thisv());
@@ -1278,7 +1280,7 @@ DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct)
return false;
if (!proto) {
- proto = cx->global()->getOrCreateDateTimeFormatPrototype(cx);
+ proto = GlobalObject::getOrCreateDateTimeFormatPrototype(cx, cx->global());
if (!proto)
return false;
}
@@ -1340,11 +1342,12 @@ static JSObject*
CreateDateTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
{
RootedFunction ctor(cx);
- ctor = global->createConstructor(cx, &DateTimeFormat, cx->names().DateTimeFormat, 0);
+ ctor = GlobalObject::createConstructor(cx, &DateTimeFormat, cx->names().DateTimeFormat, 0);
if (!ctor)
return nullptr;
- RootedNativeObject proto(cx, global->createBlankPrototype(cx, &DateTimeFormatClass));
+ RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global,
+ &DateTimeFormatClass));
if (!proto)
return nullptr;
proto->setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(nullptr));
@@ -2731,10 +2734,10 @@ static const JSFunctionSpec intl_static_methods[] = {
* Initializes the Intl Object and its standard built-in properties.
* Spec: ECMAScript Internationalization API Specification, 8.0, 8.1
*/
-bool
+/* static */ bool
GlobalObject::initIntlObject(JSContext* cx, Handle<GlobalObject*> global)
{
- RootedObject proto(cx, global->getOrCreateObjectPrototype(cx));
+ RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
if (!proto)
return false;
diff --git a/js/src/builtin/IntlTimeZoneData.h b/js/src/builtin/IntlTimeZoneData.h
index fa808c0b9..8f963ffbc 100644
--- a/js/src/builtin/IntlTimeZoneData.h
+++ b/js/src/builtin/IntlTimeZoneData.h
@@ -1,5 +1,5 @@
// Generated by make_intl_data.py. DO NOT EDIT.
-// tzdata version = 2018e
+// tzdata version = 2019a
#ifndef builtin_IntlTimeZoneData_h
#define builtin_IntlTimeZoneData_h
diff --git a/js/src/builtin/MapObject.cpp b/js/src/builtin/MapObject.cpp
index c496cfb77..34e2e566d 100644
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -164,7 +164,7 @@ MapIteratorObject::kind() const
return MapObject::IteratorKind(i);
}
-bool
+/* static */ bool
GlobalObject::initMapIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
{
Rooted<JSObject*> base(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
@@ -924,7 +924,7 @@ SetIteratorObject::kind() const
return SetObject::IteratorKind(i);
}
-bool
+/* static */ bool
GlobalObject::initSetIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
{
Rooted<JSObject*> base(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp
index 710c7a76c..798ef46e1 100644
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -103,7 +103,7 @@ GlobalObject::initImportEntryProto(JSContext* cx, Handle<GlobalObject*> global)
JS_PS_END
};
- RootedObject proto(cx, global->createBlankPrototype<PlainObject>(cx));
+ RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
if (!proto)
return false;
@@ -169,7 +169,7 @@ GlobalObject::initExportEntryProto(JSContext* cx, Handle<GlobalObject*> global)
JS_PS_END
};
- RootedObject proto(cx, global->createBlankPrototype<PlainObject>(cx));
+ RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
if (!proto)
return false;
@@ -788,7 +788,7 @@ AssertModuleScopesMatch(ModuleObject* module)
}
void
-ModuleObject::fixEnvironmentsAfterCompartmentMerge(JSContext* cx)
+ModuleObject::fixEnvironmentsAfterCompartmentMerge()
{
AssertModuleScopesMatch(this);
initialEnvironment().fixEnclosingEnvironmentAfterCompartmentMerge(script()->global());
@@ -947,7 +947,7 @@ ModuleObject::evaluate(JSContext* cx, HandleModuleObject self, MutableHandleValu
ModuleObject::createNamespace(JSContext* cx, HandleModuleObject self, HandleObject exports)
{
MOZ_ASSERT(!self->namespace_());
- MOZ_ASSERT(exports->is<ArrayObject>() || exports->is<UnboxedArrayObject>());
+ MOZ_ASSERT(exports->is<ArrayObject>());
RootedModuleNamespaceObject ns(cx, ModuleNamespaceObject::create(cx, self));
if (!ns)
@@ -1020,7 +1020,7 @@ GlobalObject::initModuleProto(JSContext* cx, Handle<GlobalObject*> global)
JS_FS_END
};
- RootedObject proto(cx, global->createBlankPrototype<PlainObject>(cx));
+ RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
if (!proto)
return false;
diff --git a/js/src/builtin/ModuleObject.h b/js/src/builtin/ModuleObject.h
index d0ed8ed08..e83520ebe 100644
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -244,7 +244,7 @@ class ModuleObject : public NativeObject
#ifdef DEBUG
static bool IsFrozen(JSContext* cx, HandleModuleObject self);
#endif
- void fixEnvironmentsAfterCompartmentMerge(JSContext* cx);
+ void fixEnvironmentsAfterCompartmentMerge();
JSScript* script() const;
Scope* enclosingScope() const;
diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp
index 389bb57db..56c77f304 100644
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -525,18 +525,6 @@ js::obj_toString(JSContext* cx, unsigned argc, Value* vp)
return true;
}
-
-bool
-js::obj_valueOf(JSContext* cx, unsigned argc, Value* vp)
-{
- CallArgs args = CallArgsFromVp(argc, vp);
- RootedObject obj(cx, ToObject(cx, args.thisv()));
- if (!obj)
- return false;
- args.rval().setObject(*obj);
- return true;
-}
-
static bool
obj_setPrototypeOf(JSContext* cx, unsigned argc, Value* vp)
{
@@ -1301,7 +1289,7 @@ static const JSFunctionSpec object_methods[] = {
#endif
JS_FN(js_toString_str, obj_toString, 0,0),
JS_SELF_HOSTED_FN(js_toLocaleString_str, "Object_toLocaleString", 0, 0),
- JS_FN(js_valueOf_str, obj_valueOf, 0,0),
+ JS_SELF_HOSTED_FN(js_valueOf_str, "Object_valueOf", 0,0),
#if JS_HAS_OBJ_WATCHPOINT
JS_FN(js_watch_str, obj_watch, 2,0),
JS_FN(js_unwatch_str, obj_unwatch, 1,0),
@@ -1420,8 +1408,8 @@ FinishObjectClassInit(JSContext* cx, JS::HandleObject ctor, JS::HandleObject pro
* only set the [[Prototype]] if it hasn't already been set.
*/
Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
- if (global->shouldSplicePrototype(cx)) {
- if (!global->splicePrototype(cx, global->getClass(), tagged))
+ if (global->shouldSplicePrototype()) {
+ if (!JSObject::splicePrototype(cx, global, global->getClass(), tagged))
return false;
}
return true;
diff --git a/js/src/builtin/Object.h b/js/src/builtin/Object.h
index 09512be36..8231888b1 100644
--- a/js/src/builtin/Object.h
+++ b/js/src/builtin/Object.h
@@ -25,9 +25,6 @@ obj_construct(JSContext* cx, unsigned argc, JS::Value* vp);
MOZ_MUST_USE bool
obj_propertyIsEnumerable(JSContext* cx, unsigned argc, Value* vp);
-MOZ_MUST_USE bool
-obj_valueOf(JSContext* cx, unsigned argc, JS::Value* vp);
-
PlainObject*
ObjectCreateImpl(JSContext* cx, HandleObject proto, NewObjectKind newKind = GenericObject,
HandleObjectGroup group = nullptr);
diff --git a/js/src/builtin/Object.js b/js/src/builtin/Object.js
index a7440aec7..9ed1be0e1 100644
--- a/js/src/builtin/Object.js
+++ b/js/src/builtin/Object.js
@@ -87,6 +87,12 @@ function Object_toLocaleString() {
return callContentFunction(O.toString, O);
}
+// ES 2017 draft bb96899bb0d9ef9be08164a26efae2ee5f25e875 19.1.3.7
+function Object_valueOf() {
+ // Step 1.
+ return ToObject(this);
+}
+
// ES7 draft (2016 March 8) B.2.2.3
function ObjectDefineSetter(name, setter) {
// Step 1.
diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp
index c781a336d..ec7845e89 100644
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -603,7 +603,7 @@ ResolvePromise(JSContext* cx, Handle<PromiseObject*> promise, HandleValue valueO
// Now that everything else is done, do the things the debugger needs.
// Step 7 of RejectPromise implemented in onSettled.
- promise->onSettled(cx);
+ PromiseObject::onSettled(cx, promise);
// Step 7 of FulfillPromise.
// Step 8 of RejectPromise.
@@ -1947,26 +1947,23 @@ PerformPromiseRace(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C,
}
// ES2016, Sub-steps of 25.4.4.4 and 25.4.4.5.
-static MOZ_MUST_USE bool
-CommonStaticResolveRejectImpl(JSContext* cx, unsigned argc, Value* vp, ResolutionMode mode)
+static MOZ_MUST_USE JSObject*
+CommonStaticResolveRejectImpl(JSContext* cx, HandleValue thisVal, HandleValue argVal,
+ ResolutionMode mode)
{
- CallArgs args = CallArgsFromVp(argc, vp);
- RootedValue x(cx, args.get(0));
-
// Steps 1-2.
- if (!args.thisv().isObject()) {
+ if (!thisVal.isObject()) {
const char* msg = mode == ResolveMode
? "Receiver of Promise.resolve call"
: "Receiver of Promise.reject call";
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, msg);
- return false;
+ return nullptr;
}
- RootedValue cVal(cx, args.thisv());
- RootedObject C(cx, &cVal.toObject());
+ RootedObject C(cx, &thisVal.toObject());
// Step 3 of Resolve.
- if (mode == ResolveMode && x.isObject()) {
- RootedObject xObj(cx, &x.toObject());
+ if (mode == ResolveMode && argVal.isObject()) {
+ RootedObject xObj(cx, &argVal.toObject());
bool isPromise = false;
if (xObj->is<PromiseObject>()) {
isPromise = true;
@@ -1985,11 +1982,9 @@ CommonStaticResolveRejectImpl(JSContext* cx, unsigned argc, Value* vp, Resolutio
if (isPromise) {
RootedValue ctorVal(cx);
if (!GetProperty(cx, xObj, xObj, cx->names().constructor, &ctorVal))
- return false;
- if (ctorVal == cVal) {
- args.rval().set(x);
- return true;
- }
+ return nullptr;
+ if (ctorVal == thisVal)
+ return xObj;
}
}
@@ -1998,15 +1993,17 @@ CommonStaticResolveRejectImpl(JSContext* cx, unsigned argc, Value* vp, Resolutio
RootedObject resolveFun(cx);
RootedObject rejectFun(cx);
if (!NewPromiseCapability(cx, C, &promise, &resolveFun, &rejectFun, true))
- return false;
+ return nullptr;
// Step 5 of Resolve, 4 of Reject.
- if (!RunResolutionFunction(cx, mode == ResolveMode ? resolveFun : rejectFun, x, mode, promise))
- return false;
+ if (!RunResolutionFunction(cx, mode == ResolveMode ? resolveFun : rejectFun, argVal, mode,
+ promise))
+ {
+ return nullptr;
+ }
// Step 6 of Resolve, 4 of Reject.
- args.rval().setObject(*promise);
- return true;
+ return promise;
}
/**
@@ -2015,7 +2012,14 @@ CommonStaticResolveRejectImpl(JSContext* cx, unsigned argc, Value* vp, Resolutio
bool
js::Promise_reject(JSContext* cx, unsigned argc, Value* vp)
{
- return CommonStaticResolveRejectImpl(cx, argc, vp, RejectMode);
+ CallArgs args = CallArgsFromVp(argc, vp);
+ RootedValue thisVal(cx, args.thisv());
+ RootedValue argVal(cx, args.get(0));
+ JSObject* result = CommonStaticResolveRejectImpl(cx, thisVal, argVal, RejectMode);
+ if (!result)
+ return false;
+ args.rval().setObject(*result);
+ return true;
}
/**
@@ -2024,19 +2028,11 @@ js::Promise_reject(JSContext* cx, unsigned argc, Value* vp)
/* static */ JSObject*
PromiseObject::unforgeableReject(JSContext* cx, HandleValue value)
{
- // Steps 1-2 (omitted).
-
- // Roughly step 3.
- Rooted<PromiseObject*> promise(cx, CreatePromiseObjectInternal(cx));
- if (!promise)
+ RootedObject promiseCtor(cx, JS::GetPromiseConstructor(cx));
+ if (!promiseCtor)
return nullptr;
-
- // Roughly step 4.
- if (!ResolvePromise(cx, promise, value, JS::PromiseState::Rejected))
- return nullptr;
-
- // Step 5.
- return promise;
+ RootedValue cVal(cx, ObjectValue(*promiseCtor));
+ return CommonStaticResolveRejectImpl(cx, cVal, value, RejectMode);
}
/**
@@ -2045,7 +2041,14 @@ PromiseObject::unforgeableReject(JSContext* cx, HandleValue value)
bool
js::Promise_static_resolve(JSContext* cx, unsigned argc, Value* vp)
{
- return CommonStaticResolveRejectImpl(cx, argc, vp, ResolveMode);
+ CallArgs args = CallArgsFromVp(argc, vp);
+ RootedValue thisVal(cx, args.thisv());
+ RootedValue argVal(cx, args.get(0));
+ JSObject* result = CommonStaticResolveRejectImpl(cx, thisVal, argVal, ResolveMode);
+ if (!result)
+ return false;
+ args.rval().setObject(*result);
+ return true;
}
/**
@@ -2054,30 +2057,11 @@ js::Promise_static_resolve(JSContext* cx, unsigned argc, Value* vp)
/* static */ JSObject*
PromiseObject::unforgeableResolve(JSContext* cx, HandleValue value)
{
- // Steps 1-2 (omitted).
-
- // Step 3.
- if (value.isObject()) {
- JSObject* obj = &value.toObject();
- if (IsWrapper(obj))
- obj = CheckedUnwrap(obj);
- // Instead of getting the `constructor` property, do an unforgeable
- // check.
- if (obj && obj->is<PromiseObject>())
- return obj;
- }
-
- // Step 4.
- Rooted<PromiseObject*> promise(cx, CreatePromiseObjectInternal(cx));
- if (!promise)
+ RootedObject promiseCtor(cx, JS::GetPromiseConstructor(cx));
+ if (!promiseCtor)
return nullptr;
-
- // Steps 5.
- if (!ResolvePromiseInternal(cx, promise, value))
- return nullptr;
-
- // Step 6.
- return promise;
+ RootedValue cVal(cx, ObjectValue(*promiseCtor));
+ return CommonStaticResolveRejectImpl(cx, cVal, value, ResolveMode);
}
// ES2016, 25.4.4.6, implemented in Promise.js.
@@ -2647,14 +2631,14 @@ PromiseObject::dependentPromises(JSContext* cx, MutableHandle<GCVector<Value>> v
return true;
}
-bool
-PromiseObject::resolve(JSContext* cx, HandleValue resolutionValue)
+/* static */ bool
+PromiseObject::resolve(JSContext* cx, Handle<PromiseObject*> promise, HandleValue resolutionValue)
{
- MOZ_ASSERT(!PromiseHasAnyFlag(*this, PROMISE_FLAG_ASYNC));
- if (state() != JS::PromiseState::Pending)
+ MOZ_ASSERT(!PromiseHasAnyFlag(*promise, PROMISE_FLAG_ASYNC));
+ if (promise->state() != JS::PromiseState::Pending)
return true;
- RootedObject resolveFun(cx, GetResolveFunctionFromPromise(this));
+ RootedObject resolveFun(cx, GetResolveFunctionFromPromise(promise));
RootedValue funVal(cx, ObjectValue(*resolveFun));
// For xray'd Promises, the resolve fun may have been created in another
@@ -2670,14 +2654,14 @@ PromiseObject::resolve(JSContext* cx, HandleValue resolutionValue)
return Call(cx, funVal, UndefinedHandleValue, args, &dummy);
}
-bool
-PromiseObject::reject(JSContext* cx, HandleValue rejectionValue)
+/* static */ bool
+PromiseObject::reject(JSContext* cx, Handle<PromiseObject*> promise, HandleValue rejectionValue)
{
- MOZ_ASSERT(!PromiseHasAnyFlag(*this, PROMISE_FLAG_ASYNC));
- if (state() != JS::PromiseState::Pending)
+ MOZ_ASSERT(!PromiseHasAnyFlag(*promise, PROMISE_FLAG_ASYNC));
+ if (promise->state() != JS::PromiseState::Pending)
return true;
- RootedValue funVal(cx, this->getFixedSlot(PromiseSlot_RejectFunction));
+ RootedValue funVal(cx, promise->getFixedSlot(PromiseSlot_RejectFunction));
MOZ_ASSERT(IsCallable(funVal));
FixedInvokeArgs<1> args(cx);
@@ -2687,10 +2671,9 @@ PromiseObject::reject(JSContext* cx, HandleValue rejectionValue)
return Call(cx, funVal, UndefinedHandleValue, args, &dummy);
}
-void
-PromiseObject::onSettled(JSContext* cx)
+/* static */ void
+PromiseObject::onSettled(JSContext* cx, Handle<PromiseObject*> promise)
{
- Rooted<PromiseObject*> promise(cx, this);
RootedObject stack(cx);
if (cx->options().asyncStack() || cx->compartment()->isDebuggee()) {
if (!JS::CaptureCurrentStack(cx, &stack, JS::StackCapture(JS::AllFrames()))) {
@@ -2750,7 +2733,7 @@ PromiseTask::executeAndFinish(JSContext* cx)
static JSObject*
CreatePromisePrototype(JSContext* cx, JSProtoKey key)
{
- return cx->global()->createBlankPrototype(cx, &PromiseObject::protoClass_);
+ return GlobalObject::createBlankPrototype(cx, cx->global(), &PromiseObject::protoClass_);
}
static const JSFunctionSpec promise_methods[] = {
diff --git a/js/src/builtin/Promise.h b/js/src/builtin/Promise.h
index bb4778631..c76dc358c 100644
--- a/js/src/builtin/Promise.h
+++ b/js/src/builtin/Promise.h
@@ -66,10 +66,12 @@ class PromiseObject : public NativeObject
return getFixedSlot(PromiseSlot_ReactionsOrResult);
}
- MOZ_MUST_USE bool resolve(JSContext* cx, HandleValue resolutionValue);
- MOZ_MUST_USE bool reject(JSContext* cx, HandleValue rejectionValue);
+ static MOZ_MUST_USE bool resolve(JSContext* cx, Handle<PromiseObject*> promise,
+ HandleValue resolutionValue);
+ static MOZ_MUST_USE bool reject(JSContext* cx, Handle<PromiseObject*> promise,
+ HandleValue rejectionValue);
- void onSettled(JSContext* cx);
+ static void onSettled(JSContext* cx, Handle<PromiseObject*> promise);
double allocationTime() { return getFixedSlot(PromiseSlot_AllocationTime).toNumber(); }
double resolutionTime() { return getFixedSlot(PromiseSlot_ResolutionTime).toNumber(); }
diff --git a/js/src/builtin/Reflect.cpp b/js/src/builtin/Reflect.cpp
index 2f509a226..4e7fae78c 100644
--- a/js/src/builtin/Reflect.cpp
+++ b/js/src/builtin/Reflect.cpp
@@ -268,7 +268,8 @@ static const JSFunctionSpec methods[] = {
JSObject*
js::InitReflect(JSContext* cx, HandleObject obj)
{
- RootedObject proto(cx, obj->as<GlobalObject>().getOrCreateObjectPrototype(cx));
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
+ RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
if (!proto)
return nullptr;
diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp
index beff58e13..8e8bb2417 100644
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -3044,7 +3044,12 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
RootedValue expr(cx);
- expr.setString(next->pn_atom);
+ if (next->isKind(PNK_RAW_UNDEFINED)) {
+ expr.setUndefined();
+ } else {
+ MOZ_ASSERT(next->isKind(PNK_TEMPLATE_STRING));
+ expr.setString(next->pn_atom);
+ }
cooked.infallibleAppend(expr);
}
@@ -3136,6 +3141,7 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
case PNK_TRUE:
case PNK_FALSE:
case PNK_NULL:
+ case PNK_RAW_UNDEFINED:
return literal(pn, dst);
case PNK_YIELD_STAR:
@@ -3216,6 +3222,8 @@ ASTSerializer::property(ParseNode* pn, MutableHandleValue dst)
return expression(pn->pn_kid, &val) &&
builder.prototypeMutation(val, &pn->pn_pos, dst);
}
+ if (pn->isKind(PNK_SPREAD))
+ return expression(pn, dst);
PropKind kind;
switch (pn->getOp()) {
@@ -3276,6 +3284,10 @@ ASTSerializer::literal(ParseNode* pn, MutableHandleValue dst)
val.setNull();
break;
+ case PNK_RAW_UNDEFINED:
+ val.setUndefined();
+ break;
+
case PNK_TRUE:
val.setBoolean(true);
break;
@@ -3332,6 +3344,16 @@ ASTSerializer::objectPattern(ParseNode* pn, MutableHandleValue dst)
return false;
for (ParseNode* propdef = pn->pn_head; propdef; propdef = propdef->pn_next) {
+ if (propdef->isKind(PNK_SPREAD)) {
+ RootedValue target(cx);
+ RootedValue spread(cx);
+ if (!pattern(propdef->pn_kid, &target))
+ return false;
+ if(!builder.spreadExpression(target, &propdef->pn_pos, &spread))
+ return false;
+ elts.infallibleAppend(spread);
+ continue;
+ }
LOCAL_ASSERT(propdef->isKind(PNK_MUTATEPROTO) != propdef->isOp(JSOP_INITPROP));
RootedValue key(cx);
@@ -3407,12 +3429,7 @@ ASTSerializer::function(ParseNode* pn, ASTType type, MutableHandleValue dst)
: GeneratorStyle::None;
bool isAsync = pn->pn_funbox->isAsync();
- bool isExpression =
-#if JS_HAS_EXPR_CLOSURES
- func->isExprBody();
-#else
- false;
-#endif
+ bool isExpression = pn->pn_funbox->isExprBody();
RootedValue id(cx);
RootedAtom funcAtom(cx, func->explicitName());
diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp
index b20f41c53..7cf20d23c 100644
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -140,12 +140,12 @@ ExecuteRegExpImpl(JSContext* cx, RegExpStatics* res, RegExpShared& re, HandleLin
/* Legacy ExecuteRegExp behavior is baked into the JSAPI. */
bool
-js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, RegExpObject& reobj,
+js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, Handle<RegExpObject*> reobj,
HandleLinearString input, size_t* lastIndex, bool test,
MutableHandleValue rval)
{
RegExpGuard shared(cx);
- if (!reobj.getShared(cx, &shared))
+ if (!RegExpObject::getShared(cx, reobj, &shared))
return false;
ScopedMatchPairs matches(&cx->tempLifoAlloc());
@@ -801,7 +801,7 @@ const JSFunctionSpec js::regexp_methods[] = {
name(JSContext* cx, unsigned argc, Value* vp) \
{ \
CallArgs args = CallArgsFromVp(argc, vp); \
- RegExpStatics* res = cx->global()->getRegExpStatics(cx); \
+ RegExpStatics* res = GlobalObject::getRegExpStatics(cx, cx->global()); \
if (!res) \
return false; \
code; \
@@ -827,7 +827,7 @@ DEFINE_STATIC_GETTER(static_paren9_getter, STATIC_PAREN_GETTER_CODE(9))
static bool \
name(JSContext* cx, unsigned argc, Value* vp) \
{ \
- RegExpStatics* res = cx->global()->getRegExpStatics(cx); \
+ RegExpStatics* res = GlobalObject::getRegExpStatics(cx, cx->global()); \
if (!res) \
return false; \
code; \
@@ -838,7 +838,7 @@ static bool
static_input_setter(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
- RegExpStatics* res = cx->global()->getRegExpStatics(cx);
+ RegExpStatics* res = GlobalObject::getRegExpStatics(cx, cx->global());
if (!res)
return false;
@@ -918,12 +918,12 @@ ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
Rooted<RegExpObject*> reobj(cx, &regexp->as<RegExpObject>());
RegExpGuard re(cx);
- if (!reobj->getShared(cx, &re))
+ if (!RegExpObject::getShared(cx, reobj, &re))
return RegExpRunStatus_Error;
RegExpStatics* res;
if (staticsUpdate == UpdateRegExpStatics) {
- res = cx->global()->getRegExpStatics(cx);
+ res = GlobalObject::getRegExpStatics(cx, cx->global());
if (!res)
return RegExpRunStatus_Error;
} else {
@@ -1725,7 +1725,7 @@ js::intrinsic_GetElemBaseForLambda(JSContext* cx, unsigned argc, Value* vp)
if (!fun->isInterpreted() || fun->isClassConstructor())
return true;
- JSScript* script = fun->getOrCreateScript(cx);
+ JSScript* script = JSFunction::getOrCreateScript(cx, fun);
if (!script)
return false;
@@ -1800,7 +1800,7 @@ js::intrinsic_GetStringDataProperty(JSContext* cx, unsigned argc, Value* vp)
return false;
RootedValue v(cx);
- if (HasDataProperty(cx, nobj, AtomToId(atom), v.address()) && v.isString())
+ if (GetPropertyPure(cx, nobj, AtomToId(atom), v.address()) && v.isString())
args.rval().set(v);
else
args.rval().setUndefined();
diff --git a/js/src/builtin/RegExp.h b/js/src/builtin/RegExp.h
index 715656f40..4e0ff6948 100644
--- a/js/src/builtin/RegExp.h
+++ b/js/src/builtin/RegExp.h
@@ -31,7 +31,7 @@ enum RegExpStaticsUpdate { UpdateRegExpStatics, DontUpdateRegExpStatics };
* |chars| and |length|.
*/
MOZ_MUST_USE bool
-ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, RegExpObject& reobj,
+ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, Handle<RegExpObject*> reobj,
HandleLinearString input, size_t* lastIndex, bool test,
MutableHandleValue rval);
diff --git a/js/src/builtin/SIMD.cpp b/js/src/builtin/SIMD.cpp
index 2383922db..5fe691152 100644
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -475,7 +475,7 @@ const Class SimdObject::class_ = {
&SimdObjectClassOps
};
-bool
+/* static */ bool
GlobalObject::initSimdObject(JSContext* cx, Handle<GlobalObject*> global)
{
// SIMD relies on the TypedObject module being initialized.
@@ -483,11 +483,11 @@ GlobalObject::initSimdObject(JSContext* cx, Handle<GlobalObject*> global)
// to be able to call GetTypedObjectModule(). It is NOT necessary
// to install the TypedObjectModule global, but at the moment
// those two things are not separable.
- if (!global->getOrCreateTypedObjectModule(cx))
+ if (!GlobalObject::getOrCreateTypedObjectModule(cx, global))
return false;
RootedObject globalSimdObject(cx);
- RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
+ RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
if (!objProto)
return false;
@@ -510,7 +510,7 @@ static bool
CreateSimdType(JSContext* cx, Handle<GlobalObject*> global, HandlePropertyName stringRepr,
SimdType simdType, const JSFunctionSpec* methods)
{
- RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx));
+ RootedObject funcProto(cx, GlobalObject::getOrCreateFunctionPrototype(cx, global));
if (!funcProto)
return false;
@@ -531,7 +531,7 @@ CreateSimdType(JSContext* cx, Handle<GlobalObject*> global, HandlePropertyName s
return false;
// Create prototype property, which inherits from Object.prototype.
- RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
+ RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
if (!objProto)
return false;
Rooted<TypedProto*> proto(cx);
@@ -551,7 +551,7 @@ CreateSimdType(JSContext* cx, Handle<GlobalObject*> global, HandlePropertyName s
}
// Bind type descriptor to the global SIMD object
- RootedObject globalSimdObject(cx, global->getOrCreateSimdGlobalObject(cx));
+ RootedObject globalSimdObject(cx, GlobalObject::getOrCreateSimdGlobalObject(cx, global));
MOZ_ASSERT(globalSimdObject);
RootedValue typeValue(cx, ObjectValue(*typeDescr));
@@ -568,7 +568,7 @@ CreateSimdType(JSContext* cx, Handle<GlobalObject*> global, HandlePropertyName s
return !!typeDescr;
}
-bool
+/* static */ bool
GlobalObject::initSimdType(JSContext* cx, Handle<GlobalObject*> global, SimdType simdType)
{
#define CREATE_(Type) \
@@ -584,13 +584,13 @@ GlobalObject::initSimdType(JSContext* cx, Handle<GlobalObject*> global, SimdType
#undef CREATE_
}
-SimdTypeDescr*
+/* static */ SimdTypeDescr*
GlobalObject::getOrCreateSimdTypeDescr(JSContext* cx, Handle<GlobalObject*> global,
SimdType simdType)
{
MOZ_ASSERT(unsigned(simdType) < unsigned(SimdType::Count), "Invalid SIMD type");
- RootedObject globalSimdObject(cx, global->getOrCreateSimdGlobalObject(cx));
+ RootedObject globalSimdObject(cx, GlobalObject::getOrCreateSimdGlobalObject(cx, global));
if (!globalSimdObject)
return nullptr;
@@ -628,8 +628,8 @@ SimdObject::resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool*
JSObject*
js::InitSimdClass(JSContext* cx, HandleObject obj)
{
- Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
- return global->getOrCreateSimdGlobalObject(cx);
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
+ return GlobalObject::getOrCreateSimdGlobalObject(cx, global);
}
template<typename V>
diff --git a/js/src/builtin/SymbolObject.cpp b/js/src/builtin/SymbolObject.cpp
index ae38d5371..8fa860ef3 100644
--- a/js/src/builtin/SymbolObject.cpp
+++ b/js/src/builtin/SymbolObject.cpp
@@ -53,17 +53,17 @@ const JSFunctionSpec SymbolObject::staticMethods[] = {
JSObject*
SymbolObject::initClass(JSContext* cx, HandleObject obj)
{
- Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
// This uses &JSObject::class_ because: "The Symbol prototype object is an
// ordinary object. It is not a Symbol instance and does not have a
// [[SymbolData]] internal slot." (ES6 rev 24, 19.4.3)
- RootedObject proto(cx, global->createBlankPrototype<PlainObject>(cx));
+ RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
if (!proto)
return nullptr;
- RootedFunction ctor(cx, global->createConstructor(cx, construct,
- ClassName(JSProto_Symbol, cx), 0));
+ RootedFunction ctor(cx, GlobalObject::createConstructor(cx, construct,
+ ClassName(JSProto_Symbol, cx), 0));
if (!ctor)
return nullptr;
diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp
index c896ce5d1..992fe2c97 100644
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -3218,13 +3218,13 @@ ByteSizeOfScript(JSContext*cx, unsigned argc, Value* vp)
return false;
}
- JSFunction* fun = &args[0].toObject().as<JSFunction>();
+ RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
if (fun->isNative()) {
JS_ReportErrorASCII(cx, "Argument must be a scripted function");
return false;
}
- RootedScript script(cx, fun->getOrCreateScript(cx));
+ RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
if (!script)
return false;
@@ -3319,7 +3319,8 @@ GetConstructorName(JSContext* cx, unsigned argc, Value* vp)
}
RootedAtom name(cx);
- if (!args[0].toObject().constructorDisplayAtom(cx, &name))
+ RootedObject obj(cx, &args[0].toObject());
+ if (!JSObject::constructorDisplayAtom(cx, obj, &name))
return false;
if (name) {
@@ -4023,7 +4024,7 @@ DisRegExp(JSContext* cx, unsigned argc, Value* vp)
return false;
}
- if (!reobj->dumpBytecode(cx, match_only, input))
+ if (!RegExpObject::dumpBytecode(cx, reobj, match_only, input))
return false;
args.rval().setUndefined();
@@ -4042,6 +4043,32 @@ IsConstructor(JSContext* cx, unsigned argc, Value* vp)
return true;
}
+static bool
+GetErrorNotes(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (!args.requireAtLeast(cx, "getErrorNotes", 1))
+ return false;
+
+ if (!args[0].isObject() || !args[0].toObject().is<ErrorObject>()) {
+ args.rval().setNull();
+ return true;
+ }
+
+ JSErrorReport* report = args[0].toObject().as<ErrorObject>().getErrorReport();
+ if (!report) {
+ args.rval().setNull();
+ return true;
+ }
+
+ RootedObject notesArray(cx, CreateErrorNotesArray(cx, report));
+ if (!notesArray)
+ return false;
+
+ args.rval().setObject(*notesArray);
+ return true;
+}
+
static const JSFunctionSpecWithHelp TestingFunctions[] = {
JS_FN_HELP("gc", ::GC, 0, 0,
"gc([obj] | 'zone' [, 'shrinking'])",
@@ -4576,6 +4603,10 @@ static const JSFunctionSpecWithHelp FuzzingUnsafeTestingFunctions[] = {
" Dumps RegExp bytecode."),
#endif
+ JS_FN_HELP("getErrorNotes", GetErrorNotes, 1, 0,
+"getErrorNotes(error)",
+" Returns an array of error notes."),
+
JS_FS_HELP_END
};
diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp
index 0dfc1123a..ff3680774 100644
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1124,11 +1124,11 @@ DefineSimpleTypeDescr(JSContext* cx,
typename T::Type type,
HandlePropertyName className)
{
- RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
+ RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
if (!objProto)
return false;
- RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx));
+ RootedObject funcProto(cx, GlobalObject::getOrCreateFunctionPrototype(cx, global));
if (!funcProto)
return false;
@@ -1185,7 +1185,7 @@ DefineMetaTypeDescr(JSContext* cx,
if (!className)
return nullptr;
- RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx));
+ RootedObject funcProto(cx, GlobalObject::getOrCreateFunctionPrototype(cx, global));
if (!funcProto)
return nullptr;
@@ -1197,7 +1197,7 @@ DefineMetaTypeDescr(JSContext* cx,
// Create ctor.prototype.prototype, which inherits from Object.__proto__
- RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
+ RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
if (!objProto)
return nullptr;
RootedObject protoProto(cx);
@@ -1216,7 +1216,7 @@ DefineMetaTypeDescr(JSContext* cx,
const int constructorLength = 2;
RootedFunction ctor(cx);
- ctor = global->createConstructor(cx, T::construct, className, constructorLength);
+ ctor = GlobalObject::createConstructor(cx, T::construct, className, constructorLength);
if (!ctor ||
!LinkConstructorAndPrototype(cx, ctor, proto) ||
!DefinePropertiesAndFunctions(cx, proto,
@@ -1240,10 +1240,10 @@ DefineMetaTypeDescr(JSContext* cx,
* initializer for the `TypedObject` class populate the
* `TypedObject` global (which is referred to as "module" herein).
*/
-bool
+/* static */ bool
GlobalObject::initTypedObjectModule(JSContext* cx, Handle<GlobalObject*> global)
{
- RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
+ RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
if (!objProto)
return false;
@@ -1317,9 +1317,8 @@ GlobalObject::initTypedObjectModule(JSContext* cx, Handle<GlobalObject*> global)
JSObject*
js::InitTypedObjectModuleObject(JSContext* cx, HandleObject obj)
{
- MOZ_ASSERT(obj->is<GlobalObject>());
- Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
- return global->getOrCreateTypedObjectModule(cx);
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
+ return GlobalObject::getOrCreateTypedObjectModule(cx, global);
}
/******************************************************************************
diff --git a/js/src/builtin/Utilities.js b/js/src/builtin/Utilities.js
index c73bc5e7f..d5f233d05 100644
--- a/js/src/builtin/Utilities.js
+++ b/js/src/builtin/Utilities.js
@@ -229,6 +229,69 @@ function GetInternalError(msg) {
// To be used when a function is required but calling it shouldn't do anything.
function NullFunction() {}
+// Object Rest/Spread Properties proposal
+// Abstract operation: CopyDataProperties (target, source, excluded)
+function CopyDataProperties(target, source, excluded) {
+ // Step 1.
+ assert(IsObject(target), "target is an object");
+
+ // Step 2.
+ assert(IsObject(excluded), "excluded is an object");
+
+ // Steps 3, 6.
+ if (source === undefined || source === null)
+ return;
+
+ // Step 4.a.
+ source = ToObject(source);
+
+ // Step 4.b.
+ var keys = OwnPropertyKeys(source, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS);
+
+ // Step 5.
+ for (var index = 0; index < keys.length; index++) {
+ var key = keys[index];
+
+ // We abbreviate this by calling propertyIsEnumerable which is faster
+ // and returns false for not defined properties.
+ if (!callFunction(std_Object_hasOwnProperty, excluded, key) && callFunction(std_Object_propertyIsEnumerable, source, key))
+ _DefineDataProperty(target, key, source[key]);
+ }
+
+ // Step 6 (Return).
+}
+
+// Object Rest/Spread Properties proposal
+// Abstract operation: CopyDataProperties (target, source, excluded)
+function CopyDataPropertiesUnfiltered(target, source) {
+ // Step 1.
+ assert(IsObject(target), "target is an object");
+
+ // Step 2 (Not applicable).
+
+ // Steps 3, 6.
+ if (source === undefined || source === null)
+ return;
+
+ // Step 4.a.
+ source = ToObject(source);
+
+ // Step 4.b.
+ var keys = OwnPropertyKeys(source, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS);
+
+ // Step 5.
+ for (var index = 0; index < keys.length; index++) {
+ var key = keys[index];
+
+ // We abbreviate this by calling propertyIsEnumerable which is faster
+ // and returns false for not defined properties.
+ if (callFunction(std_Object_propertyIsEnumerable, source, key))
+ _DefineDataProperty(target, key, source[key]);
+ }
+
+ // Step 6 (Return).
+}
+
/*************************************** Testing functions ***************************************/
function outer() {
return function inner() {
diff --git a/js/src/builtin/WeakMapObject.cpp b/js/src/builtin/WeakMapObject.cpp
index 555b9e03a..dcfa19776 100644
--- a/js/src/builtin/WeakMapObject.cpp
+++ b/js/src/builtin/WeakMapObject.cpp
@@ -350,14 +350,14 @@ InitWeakMapClass(JSContext* cx, HandleObject obj, bool defineMembers)
{
MOZ_ASSERT(obj->isNative());
- Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
RootedPlainObject proto(cx, NewBuiltinClassInstance<PlainObject>(cx));
if (!proto)
return nullptr;
- RootedFunction ctor(cx, global->createConstructor(cx, WeakMap_construct,
- cx->names().WeakMap, 0));
+ RootedFunction ctor(cx, GlobalObject::createConstructor(cx, WeakMap_construct,
+ cx->names().WeakMap, 0));
if (!ctor)
return nullptr;
diff --git a/js/src/builtin/WeakSetObject.cpp b/js/src/builtin/WeakSetObject.cpp
index 7ea3f2fef..fbe5e418c 100644
--- a/js/src/builtin/WeakSetObject.cpp
+++ b/js/src/builtin/WeakSetObject.cpp
@@ -41,14 +41,15 @@ const JSFunctionSpec WeakSetObject::methods[] = {
};
JSObject*
-WeakSetObject::initClass(JSContext* cx, JSObject* obj)
+WeakSetObject::initClass(JSContext* cx, HandleObject obj)
{
- Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
RootedPlainObject proto(cx, NewBuiltinClassInstance<PlainObject>(cx));
if (!proto)
return nullptr;
- Rooted<JSFunction*> ctor(cx, global->createConstructor(cx, construct, ClassName(JSProto_WeakSet, cx), 0));
+ Rooted<JSFunction*> ctor(cx, GlobalObject::createConstructor(cx, construct,
+ ClassName(JSProto_WeakSet, cx), 0));
if (!ctor ||
!LinkConstructorAndPrototype(cx, ctor, proto) ||
!DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
diff --git a/js/src/builtin/WeakSetObject.h b/js/src/builtin/WeakSetObject.h
index 0a6ff33f3..50e54c182 100644
--- a/js/src/builtin/WeakSetObject.h
+++ b/js/src/builtin/WeakSetObject.h
@@ -16,7 +16,7 @@ class WeakSetObject : public NativeObject
public:
static const unsigned RESERVED_SLOTS = 1;
- static JSObject* initClass(JSContext* cx, JSObject* obj);
+ static JSObject* initClass(JSContext* cx, HandleObject obj);
static const Class class_;
private:
diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp
index b5be5f5ac..a1abbfeda 100644
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -77,7 +77,13 @@ class MOZ_STACK_CLASS BytecodeCompiler
bool canLazilyParse();
bool createParser();
bool createSourceAndParser(Maybe<uint32_t> parameterListEnd = Nothing());
- bool createScript(uint32_t preludeStart = 0);
+
+ // If toString{Start,End} are not explicitly passed, assume the script's
+ // offsets in the source used to parse it are the same as what should be
+ // used to compute its Function.prototype.toString() value.
+ bool createScript();
+ bool createScript(uint32_t toStringStart, uint32_t toStringEnd);
+
bool emplaceEmitter(Maybe<BytecodeEmitter>& emitter, SharedContext* sharedContext);
bool handleParseFailure(const Directives& newDirectives);
bool deoptimizeArgumentsInEnclosingScripts(JSContext* cx, HandleObject environment);
@@ -242,11 +248,17 @@ BytecodeCompiler::createSourceAndParser(Maybe<uint32_t> parameterListEnd /* = No
}
bool
-BytecodeCompiler::createScript(uint32_t preludeStart /* = 0 */)
+BytecodeCompiler::createScript()
+{
+ return createScript(0, sourceBuffer.length());
+}
+
+bool
+BytecodeCompiler::createScript(uint32_t toStringStart, uint32_t toStringEnd)
{
script = JSScript::Create(cx, options,
sourceObject, /* sourceStart = */ 0, sourceBuffer.length(),
- preludeStart);
+ toStringStart, toStringEnd);
return script != nullptr;
}
@@ -287,7 +299,8 @@ BytecodeCompiler::deoptimizeArgumentsInEnclosingScripts(JSContext* cx, HandleObj
RootedObject env(cx, environment);
while (env->is<EnvironmentObject>() || env->is<DebugEnvironmentProxy>()) {
if (env->is<CallObject>()) {
- RootedScript script(cx, env->as<CallObject>().callee().getOrCreateScript(cx));
+ RootedFunction fun(cx, &env->as<CallObject>().callee());
+ RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
if (!script)
return false;
if (script->argumentsHasVarBinding()) {
@@ -457,7 +470,7 @@ BytecodeCompiler::compileStandaloneFunction(MutableHandleFunction fun,
if (fn->pn_funbox->function()->isInterpreted()) {
MOZ_ASSERT(fun == fn->pn_funbox->function());
- if (!createScript(fn->pn_funbox->preludeStart))
+ if (!createScript(fn->pn_funbox->toStringStart, fn->pn_funbox->toStringEnd))
return false;
Maybe<BytecodeEmitter> emitter;
@@ -652,7 +665,7 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
Rooted<JSScript*> script(cx, JSScript::Create(cx, options, sourceObject,
lazy->begin(), lazy->end(),
- lazy->preludeStart()));
+ lazy->toStringStart(), lazy->toStringEnd()));
if (!script)
return false;
diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
index c524184d6..309d6c290 100644
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -3069,6 +3069,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
case PNK_TRUE:
case PNK_FALSE:
case PNK_NULL:
+ case PNK_RAW_UNDEFINED:
case PNK_ELISION:
case PNK_GENERATOR:
case PNK_NUMBER:
@@ -3538,9 +3539,10 @@ BytecodeEmitter::needsImplicitThis()
bool
BytecodeEmitter::maybeSetDisplayURL()
{
- if (tokenStream()->hasDisplayURL()) {
- if (!parser->ss->setDisplayURL(cx, tokenStream()->displayURL()))
+ if (tokenStream().hasDisplayURL()) {
+ if (!parser->ss->setDisplayURL(cx, tokenStream().displayURL())) {
return false;
+ }
}
return true;
}
@@ -3548,10 +3550,11 @@ BytecodeEmitter::maybeSetDisplayURL()
bool
BytecodeEmitter::maybeSetSourceMap()
{
- if (tokenStream()->hasSourceMapURL()) {
+ if (tokenStream().hasSourceMapURL()) {
MOZ_ASSERT(!parser->ss->hasSourceMapURL());
- if (!parser->ss->setSourceMapURL(cx, tokenStream()->sourceMapURL()))
+ if (!parser->ss->setSourceMapURL(cx, tokenStream().sourceMapURL())) {
return false;
+ }
}
/*
@@ -3590,21 +3593,21 @@ BytecodeEmitter::tellDebuggerAboutCompiledScript(ExclusiveContext* cx)
}
}
-inline TokenStream*
+inline TokenStream&
BytecodeEmitter::tokenStream()
{
- return &parser->tokenStream;
+ return parser->tokenStream;
}
bool
BytecodeEmitter::reportError(ParseNode* pn, unsigned errorNumber, ...)
{
- TokenPos pos = pn ? pn->pn_pos : tokenStream()->currentToken().pos;
+ TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos;
va_list args;
va_start(args, errorNumber);
- bool result = tokenStream()->reportCompileErrorNumberVA(pos.begin, JSREPORT_ERROR,
- errorNumber, args);
+ bool result = tokenStream().reportCompileErrorNumberVA(nullptr, pos.begin, JSREPORT_ERROR,
+ errorNumber, args);
va_end(args);
return result;
}
@@ -3612,11 +3615,12 @@ BytecodeEmitter::reportError(ParseNode* pn, unsigned errorNumber, ...)
bool
BytecodeEmitter::reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...)
{
- TokenPos pos = pn ? pn->pn_pos : tokenStream()->currentToken().pos;
+ TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos;
va_list args;
va_start(args, errorNumber);
- bool result = tokenStream()->reportExtraWarningErrorNumberVA(pos.begin, errorNumber, args);
+ bool result = tokenStream().reportExtraWarningErrorNumberVA(nullptr, pos.begin,
+ errorNumber, args);
va_end(args);
return result;
}
@@ -3624,12 +3628,12 @@ BytecodeEmitter::reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...)
bool
BytecodeEmitter::reportStrictModeError(ParseNode* pn, unsigned errorNumber, ...)
{
- TokenPos pos = pn ? pn->pn_pos : tokenStream()->currentToken().pos;
+ TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos;
va_list args;
va_start(args, errorNumber);
- bool result = tokenStream()->reportStrictModeErrorNumberVA(pos.begin, sc->strict(),
- errorNumber, args);
+ bool result = tokenStream().reportStrictModeErrorNumberVA(nullptr, pos.begin, sc->strict(),
+ errorNumber, args);
va_end(args);
return result;
}
@@ -4599,7 +4603,7 @@ BytecodeEmitter::emitSwitch(ParseNode* pn)
// If the expression is a literal, suppress line number emission so
// that debugging works more naturally.
if (caseValue) {
- if (!emitTree(caseValue,
+ if (!emitTree(caseValue, ValueUsage::WantValue,
caseValue->isLiteral() ? SUPPRESS_LINENOTE : EMIT_LINENOTE))
{
return false;
@@ -5810,36 +5814,78 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla
if (!emitRequireObjectCoercible()) // ... RHS
return false;
+ bool needsRestPropertyExcludedSet = pattern->pn_count > 1 &&
+ pattern->last()->isKind(PNK_SPREAD);
+ if (needsRestPropertyExcludedSet) {
+ if (!emitDestructuringObjRestExclusionSet(pattern)) // ... RHS SET
+ return false;
+
+ if (!emit1(JSOP_SWAP)) // ... SET RHS
+ return false;
+ }
+
for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
ParseNode* subpattern;
- if (member->isKind(PNK_MUTATEPROTO))
+ if (member->isKind(PNK_MUTATEPROTO) || member->isKind(PNK_SPREAD))
subpattern = member->pn_kid;
else
subpattern = member->pn_right;
+
ParseNode* lhs = subpattern;
+ MOZ_ASSERT_IF(member->isKind(PNK_SPREAD), !lhs->isKind(PNK_ASSIGN));
if (lhs->isKind(PNK_ASSIGN))
lhs = lhs->pn_left;
size_t emitted;
- if (!emitDestructuringLHSRef(lhs, &emitted)) // ... RHS *LREF
+ if (!emitDestructuringLHSRef(lhs, &emitted)) // ... *SET RHS *LREF
return false;
// Duplicate the value being destructured to use as a reference base.
if (emitted) {
- if (!emitDupAt(emitted)) // ... RHS *LREF RHS
+ if (!emitDupAt(emitted)) // ... *SET RHS *LREF RHS
return false;
} else {
- if (!emit1(JSOP_DUP)) // ... RHS RHS
+ if (!emit1(JSOP_DUP)) // ... *SET RHS RHS
return false;
}
+ if (member->isKind(PNK_SPREAD)) {
+ if (!updateSourceCoordNotes(member->pn_pos.begin))
+ return false;
+
+ if (!emitNewInit(JSProto_Object)) // ... *SET RHS *LREF RHS TARGET
+ return false;
+ if (!emit1(JSOP_DUP)) // ... *SET RHS *LREF RHS TARGET TARGET
+ return false;
+ if (!emit2(JSOP_PICK, 2)) // ... *SET RHS *LREF TARGET TARGET RHS
+ return false;
+
+ if (needsRestPropertyExcludedSet) {
+ if (!emit2(JSOP_PICK, emitted + 4)) // ... RHS *LREF TARGET TARGET RHS SET
+ return false;
+ }
+
+ CopyOption option = needsRestPropertyExcludedSet
+ ? CopyOption::Filtered
+ : CopyOption::Unfiltered;
+ if (!emitCopyDataProperties(option)) // ... RHS *LREF TARGET
+ return false;
+
+ // Destructure TARGET per this member's lhs.
+ if (!emitSetOrInitializeDestructuring(lhs, flav)) // ... RHS
+ return false;
+
+ MOZ_ASSERT(member == pattern->last(), "Rest property is always last");
+ break;
+ }
+
// Now push the property name currently being matched, which is the
// current property name "label" on the left of a colon in the object
// initialiser.
bool needsGetElem = true;
if (member->isKind(PNK_MUTATEPROTO)) {
- if (!emitAtomOp(cx->names().proto, JSOP_GETPROP)) // ... RHS *LREF PROP
+ if (!emitAtomOp(cx->names().proto, JSOP_GETPROP)) // ... *SET RHS *LREF PROP
return false;
needsGetElem = false;
} else {
@@ -5847,40 +5893,131 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla
ParseNode* key = member->pn_left;
if (key->isKind(PNK_NUMBER)) {
- if (!emitNumberOp(key->pn_dval)) // ... RHS *LREF RHS KEY
+ if (!emitNumberOp(key->pn_dval)) // ... *SET RHS *LREF RHS KEY
return false;
} else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) {
- PropertyName* name = key->pn_atom->asPropertyName();
-
- // The parser already checked for atoms representing indexes and
- // used PNK_NUMBER instead, but also watch for ids which TI treats
- // as indexes for simplification of downstream analysis.
- jsid id = NameToId(name);
- if (id != IdToTypeId(id)) {
- if (!emitTree(key)) // ... RHS *LREF RHS KEY
+ if (!emitAtomOp(key->pn_atom, JSOP_GETPROP)) // ... *SET RHS *LREF PROP
+ return false;
+ needsGetElem = false;
+ } else {
+ if (!emitComputedPropertyName(key)) // ... *SET RHS *LREF RHS KEY
+ return false;
+
+ // Add the computed property key to the exclusion set.
+ if (needsRestPropertyExcludedSet) {
+ if (!emitDupAt(emitted + 3)) // ... SET RHS *LREF RHS KEY SET
return false;
- } else {
- if (!emitAtomOp(name, JSOP_GETPROP)) // ... RHS *LREF PROP
+ if (!emitDupAt(1)) // ... SET RHS *LREF RHS KEY SET KEY
+ return false;
+ if (!emit1(JSOP_UNDEFINED)) // ... SET RHS *LREF RHS KEY SET KEY UNDEFINED
+ return false;
+ if (!emit1(JSOP_INITELEM)) // ... SET RHS *LREF RHS KEY SET
+ return false;
+ if (!emit1(JSOP_POP)) // ... SET RHS *LREF RHS KEY
return false;
- needsGetElem = false;
}
- } else {
- if (!emitComputedPropertyName(key)) // ... RHS *LREF RHS KEY
- return false;
}
}
// Get the property value if not done already.
- if (needsGetElem && !emitElemOpBase(JSOP_GETELEM)) // ... RHS *LREF PROP
+ if (needsGetElem && !emitElemOpBase(JSOP_GETELEM)) // ... *SET RHS *LREF PROP
return false;
if (subpattern->isKind(PNK_ASSIGN)) {
- if (!emitDefault(subpattern->pn_right, lhs)) // ... RHS *LREF VALUE
+ if (!emitDefault(subpattern->pn_right, lhs)) // ... *SET RHS *LREF VALUE
return false;
}
// Destructure PROP per this member's lhs.
- if (!emitSetOrInitializeDestructuring(subpattern, flav)) // ... RHS
+ if (!emitSetOrInitializeDestructuring(subpattern, flav)) // ... *SET RHS
+ return false;
+ }
+
+ return true;
+}
+
+bool
+BytecodeEmitter::emitDestructuringObjRestExclusionSet(ParseNode* pattern)
+{
+ MOZ_ASSERT(pattern->isKind(PNK_OBJECT));
+ MOZ_ASSERT(pattern->isArity(PN_LIST));
+ MOZ_ASSERT(pattern->last()->isKind(PNK_SPREAD));
+
+ ptrdiff_t offset = this->offset();
+ if (!emitNewInit(JSProto_Object))
+ return false;
+
+ // Try to construct the shape of the object as we go, so we can emit a
+ // JSOP_NEWOBJECT with the final shape instead.
+ // In the case of computed property names and indices, we cannot fix the
+ // shape at bytecode compile time. When the shape cannot be determined,
+ // |obj| is nulled out.
+
+ // No need to do any guessing for the object kind, since we know the upper
+ // bound of how many properties we plan to have.
+ gc::AllocKind kind = gc::GetGCObjectKind(pattern->pn_count - 1);
+ RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject));
+ if (!obj)
+ return false;
+
+ RootedAtom pnatom(cx);
+ for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
+ if (member->isKind(PNK_SPREAD))
+ break;
+
+ bool isIndex = false;
+ if (member->isKind(PNK_MUTATEPROTO)) {
+ pnatom.set(cx->names().proto);
+ } else {
+ ParseNode* key = member->pn_left;
+ if (key->isKind(PNK_NUMBER)) {
+ if (!emitNumberOp(key->pn_dval))
+ return false;
+ isIndex = true;
+ } else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) {
+ pnatom.set(key->pn_atom);
+ } else {
+ // Otherwise this is a computed property name which needs to
+ // be added dynamically.
+ obj.set(nullptr);
+ continue;
+ }
+ }
+
+ // Initialize elements with |undefined|.
+ if (!emit1(JSOP_UNDEFINED))
+ return false;
+
+ if (isIndex) {
+ obj.set(nullptr);
+ if (!emit1(JSOP_INITELEM))
+ return false;
+ } else {
+ uint32_t index;
+ if (!makeAtomIndex(pnatom, &index))
+ return false;
+
+ if (obj) {
+ MOZ_ASSERT(!obj->inDictionaryMode());
+ Rooted<jsid> id(cx, AtomToId(pnatom));
+ if (!NativeDefineProperty(cx, obj, id, UndefinedHandleValue, nullptr, nullptr,
+ JSPROP_ENUMERATE))
+ {
+ return false;
+ }
+ if (obj->inDictionaryMode())
+ obj.set(nullptr);
+ }
+
+ if (!emitIndex32(JSOP_INITPROP, index))
+ return false;
+ }
+ }
+
+ if (obj) {
+ // The object survived and has a predictable shape: update the
+ // original bytecode.
+ if (!replaceNewInitWithNewObject(obj, offset))
return false;
}
@@ -6242,6 +6379,9 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje
case PNK_NULL:
vp.setNull();
return true;
+ case PNK_RAW_UNDEFINED:
+ vp.setUndefined();
+ return true;
case PNK_CALLSITEOBJ:
case PNK_ARRAY: {
unsigned count;
@@ -6281,8 +6421,8 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje
}
MOZ_ASSERT(idx == count);
- JSObject* obj = ObjectGroup::newArrayObject(cx, values.begin(), values.length(),
- newKind, arrayKind);
+ ArrayObject* obj = ObjectGroup::newArrayObject(cx, values.begin(), values.length(),
+ newKind, arrayKind);
if (!obj)
return false;
@@ -6645,7 +6785,7 @@ BytecodeEmitter::emitLexicalScopeBody(ParseNode* body, EmitLineNumberNote emitLi
}
// Line notes were updated by emitLexicalScope.
- return emitTree(body, emitLineNote);
+ return emitTree(body, ValueUsage::WantValue, emitLineNote);
}
// Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
@@ -6747,9 +6887,9 @@ BytecodeEmitter::emitRequireObjectCoercible()
return false;
if (!emit2(JSOP_PICK, 2)) // VAL REQUIREOBJECTCOERCIBLE UNDEFINED VAL
return false;
- if (!emitCall(JSOP_CALL, 1)) // VAL IGNORED
+ if (!emitCall(JSOP_CALL_IGNORES_RV, 1))// VAL IGNORED
return false;
- checkTypeSet(JSOP_CALL);
+ checkTypeSet(JSOP_CALL_IGNORES_RV);
if (!emit1(JSOP_POP)) // VAL
return false;
@@ -6759,6 +6899,53 @@ BytecodeEmitter::emitRequireObjectCoercible()
}
bool
+BytecodeEmitter::emitCopyDataProperties(CopyOption option)
+{
+ DebugOnly<int32_t> depth = this->stackDepth;
+
+ uint32_t argc;
+ if (option == CopyOption::Filtered) {
+ MOZ_ASSERT(depth > 2); // TARGET SOURCE SET
+ argc = 3;
+
+ if (!emitAtomOp(cx->names().CopyDataProperties,
+ JSOP_GETINTRINSIC)) // TARGET SOURCE SET COPYDATAPROPERTIES
+ {
+ return false;
+ }
+ } else {
+ MOZ_ASSERT(depth > 1); // TARGET SOURCE
+ argc = 2;
+
+ if (!emitAtomOp(cx->names().CopyDataPropertiesUnfiltered,
+ JSOP_GETINTRINSIC)) // TARGET SOURCE COPYDATAPROPERTIES
+ {
+ return false;
+ }
+ }
+
+ if (!emit1(JSOP_UNDEFINED)) // TARGET SOURCE *SET COPYDATAPROPERTIES UNDEFINED
+ return false;
+ if (!emit2(JSOP_PICK, argc + 1)) // SOURCE *SET COPYDATAPROPERTIES UNDEFINED TARGET
+ return false;
+ if (!emit2(JSOP_PICK, argc + 1)) // *SET COPYDATAPROPERTIES UNDEFINED TARGET SOURCE
+ return false;
+ if (option == CopyOption::Filtered) {
+ if (!emit2(JSOP_PICK, argc + 1)) // COPYDATAPROPERTIES UNDEFINED TARGET SOURCE SET
+ return false;
+ }
+ if (!emitCall(JSOP_CALL_IGNORES_RV, argc)) // IGNORED
+ return false;
+ checkTypeSet(JSOP_CALL_IGNORES_RV);
+
+ if (!emit1(JSOP_POP)) // -
+ return false;
+
+ MOZ_ASSERT(depth - int(argc) == this->stackDepth);
+ return true;
+}
+
+bool
BytecodeEmitter::emitIterator()
{
// Convert iterable to iterator.
@@ -7269,12 +7456,14 @@ BytecodeEmitter::emitCStyleFor(ParseNode* pn, EmitterScope* headLexicalEmitterSc
// scope, but we still need to emit code for the initializers.)
if (!updateSourceCoordNotes(init->pn_pos.begin))
return false;
- if (!emitTree(init))
- return false;
-
- if (!init->isForLoopDeclaration()) {
+ if (init->isForLoopDeclaration()) {
+ if (!emitTree(init))
+ return false;
+ } else {
// 'init' is an expression, not a declaration. emitTree left its
// value on the stack.
+ if (!emitTree(init, ValueUsage::IgnoreValue))
+ return false;
if (!emit1(JSOP_POP))
return false;
}
@@ -7356,7 +7545,7 @@ BytecodeEmitter::emitCStyleFor(ParseNode* pn, EmitterScope* headLexicalEmitterSc
if (!updateSourceCoordNotes(update->pn_pos.begin))
return false;
- if (!emitTree(update))
+ if (!emitTree(update, ValueUsage::IgnoreValue))
return false;
if (!emit1(JSOP_POP))
return false;
@@ -7839,7 +8028,8 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
Rooted<JSObject*> sourceObject(cx, script->sourceObject());
Rooted<JSScript*> script(cx, JSScript::Create(cx, options, sourceObject,
funbox->bufStart, funbox->bufEnd,
- funbox->preludeStart));
+ funbox->toStringStart,
+ funbox->toStringEnd));
if (!script)
return false;
@@ -8682,8 +8872,9 @@ BytecodeEmitter::emitStatement(ParseNode* pn)
if (useful) {
JSOp op = wantval ? JSOP_SETRVAL : JSOP_POP;
+ ValueUsage valueUsage = wantval ? ValueUsage::WantValue : ValueUsage::IgnoreValue;
MOZ_ASSERT_IF(pn2->isKind(PNK_ASSIGN), pn2->isOp(JSOP_NOP));
- if (!emitTree(pn2))
+ if (!emitTree(pn2, valueUsage))
return false;
if (!emit1(op))
return false;
@@ -9033,7 +9224,7 @@ BytecodeEmitter::emitOptimizeSpread(ParseNode* arg0, JumpList* jmp, bool* emitte
}
bool
-BytecodeEmitter::emitCallOrNew(ParseNode* pn)
+BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */)
{
bool callop = pn->isKind(PNK_CALL) || pn->isKind(PNK_TAGGED_TEMPLATE);
/*
@@ -9192,7 +9383,7 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn)
return false;
}
- if (!emitArray(args, argc, JSOP_SPREADCALLARRAY))
+ if (!emitArray(args, argc))
return false;
if (optCodeEmitted) {
@@ -9212,13 +9403,20 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn)
}
if (!spread) {
- if (!emitCall(pn->getOp(), argc, pn))
- return false;
+ if (pn->getOp() == JSOP_CALL && valueUsage == ValueUsage::IgnoreValue) {
+ if (!emitCall(JSOP_CALL_IGNORES_RV, argc, pn))
+ return false;
+ checkTypeSet(JSOP_CALL_IGNORES_RV);
+ } else {
+ if (!emitCall(pn->getOp(), argc, pn))
+ return false;
+ checkTypeSet(pn->getOp());
+ }
} else {
if (!emit1(pn->getOp()))
return false;
+ checkTypeSet(pn->getOp());
}
- checkTypeSet(pn->getOp());
if (pn->isOp(JSOP_EVAL) ||
pn->isOp(JSOP_STRICTEVAL) ||
pn->isOp(JSOP_SPREADEVAL) ||
@@ -9316,12 +9514,13 @@ BytecodeEmitter::emitLogical(ParseNode* pn)
}
bool
-BytecodeEmitter::emitSequenceExpr(ParseNode* pn)
+BytecodeEmitter::emitSequenceExpr(ParseNode* pn,
+ ValueUsage valueUsage /* = ValueUsage::WantValue */)
{
for (ParseNode* child = pn->pn_head; ; child = child->pn_next) {
if (!updateSourceCoordNotes(child->pn_pos.begin))
return false;
- if (!emitTree(child))
+ if (!emitTree(child, child->pn_next ? ValueUsage::IgnoreValue : valueUsage))
return false;
if (!child->pn_next)
break;
@@ -9384,7 +9583,8 @@ BytecodeEmitter::emitLabeledStatement(const LabeledStatement* pn)
}
bool
-BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional)
+BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional,
+ ValueUsage valueUsage /* = ValueUsage::WantValue */)
{
/* Emit the condition, then branch if false to the else part. */
if (!emitTree(&conditional.condition()))
@@ -9394,13 +9594,13 @@ BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional)
if (!ifThenElse.emitCond())
return false;
- if (!emitTreeInBranch(&conditional.thenExpression()))
+ if (!emitTreeInBranch(&conditional.thenExpression(), valueUsage))
return false;
if (!ifThenElse.emitElse())
return false;
- if (!emitTreeInBranch(&conditional.elseExpression()))
+ if (!emitTreeInBranch(&conditional.elseExpression(), valueUsage))
return false;
if (!ifThenElse.emitEnd())
@@ -9429,6 +9629,22 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp,
continue;
}
+ if (propdef->isKind(PNK_SPREAD)) {
+ MOZ_ASSERT(type == ObjectLiteral);
+
+ if (!emit1(JSOP_DUP))
+ return false;
+
+ if (!emitTree(propdef->pn_kid))
+ return false;
+
+ if (!emitCopyDataProperties(CopyOption::Unfiltered))
+ return false;
+
+ objp.set(nullptr);
+ continue;
+ }
+
bool extraPop = false;
if (type == ClassBody && propdef->as<ClassMethod>().isStatic()) {
extraPop = true;
@@ -9452,16 +9668,6 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp,
{
continue;
}
-
- // The parser already checked for atoms representing indexes and
- // used PNK_NUMBER instead, but also watch for ids which TI treats
- // as indexes for simpliciation of downstream analysis.
- jsid id = NameToId(key->pn_atom->asPropertyName());
- if (id != IdToTypeId(id)) {
- if (!emitTree(key))
- return false;
- isIndex = true;
- }
} else {
if (!emitComputedPropertyName(key))
return false;
@@ -9542,8 +9748,7 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp,
MOZ_ASSERT(!IsHiddenInitOp(op));
MOZ_ASSERT(!objp->inDictionaryMode());
Rooted<jsid> id(cx, AtomToId(key->pn_atom));
- RootedValue undefinedValue(cx, UndefinedValue());
- if (!NativeDefineProperty(cx, objp, id, undefinedValue, nullptr, nullptr,
+ if (!NativeDefineProperty(cx, objp, id, UndefinedHandleValue, nullptr, nullptr,
JSPROP_ENUMERATE))
{
return false;
@@ -9586,15 +9791,16 @@ BytecodeEmitter::emitObject(ParseNode* pn)
if (!emitNewInit(JSProto_Object))
return false;
- /*
- * Try to construct the shape of the object as we go, so we can emit a
- * JSOP_NEWOBJECT with the final shape instead.
- */
- RootedPlainObject obj(cx);
- // No need to do any guessing for the object kind, since we know exactly
- // how many properties we plan to have.
+ // Try to construct the shape of the object as we go, so we can emit a
+ // JSOP_NEWOBJECT with the final shape instead.
+ // In the case of computed property names and indices, we cannot fix the
+ // shape at bytecode compile time. When the shape cannot be determined,
+ // |obj| is nulled out.
+
+ // No need to do any guessing for the object kind, since we know the upper
+ // bound of how many properties we plan to have.
gc::AllocKind kind = gc::GetGCObjectKind(pn->pn_count);
- obj = NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject);
+ RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject));
if (!obj)
return false;
@@ -9602,25 +9808,34 @@ BytecodeEmitter::emitObject(ParseNode* pn)
return false;
if (obj) {
- /*
- * The object survived and has a predictable shape: update the original
- * bytecode.
- */
- ObjectBox* objbox = parser->newObjectBox(obj);
- if (!objbox)
+ // The object survived and has a predictable shape: update the original
+ // bytecode.
+ if (!replaceNewInitWithNewObject(obj, offset))
return false;
+ }
- static_assert(JSOP_NEWINIT_LENGTH == JSOP_NEWOBJECT_LENGTH,
- "newinit and newobject must have equal length to edit in-place");
+ return true;
+}
- uint32_t index = objectList.add(objbox);
- jsbytecode* code = this->code(offset);
- code[0] = JSOP_NEWOBJECT;
- code[1] = jsbytecode(index >> 24);
- code[2] = jsbytecode(index >> 16);
- code[3] = jsbytecode(index >> 8);
- code[4] = jsbytecode(index);
- }
+bool
+BytecodeEmitter::replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset)
+{
+ ObjectBox* objbox = parser->newObjectBox(obj);
+ if (!objbox)
+ return false;
+
+ static_assert(JSOP_NEWINIT_LENGTH == JSOP_NEWOBJECT_LENGTH,
+ "newinit and newobject must have equal length to edit in-place");
+
+ uint32_t index = objectList.add(objbox);
+ jsbytecode* code = this->code(offset);
+
+ MOZ_ASSERT(code[0] == JSOP_NEWINIT);
+ code[0] = JSOP_NEWOBJECT;
+ code[1] = jsbytecode(index >> 24);
+ code[2] = jsbytecode(index >> 16);
+ code[3] = jsbytecode(index >> 8);
+ code[4] = jsbytecode(index);
return true;
}
@@ -9683,11 +9898,11 @@ BytecodeEmitter::emitArrayLiteral(ParseNode* pn)
}
}
- return emitArray(pn->pn_head, pn->pn_count, JSOP_NEWARRAY);
+ return emitArray(pn->pn_head, pn->pn_count);
}
bool
-BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count, JSOp op)
+BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count)
{
/*
@@ -9698,7 +9913,6 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count, JSOp op)
* to avoid dup'ing and popping the array as each element is added, as
* JSOP_SETELEM/JSOP_SETPROP would do.
*/
- MOZ_ASSERT(op == JSOP_NEWARRAY || op == JSOP_SPREADCALLARRAY);
uint32_t nspread = 0;
for (ParseNode* elt = pn; elt; elt = elt->pn_next) {
@@ -9719,7 +9933,7 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count, JSOp op)
// For arrays with spread, this is a very pessimistic allocation, the
// minimum possible final size.
- if (!emitUint32Operand(op, count - nspread)) // ARRAY
+ if (!emitUint32Operand(JSOP_NEWARRAY, count - nspread)) // ARRAY
return false;
ParseNode* pn2 = pn;
@@ -10220,6 +10434,13 @@ BytecodeEmitter::emitClass(ParseNode* pn)
return false;
}
} else {
+ // In the case of default class constructors, emit the start and end
+ // offsets in the source buffer as source notes so that when we
+ // actually make the constructor during execution, we can give it the
+ // correct toString output.
+ if (!newSrcNote3(SRC_CLASS_SPAN, ptrdiff_t(pn->pn_pos.begin), ptrdiff_t(pn->pn_pos.end)))
+ return false;
+
JSAtom *name = names ? names->innerBinding()->pn_atom : cx->names().empty;
if (heritageExpression) {
if (!emitAtomOp(name, JSOP_DERIVEDCONSTRUCTOR))
@@ -10274,7 +10495,8 @@ BytecodeEmitter::emitClass(ParseNode* pn)
}
bool
-BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
+BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */,
+ EmitLineNumberNote emitLineNote /* = EMIT_LINENOTE */)
{
JS_CHECK_RECURSION(cx, return false);
@@ -10396,7 +10618,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
break;
case PNK_COMMA:
- if (!emitSequenceExpr(pn))
+ if (!emitSequenceExpr(pn, valueUsage))
return false;
break;
@@ -10418,7 +10640,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
break;
case PNK_CONDITIONAL:
- if (!emitConditionalExpression(pn->as<ConditionalExpression>()))
+ if (!emitConditionalExpression(pn->as<ConditionalExpression>(), valueUsage))
return false;
break;
@@ -10531,7 +10753,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
case PNK_CALL:
case PNK_GENEXP:
case PNK_SUPERCALL:
- if (!emitCallOrNew(pn))
+ if (!emitCallOrNew(pn, valueUsage))
return false;
break;
@@ -10637,6 +10859,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
case PNK_TRUE:
case PNK_FALSE:
case PNK_NULL:
+ case PNK_RAW_UNDEFINED:
if (!emit1(pn->getOp()))
return false;
break;
@@ -10688,12 +10911,13 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
}
bool
-BytecodeEmitter::emitTreeInBranch(ParseNode* pn)
+BytecodeEmitter::emitTreeInBranch(ParseNode* pn,
+ ValueUsage valueUsage /* = ValueUsage::WantValue */)
{
// Code that may be conditionally executed always need their own TDZ
// cache.
TDZCheckCache tdzCache(this);
- return emitTree(pn);
+ return emitTree(pn, valueUsage);
}
static bool
diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h
index 814fa11d4..595ee6405 100644
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -171,6 +171,11 @@ struct JumpList {
void patchAll(jsbytecode* code, JumpTarget target);
};
+enum class ValueUsage {
+ WantValue,
+ IgnoreValue
+};
+
struct MOZ_STACK_CLASS BytecodeEmitter
{
class TDZCheckCache;
@@ -356,7 +361,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool maybeSetSourceMap();
void tellDebuggerAboutCompiledScript(ExclusiveContext* cx);
- inline TokenStream* tokenStream();
+ inline TokenStream& tokenStream();
BytecodeVector& code() const { return current->code; }
jsbytecode* code(ptrdiff_t offset) const { return current->code.begin() + offset; }
@@ -434,10 +439,12 @@ struct MOZ_STACK_CLASS BytecodeEmitter
};
// Emit code for the tree rooted at pn.
- MOZ_MUST_USE bool emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote = EMIT_LINENOTE);
+ MOZ_MUST_USE bool emitTree(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue,
+ EmitLineNumberNote emitLineNote = EMIT_LINENOTE);
// Emit code for the tree rooted at pn with its own TDZ cache.
- MOZ_MUST_USE bool emitTreeInBranch(ParseNode* pn);
+ MOZ_MUST_USE bool emitTreeInBranch(ParseNode* pn,
+ ValueUsage valueUsage = ValueUsage::WantValue);
// Emit global, eval, or module code for tree rooted at body. Always
// encompasses the entire source.
@@ -521,7 +528,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitAtomOp(ParseNode* pn, JSOp op);
MOZ_MUST_USE bool emitArrayLiteral(ParseNode* pn);
- MOZ_MUST_USE bool emitArray(ParseNode* pn, uint32_t count, JSOp op);
+ MOZ_MUST_USE bool emitArray(ParseNode* pn, uint32_t count);
MOZ_MUST_USE bool emitArrayComp(ParseNode* pn);
MOZ_MUST_USE bool emitInternedScopeOp(uint32_t index, JSOp op);
@@ -533,6 +540,8 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitFunction(ParseNode* pn, bool needsProto = false);
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitObject(ParseNode* pn);
+ MOZ_MUST_USE bool replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset);
+
MOZ_MUST_USE bool emitHoistedFunctionsInList(ParseNode* pn);
MOZ_MUST_USE bool emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp,
@@ -660,6 +669,10 @@ struct MOZ_STACK_CLASS BytecodeEmitter
// []/{} expression).
MOZ_MUST_USE bool emitSetOrInitializeDestructuring(ParseNode* target, DestructuringFlavor flav);
+ // emitDestructuringObjRestExclusionSet emits the property exclusion set
+ // for the rest-property in an object pattern.
+ MOZ_MUST_USE bool emitDestructuringObjRestExclusionSet(ParseNode* pattern);
+
// emitDestructuringOps assumes the to-be-destructured value has been
// pushed on the stack and emits code to destructure each part of a [] or
// {} lhs expression.
@@ -677,6 +690,15 @@ struct MOZ_STACK_CLASS BytecodeEmitter
// object, with no overall effect on the stack.
MOZ_MUST_USE bool emitRequireObjectCoercible();
+ enum class CopyOption {
+ Filtered, Unfiltered
+ };
+
+ // Calls either the |CopyDataProperties| or the
+ // |CopyDataPropertiesUnfiltered| intrinsic function, consumes three (or
+ // two in the latter case) elements from the stack.
+ MOZ_MUST_USE bool emitCopyDataProperties(CopyOption option);
+
// emitIterator expects the iterable to already be on the stack.
// It will replace that stack value with the corresponding iterator
MOZ_MUST_USE bool emitIterator();
@@ -724,16 +746,18 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitRightAssociative(ParseNode* pn);
MOZ_MUST_USE bool emitLeftAssociative(ParseNode* pn);
MOZ_MUST_USE bool emitLogical(ParseNode* pn);
- MOZ_MUST_USE bool emitSequenceExpr(ParseNode* pn);
+ MOZ_MUST_USE bool emitSequenceExpr(ParseNode* pn,
+ ValueUsage valueUsage = ValueUsage::WantValue);
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitIncOrDec(ParseNode* pn);
- MOZ_MUST_USE bool emitConditionalExpression(ConditionalExpression& conditional);
+ MOZ_MUST_USE bool emitConditionalExpression(ConditionalExpression& conditional,
+ ValueUsage valueUsage = ValueUsage::WantValue);
MOZ_MUST_USE bool isRestParameter(ParseNode* pn, bool* result);
MOZ_MUST_USE bool emitOptimizeSpread(ParseNode* arg0, JumpList* jmp, bool* emitted);
- MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn);
+ MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue);
MOZ_MUST_USE bool emitSelfHostedCallFunction(ParseNode* pn);
MOZ_MUST_USE bool emitSelfHostedResumeGenerator(ParseNode* pn);
MOZ_MUST_USE bool emitSelfHostedForceInterpreter(ParseNode* pn);
diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp
index 6f62ffac6..689fa02b4 100644
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -378,6 +378,7 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
case PNK_TRUE:
case PNK_FALSE:
case PNK_NULL:
+ case PNK_RAW_UNDEFINED:
case PNK_THIS:
case PNK_ELISION:
case PNK_NUMBER:
@@ -468,6 +469,7 @@ IsEffectless(ParseNode* node)
node->isKind(PNK_TEMPLATE_STRING) ||
node->isKind(PNK_NUMBER) ||
node->isKind(PNK_NULL) ||
+ node->isKind(PNK_RAW_UNDEFINED) ||
node->isKind(PNK_FUNCTION) ||
node->isKind(PNK_GENEXP);
}
@@ -492,6 +494,7 @@ Boolish(ParseNode* pn)
case PNK_FALSE:
case PNK_NULL:
+ case PNK_RAW_UNDEFINED:
return Falsy;
case PNK_VOID: {
@@ -1342,15 +1345,8 @@ FoldElement(ExclusiveContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>&
if (!name)
return true;
- // Also don't optimize if the name doesn't map directly to its id for TI's
- // purposes.
- if (NameToId(name) != IdToTypeId(NameToId(name)))
- return true;
-
// Optimization 3: We have expr["foo"] where foo is not an index. Convert
// to a property access (like expr.foo) that optimizes better downstream.
- // Don't bother with this for names that TI considers to be indexes, to
- // simplify downstream analysis.
ParseNode* dottedAccess = parser.handler.newPropertyAccess(expr, name, node->pn_pos.end);
if (!dottedAccess)
return false;
@@ -1643,6 +1639,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
case PNK_TRUE:
case PNK_FALSE:
case PNK_NULL:
+ case PNK_RAW_UNDEFINED:
case PNK_ELISION:
case PNK_NUMBER:
case PNK_DEBUGGER:
diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
index b619cf24c..2d7f57e1e 100644
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -10,6 +10,8 @@
#include "mozilla/Attributes.h"
#include "mozilla/PodOperations.h"
+#include <string.h>
+
#include "frontend/ParseNode.h"
#include "frontend/SharedContext.h"
@@ -181,6 +183,10 @@ class FullParseHandler
return new_<NullLiteral>(pos);
}
+ ParseNode* newRawUndefinedLiteral(const TokenPos& pos) {
+ return new_<RawUndefinedLiteral>(pos);
+ }
+
// The Boxer object here is any object that can allocate ObjectBoxes.
// Specifically, a Boxer has a .newObjectBox(T) method that accepts a
// Rooted<RegExpObject*> argument and returns an ObjectBox*.
@@ -296,10 +302,9 @@ class FullParseHandler
}
MOZ_MUST_USE bool addSpreadElement(ParseNode* literal, uint32_t begin, ParseNode* inner) {
- TokenPos pos(begin, inner->pn_pos.end);
- ParseNode* spread = new_<UnaryNode>(PNK_SPREAD, JSOP_NOP, pos, inner);
+ ParseNode* spread = newSpread(begin, inner);
if (!spread)
- return null();
+ return false;
literal->append(spread);
literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST;
return true;
@@ -327,8 +332,10 @@ class FullParseHandler
return literal;
}
- ParseNode* newClass(ParseNode* name, ParseNode* heritage, ParseNode* methodBlock) {
- return new_<ClassNode>(name, heritage, methodBlock);
+ ParseNode* newClass(ParseNode* name, ParseNode* heritage, ParseNode* methodBlock,
+ const TokenPos& pos)
+ {
+ return new_<ClassNode>(name, heritage, methodBlock, pos);
}
ParseNode* newClassMethodList(uint32_t begin) {
return new_<ListNode>(PNK_CLASSMETHODLIST, TokenPos(begin, begin + 1));
@@ -388,6 +395,18 @@ class FullParseHandler
return true;
}
+ MOZ_MUST_USE bool addSpreadProperty(ParseNode* literal, uint32_t begin, ParseNode* inner) {
+ MOZ_ASSERT(literal->isKind(PNK_OBJECT));
+ MOZ_ASSERT(literal->isArity(PN_LIST));
+
+ setListFlag(literal, PNX_NONCONST);
+ ParseNode* spread = newSpread(begin, inner);
+ if (!spread)
+ return false;
+ literal->append(spread);
+ return true;
+ }
+
MOZ_MUST_USE bool addObjectMethodDefinition(ParseNode* literal, ParseNode* key, ParseNode* fn,
JSOp op)
{
@@ -870,29 +889,29 @@ class FullParseHandler
return node->isKind(PNK_NAME);
}
- bool nameIsEvalAnyParentheses(ParseNode* node, ExclusiveContext* cx) {
- MOZ_ASSERT(isNameAnyParentheses(node),
- "must only call this function on known names");
+ bool isArgumentsAnyParentheses(ParseNode* node, ExclusiveContext* cx) {
+ return node->isKind(PNK_NAME) && node->pn_atom == cx->names().arguments;
+ }
- return node->pn_atom == cx->names().eval;
+ bool isEvalAnyParentheses(ParseNode* node, ExclusiveContext* cx) {
+ return node->isKind(PNK_NAME) && node->pn_atom == cx->names().eval;
}
const char* nameIsArgumentsEvalAnyParentheses(ParseNode* node, ExclusiveContext* cx) {
MOZ_ASSERT(isNameAnyParentheses(node),
"must only call this function on known names");
- if (nameIsEvalAnyParentheses(node, cx))
+ if (isEvalAnyParentheses(node, cx))
return js_eval_str;
- if (node->pn_atom == cx->names().arguments)
+ if (isArgumentsAnyParentheses(node, cx))
return js_arguments_str;
return nullptr;
}
- bool nameIsUnparenthesizedAsync(ParseNode* node, ExclusiveContext* cx) {
- MOZ_ASSERT(isNameAnyParentheses(node),
- "must only call this function on known names");
-
- return node->pn_atom == cx->names().async;
+ bool isAsyncKeyword(ParseNode* node, ExclusiveContext* cx) {
+ return node->isKind(PNK_NAME) &&
+ node->pn_pos.begin + strlen("async") == node->pn_pos.end &&
+ node->pn_atom == cx->names().async;
}
bool isCall(ParseNode* pn) {
diff --git a/js/src/frontend/GenerateReservedWords.py b/js/src/frontend/GenerateReservedWords.py
new file mode 100644
index 000000000..bd698cc5f
--- /dev/null
+++ b/js/src/frontend/GenerateReservedWords.py
@@ -0,0 +1,213 @@
+# 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/.
+
+import re
+import sys
+
+def read_reserved_word_list(filename):
+ macro_pat = re.compile(r"^\s*macro\(([^,]+), *[^,]+, *[^\)]+\)\s*\\?$")
+
+ reserved_word_list = []
+ index = 0
+ with open(filename, 'r') as f:
+ for line in f:
+ m = macro_pat.search(line)
+ if m:
+ reserved_word_list.append((index, m.group(1)))
+ index += 1
+
+ assert(len(reserved_word_list) != 0)
+
+ return reserved_word_list
+
+def line(opt, s):
+ opt['output'].write('{}{}\n'.format(' ' * opt['indent_level'], s))
+
+def indent(opt):
+ opt['indent_level'] += 1
+
+def dedent(opt):
+ opt['indent_level'] -= 1
+
+def span_and_count_at(reserved_word_list, column):
+ assert(len(reserved_word_list) != 0);
+
+ chars_dict = {}
+ for index, word in reserved_word_list:
+ chars_dict[ord(word[column])] = True
+
+ chars = sorted(chars_dict.keys())
+ return chars[-1] - chars[0] + 1, len(chars)
+
+def optimal_switch_column(opt, reserved_word_list, columns, unprocessed_columns):
+ assert(len(reserved_word_list) != 0);
+ assert(unprocessed_columns != 0);
+
+ min_count = 0
+ min_span = 0
+ min_count_index = 0
+ min_span_index = 0
+
+ for index in range(0, unprocessed_columns):
+ span, count = span_and_count_at(reserved_word_list, columns[index])
+ assert(span != 0)
+
+ if span == 1:
+ assert(count == 1)
+ return 1, True
+
+ assert(count != 1)
+ if index == 0 or min_span > span:
+ min_span = span
+ min_span_index = index
+
+ if index == 0 or min_count > count:
+ min_count = count
+ min_count_index = index
+
+ if min_count <= opt['use_if_threshold']:
+ return min_count_index, True
+
+ return min_span_index, False
+
+def split_list_per_column(reserved_word_list, column):
+ assert(len(reserved_word_list) != 0);
+
+ column_dict = {}
+ for item in reserved_word_list:
+ index, word = item
+ per_column = column_dict.setdefault(word[column], [])
+ per_column.append(item)
+
+ return sorted(column_dict.items(), key=lambda (char, word): ord(char))
+
+def generate_letter_switch(opt, unprocessed_columns, reserved_word_list,
+ columns=None):
+ assert(len(reserved_word_list) != 0);
+
+ if not columns:
+ columns = range(0, unprocessed_columns)
+
+ if len(reserved_word_list) == 1:
+ index, word = reserved_word_list[0]
+
+ if unprocessed_columns == 0:
+ line(opt, 'JSRW_GOT_MATCH({}) /* {} */'.format(index, word))
+ return
+
+ if unprocessed_columns > opt['char_tail_test_threshold']:
+ line(opt, 'JSRW_TEST_GUESS({}) /* {} */'.format(index, word))
+ return
+
+ conds = []
+ for column in columns[0:unprocessed_columns]:
+ quoted = repr(word[column])
+ conds.append('JSRW_AT({})=={}'.format(column, quoted))
+
+ line(opt, 'if ({}) {{'.format(' && '.join(conds)))
+
+ indent(opt)
+ line(opt, 'JSRW_GOT_MATCH({}) /* {} */'.format(index, word))
+ dedent(opt)
+
+ line(opt, '}')
+ line(opt, 'JSRW_NO_MATCH()')
+ return
+
+ assert(unprocessed_columns != 0);
+
+ optimal_column_index, use_if = optimal_switch_column(opt, reserved_word_list,
+ columns,
+ unprocessed_columns)
+ optimal_column = columns[optimal_column_index]
+
+ # Make a copy to avoid breaking passed list.
+ columns = columns[:]
+ columns[optimal_column_index] = columns[unprocessed_columns - 1]
+
+ list_per_column = split_list_per_column(reserved_word_list, optimal_column)
+
+ if not use_if:
+ line(opt, 'switch (JSRW_AT({})) {{'.format(optimal_column))
+
+ for char, reserved_word_list_per_column in list_per_column:
+ quoted = repr(char)
+ if use_if:
+ line(opt, 'if (JSRW_AT({}) == {}) {{'.format(optimal_column,
+ quoted))
+ else:
+ line(opt, ' case {}:'.format(quoted))
+
+ indent(opt)
+ generate_letter_switch(opt, unprocessed_columns - 1,
+ reserved_word_list_per_column, columns)
+ dedent(opt)
+
+ if use_if:
+ line(opt, '}')
+
+ if not use_if:
+ line(opt, '}')
+
+ line(opt, 'JSRW_NO_MATCH()')
+
+def split_list_per_length(reserved_word_list):
+ assert(len(reserved_word_list) != 0);
+
+ length_dict = {}
+ for item in reserved_word_list:
+ index, word = item
+ per_length = length_dict.setdefault(len(word), [])
+ per_length.append(item)
+
+ return sorted(length_dict.items(), key=lambda (length, word): length)
+
+def generate_switch(opt, reserved_word_list):
+ assert(len(reserved_word_list) != 0);
+
+ line(opt, '/*')
+ line(opt, ' * Generating switch for the list of {} entries:'.format(len(reserved_word_list)))
+ for index, word in reserved_word_list:
+ line(opt, ' * {}'.format(word))
+ line(opt, ' */')
+
+ list_per_length = split_list_per_length(reserved_word_list)
+
+ use_if = False
+ if len(list_per_length) < opt['use_if_threshold']:
+ use_if = True
+
+ if not use_if:
+ line(opt, 'switch (JSRW_LENGTH()) {')
+
+ for length, reserved_word_list_per_length in list_per_length:
+ if use_if:
+ line(opt, 'if (JSRW_LENGTH() == {}) {{'.format(length))
+ else:
+ line(opt, ' case {}:'.format(length))
+
+ indent(opt)
+ generate_letter_switch(opt, length, reserved_word_list_per_length)
+ dedent(opt)
+
+ if use_if:
+ line(opt, '}')
+
+ if not use_if:
+ line(opt, '}')
+ line(opt, 'JSRW_NO_MATCH()')
+
+def main(output, reserved_words_h):
+ reserved_word_list = read_reserved_word_list(reserved_words_h)
+
+ opt = {
+ 'indent_level': 1,
+ 'use_if_threshold': 3,
+ 'char_tail_test_threshold': 4,
+ 'output': output
+ }
+ generate_switch(opt, reserved_word_list)
+
+if __name__ == '__main__':
+ main(sys.stdout, *sys.argv[1:])
diff --git a/js/src/frontend/NameAnalysisTypes.h b/js/src/frontend/NameAnalysisTypes.h
index d39e177fb..2d327c827 100644
--- a/js/src/frontend/NameAnalysisTypes.h
+++ b/js/src/frontend/NameAnalysisTypes.h
@@ -78,6 +78,7 @@ enum class DeclarationKind : uint8_t
Const,
Import,
BodyLevelFunction,
+ ModuleBodyLevelFunction,
LexicalFunction,
VarForAnnexBLexicalFunction,
SimpleCatchParameter,
@@ -95,6 +96,7 @@ DeclarationKindToBindingKind(DeclarationKind kind)
case DeclarationKind::Var:
case DeclarationKind::BodyLevelFunction:
+ case DeclarationKind::ModuleBodyLevelFunction:
case DeclarationKind::VarForAnnexBLexicalFunction:
case DeclarationKind::ForOfVar:
return BindingKind::Var;
@@ -124,6 +126,7 @@ DeclarationKindIsLexical(DeclarationKind kind)
// Used in Parser to track declared names.
class DeclaredNameInfo
{
+ uint32_t pos_;
DeclarationKind kind_;
// If the declared name is a binding, whether the binding is closed
@@ -132,8 +135,9 @@ class DeclaredNameInfo
bool closedOver_;
public:
- explicit DeclaredNameInfo(DeclarationKind kind)
- : kind_(kind),
+ explicit DeclaredNameInfo(DeclarationKind kind, uint32_t pos)
+ : pos_(pos),
+ kind_(kind),
closedOver_(false)
{ }
@@ -144,6 +148,12 @@ class DeclaredNameInfo
return kind_;
}
+ static const uint32_t npos = uint32_t(-1);
+
+ uint32_t pos() const {
+ return pos_;
+ }
+
void alterKind(DeclarationKind kind) {
kind_ = kind;
}
diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp
index ce1318f0b..dc54d0a88 100644
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -316,7 +316,8 @@ class NameResolver
return false;
// Next is the callsite object node. This node only contains
- // internal strings and an array -- no user-controlled expressions.
+ // internal strings or undefined and an array -- no user-controlled
+ // expressions.
element = element->pn_next;
#ifdef DEBUG
{
@@ -326,7 +327,7 @@ class NameResolver
for (ParseNode* kid = array->pn_head; kid; kid = kid->pn_next)
MOZ_ASSERT(kid->isKind(PNK_TEMPLATE_STRING));
for (ParseNode* next = array->pn_next; next; next = next->pn_next)
- MOZ_ASSERT(next->isKind(PNK_TEMPLATE_STRING));
+ MOZ_ASSERT(next->isKind(PNK_TEMPLATE_STRING) || next->isKind(PNK_RAW_UNDEFINED));
}
#endif
@@ -382,6 +383,7 @@ class NameResolver
case PNK_TRUE:
case PNK_FALSE:
case PNK_NULL:
+ case PNK_RAW_UNDEFINED:
case PNK_ELISION:
case PNK_GENERATOR:
case PNK_NUMBER:
diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp
index 91f17625c..5fe64e3d3 100644
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -190,6 +190,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
case PNK_TRUE:
case PNK_FALSE:
case PNK_NULL:
+ case PNK_RAW_UNDEFINED:
case PNK_ELISION:
case PNK_GENERATOR:
case PNK_NUMBER:
@@ -685,6 +686,7 @@ NullaryNode::dump()
case PNK_TRUE: fprintf(stderr, "#true"); break;
case PNK_FALSE: fprintf(stderr, "#false"); break;
case PNK_NULL: fprintf(stderr, "#null"); break;
+ case PNK_RAW_UNDEFINED: fprintf(stderr, "#undefined"); break;
case PNK_NUMBER: {
ToCStringBuf cbuf;
diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h
index c3523afe4..1f20f3988 100644
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -54,6 +54,7 @@ class ObjectBox;
F(TRUE) \
F(FALSE) \
F(NULL) \
+ F(RAW_UNDEFINED) \
F(THIS) \
F(FUNCTION) \
F(MODULE) \
@@ -406,7 +407,8 @@ IsTypeofKind(ParseNodeKind kind)
* PNK_NUMBER dval pn_dval: double value of numeric literal
* PNK_TRUE, nullary pn_op: JSOp bytecode
* PNK_FALSE,
- * PNK_NULL
+ * PNK_NULL,
+ * PNK_RAW_UNDEFINED
*
* PNK_THIS, unary pn_kid: '.this' Name if function `this`, else nullptr
* PNK_SUPERBASE unary pn_kid: '.this' Name
@@ -686,7 +688,8 @@ class ParseNode
isKind(PNK_STRING) ||
isKind(PNK_TRUE) ||
isKind(PNK_FALSE) ||
- isKind(PNK_NULL);
+ isKind(PNK_NULL) ||
+ isKind(PNK_RAW_UNDEFINED);
}
/* Return true if this node appears in a Directive Prologue. */
@@ -1141,6 +1144,16 @@ class NullLiteral : public ParseNode
explicit NullLiteral(const TokenPos& pos) : ParseNode(PNK_NULL, JSOP_NULL, PN_NULLARY, pos) { }
};
+// This is only used internally, currently just for tagged templates.
+// It represents the value 'undefined' (aka `void 0`), like NullLiteral
+// represents the value 'null'.
+class RawUndefinedLiteral : public ParseNode
+{
+ public:
+ explicit RawUndefinedLiteral(const TokenPos& pos)
+ : ParseNode(PNK_RAW_UNDEFINED, JSOP_UNDEFINED, PN_NULLARY, pos) { }
+};
+
class BooleanLiteral : public ParseNode
{
public:
@@ -1296,8 +1309,9 @@ struct ClassNames : public BinaryNode {
};
struct ClassNode : public TernaryNode {
- ClassNode(ParseNode* names, ParseNode* heritage, ParseNode* methodsOrBlock)
- : TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, methodsOrBlock)
+ ClassNode(ParseNode* names, ParseNode* heritage, ParseNode* methodsOrBlock,
+ const TokenPos& pos)
+ : TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, methodsOrBlock, pos)
{
MOZ_ASSERT_IF(names, names->is<ClassNames>());
MOZ_ASSERT(methodsOrBlock->is<LexicalScopeNode>() ||
@@ -1361,6 +1375,7 @@ ParseNode::isConstant()
case PNK_STRING:
case PNK_TEMPLATE_STRING:
case PNK_NULL:
+ case PNK_RAW_UNDEFINED:
case PNK_FALSE:
case PNK_TRUE:
return true;
diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
index 3b7a0e612..0c279591f 100644
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -19,6 +19,8 @@
#include "frontend/Parser.h"
+#include "mozilla/Sprintf.h"
+
#include <new>
#include "jsapi.h"
@@ -62,19 +64,33 @@ using AddDeclaredNamePtr = ParseContext::Scope::AddDeclaredNamePtr;
using BindingIter = ParseContext::Scope::BindingIter;
using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr;
-/* Read a token. Report an error and return null() if that token isn't of type tt. */
-#define MUST_MATCH_TOKEN_MOD(tt, modifier, errorNumber) \
+// Read a token. Report an error and return null() if that token doesn't match
+// to the condition. Do not use MUST_MATCH_TOKEN_INTERNAL directly.
+#define MUST_MATCH_TOKEN_INTERNAL(cond, modifier, errorReport) \
JS_BEGIN_MACRO \
TokenKind token; \
if (!tokenStream.getToken(&token, modifier)) \
return null(); \
- if (token != tt) { \
- error(errorNumber); \
+ if (!(cond)) { \
+ errorReport; \
return null(); \
} \
JS_END_MACRO
-#define MUST_MATCH_TOKEN(tt, errno) MUST_MATCH_TOKEN_MOD(tt, TokenStream::None, errno)
+#define MUST_MATCH_TOKEN_MOD(tt, modifier, errorNumber) \
+ MUST_MATCH_TOKEN_INTERNAL(token == tt, modifier, error(errorNumber))
+
+#define MUST_MATCH_TOKEN(tt, errorNumber) \
+ MUST_MATCH_TOKEN_MOD(tt, TokenStream::None, errorNumber)
+
+#define MUST_MATCH_TOKEN_FUNC_MOD(func, modifier, errorNumber) \
+ MUST_MATCH_TOKEN_INTERNAL((func)(token), modifier, error(errorNumber))
+
+#define MUST_MATCH_TOKEN_FUNC(func, errorNumber) \
+ MUST_MATCH_TOKEN_FUNC_MOD(func, TokenStream::None, errorNumber)
+
+#define MUST_MATCH_TOKEN_MOD_WITH_REPORT(tt, modifier, errorReport) \
+ MUST_MATCH_TOKEN_INTERNAL(token == tt, modifier, errorReport)
template <class T, class U>
static inline void
@@ -106,6 +122,7 @@ DeclarationKindString(DeclarationKind kind)
case DeclarationKind::Import:
return "import";
case DeclarationKind::BodyLevelFunction:
+ case DeclarationKind::ModuleBodyLevelFunction:
case DeclarationKind::LexicalFunction:
return "function";
case DeclarationKind::VarForAnnexBLexicalFunction:
@@ -127,7 +144,8 @@ StatementKindIsBraced(StatementKind kind)
kind == StatementKind::Switch ||
kind == StatementKind::Try ||
kind == StatementKind::Catch ||
- kind == StatementKind::Finally;
+ kind == StatementKind::Finally ||
+ kind == StatementKind::Class;
}
void
@@ -189,11 +207,12 @@ ParseContext::Scope::addCatchParameters(ParseContext* pc, Scope& catchParamScope
for (DeclaredNameMap::Range r = catchParamScope.declared_->all(); !r.empty(); r.popFront()) {
DeclarationKind kind = r.front().value()->kind();
+ uint32_t pos = r.front().value()->pos();
MOZ_ASSERT(DeclarationKindIsCatchParameter(kind));
JSAtom* name = r.front().key();
AddDeclaredNamePtr p = lookupDeclaredNameForAdd(name);
MOZ_ASSERT(!p);
- if (!addDeclaredName(pc, p, name, kind))
+ if (!addDeclaredName(pc, p, name, kind, pos))
return false;
}
@@ -332,7 +351,8 @@ ParseContext::init()
namedLambdaScope_->lookupDeclaredNameForAdd(fun->explicitName());
MOZ_ASSERT(!p);
if (!namedLambdaScope_->addDeclaredName(this, p, fun->explicitName(),
- DeclarationKind::Const))
+ DeclarationKind::Const,
+ DeclaredNameInfo::npos))
{
return false;
}
@@ -441,7 +461,7 @@ UsedNameTracker::rewind(RewindToken token)
}
FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* traceListHead,
- JSFunction* fun, uint32_t preludeStart,
+ JSFunction* fun, uint32_t toStringStart,
Directives directives, bool extraWarnings,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind)
: ObjectBox(fun, traceListHead),
@@ -455,7 +475,8 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* trac
bufEnd(0),
startLine(1),
startColumn(0),
- preludeStart(preludeStart),
+ toStringStart(toStringStart),
+ toStringEnd(0),
length(0),
generatorKindBits_(GeneratorKindAsBits(generatorKind)),
asyncKindBits_(AsyncKindAsBits(asyncKind)),
@@ -474,6 +495,7 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* trac
usesThis(false),
usesReturn(false),
hasRest_(false),
+ isExprBody_(false),
funCxFlags()
{
// Functions created at parse time may be set singleton after parsing and
@@ -525,10 +547,16 @@ FunctionBox::initWithEnclosingParseContext(ParseContext* enclosing, FunctionSynt
allowNewTarget_ = true;
allowSuperProperty_ = fun->allowSuperProperty();
- if (kind == DerivedClassConstructor) {
- setDerivedClassConstructor();
- allowSuperCall_ = true;
- needsThisTDZChecks_ = true;
+ if (kind == ClassConstructor || kind == DerivedClassConstructor) {
+ auto stmt = enclosing->findInnermostStatement<ParseContext::ClassStatement>();
+ MOZ_ASSERT(stmt);
+ stmt->constructorBox = this;
+
+ if (kind == DerivedClassConstructor) {
+ setDerivedClassConstructor();
+ allowSuperCall_ = true;
+ needsThisTDZChecks_ = true;
+ }
}
if (isGenexpLambda)
@@ -570,97 +598,136 @@ FunctionBox::initWithEnclosingScope(Scope* enclosingScope)
computeInWith(enclosingScope);
}
-template <typename ParseHandler>
void
-Parser<ParseHandler>::error(unsigned errorNumber, ...)
+ParserBase::error(unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
#ifdef DEBUG
bool result =
#endif
- tokenStream.reportCompileErrorNumberVA(pos().begin, JSREPORT_ERROR, errorNumber, args);
+ tokenStream.reportCompileErrorNumberVA(nullptr, pos().begin, JSREPORT_ERROR,
+ errorNumber, args);
MOZ_ASSERT(!result, "reporting an error returned true?");
va_end(args);
}
-template <typename ParseHandler>
void
-Parser<ParseHandler>::errorAt(uint32_t offset, unsigned errorNumber, ...)
+ParserBase::errorWithNotes(UniquePtr<JSErrorNotes> notes, unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
#ifdef DEBUG
bool result =
#endif
- tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_ERROR, errorNumber, args);
+ tokenStream.reportCompileErrorNumberVA(Move(notes), pos().begin, JSREPORT_ERROR,
+ errorNumber, args);
+ MOZ_ASSERT(!result, "reporting an error returned true?");
+ va_end(args);
+}
+
+void
+ParserBase::errorAt(uint32_t offset, unsigned errorNumber, ...)
+{
+ va_list args;
+ va_start(args, errorNumber);
+#ifdef DEBUG
+ bool result =
+#endif
+ tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_ERROR, errorNumber, args);
+ MOZ_ASSERT(!result, "reporting an error returned true?");
+ va_end(args);
+}
+
+void
+ParserBase::errorWithNotesAt(UniquePtr<JSErrorNotes> notes, uint32_t offset,
+ unsigned errorNumber, ...)
+{
+ va_list args;
+ va_start(args, errorNumber);
+#ifdef DEBUG
+ bool result =
+#endif
+ tokenStream.reportCompileErrorNumberVA(Move(notes), offset, JSREPORT_ERROR,
+ errorNumber, args);
MOZ_ASSERT(!result, "reporting an error returned true?");
va_end(args);
}
-template <typename ParseHandler>
bool
-Parser<ParseHandler>::warning(unsigned errorNumber, ...)
+ParserBase::warning(unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
bool result =
- tokenStream.reportCompileErrorNumberVA(pos().begin, JSREPORT_WARNING, errorNumber, args);
+ tokenStream.reportCompileErrorNumberVA(nullptr, pos().begin, JSREPORT_WARNING,
+ errorNumber, args);
va_end(args);
return result;
}
-template <typename ParseHandler>
bool
-Parser<ParseHandler>::warningAt(uint32_t offset, unsigned errorNumber, ...)
+ParserBase::warningAt(uint32_t offset, unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
bool result =
- tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_WARNING, errorNumber, args);
+ tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_WARNING,
+ errorNumber, args);
va_end(args);
return result;
}
-template <typename ParseHandler>
bool
-Parser<ParseHandler>::extraWarning(unsigned errorNumber, ...)
+ParserBase::extraWarning(unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
- bool result = tokenStream.reportExtraWarningErrorNumberVA(pos().begin, errorNumber, args);
+ bool result = tokenStream.reportExtraWarningErrorNumberVA(nullptr, pos().begin,
+ errorNumber, args);
va_end(args);
return result;
}
-template <typename ParseHandler>
bool
-Parser<ParseHandler>::strictModeError(unsigned errorNumber, ...)
+ParserBase::extraWarningAt(uint32_t offset, unsigned errorNumber, ...)
+{
+ va_list args;
+ va_start(args, errorNumber);
+
+ bool result =
+ tokenStream.reportExtraWarningErrorNumberVA(nullptr, offset, errorNumber, args);
+
+ va_end(args);
+ return result;
+}
+
+bool
+ParserBase::strictModeError(unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
bool res =
- tokenStream.reportStrictModeErrorNumberVA(pos().begin, pc->sc()->strict(),
+ tokenStream.reportStrictModeErrorNumberVA(nullptr, pos().begin, pc->sc()->strict(),
errorNumber, args);
va_end(args);
return res;
}
-template <typename ParseHandler>
bool
-Parser<ParseHandler>::strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...)
+ParserBase::strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
bool res =
- tokenStream.reportStrictModeErrorNumberVA(offset, pc->sc()->strict(), errorNumber, args);
+ tokenStream.reportStrictModeErrorNumberVA(nullptr, offset, pc->sc()->strict(),
+ errorNumber, args);
va_end(args);
return res;
}
-template <typename ParseHandler>
bool
-Parser<ParseHandler>::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...)
+ParserBase::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
@@ -668,17 +735,21 @@ Parser<ParseHandler>::reportNoOffset(ParseReportKind kind, bool strict, unsigned
uint32_t offset = TokenStream::NoOffset;
switch (kind) {
case ParseError:
- result = tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_ERROR, errorNumber, args);
+ result = tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_ERROR,
+ errorNumber, args);
break;
case ParseWarning:
result =
- tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_WARNING, errorNumber, args);
+ tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_WARNING,
+ errorNumber, args);
break;
case ParseExtraWarning:
- result = tokenStream.reportExtraWarningErrorNumberVA(offset, errorNumber, args);
+ result = tokenStream.reportExtraWarningErrorNumberVA(nullptr, offset,
+ errorNumber, args);
break;
case ParseStrictError:
- result = tokenStream.reportStrictModeErrorNumberVA(offset, strict, errorNumber, args);
+ result = tokenStream.reportStrictModeErrorNumberVA(nullptr, offset, strict,
+ errorNumber, args);
break;
}
va_end(args);
@@ -686,7 +757,7 @@ Parser<ParseHandler>::reportNoOffset(ParseReportKind kind, bool strict, unsigned
}
template <>
-bool
+inline bool
Parser<FullParseHandler>::abortIfSyntaxParser()
{
handler.disableSyntaxParser();
@@ -694,23 +765,21 @@ Parser<FullParseHandler>::abortIfSyntaxParser()
}
template <>
-bool
+inline bool
Parser<SyntaxParseHandler>::abortIfSyntaxParser()
{
abortedSyntaxParse = true;
return false;
}
-template <typename ParseHandler>
-Parser<ParseHandler>::Parser(ExclusiveContext* cx, LifoAlloc& alloc,
- const ReadOnlyCompileOptions& options,
- const char16_t* chars, size_t length,
- bool foldConstants,
- UsedNameTracker& usedNames,
- Parser<SyntaxParseHandler>* syntaxParser,
- LazyScript* lazyOuterFunction)
- : AutoGCRooter(cx, PARSER),
- context(cx),
+ParserBase::ParserBase(ExclusiveContext* cx, LifoAlloc& alloc,
+ const ReadOnlyCompileOptions& options,
+ const char16_t* chars, size_t length,
+ bool foldConstants,
+ UsedNameTracker& usedNames,
+ Parser<SyntaxParseHandler>* syntaxParser,
+ LazyScript* lazyOuterFunction)
+ : context(cx),
alloc(alloc),
tokenStream(cx, options, chars, length, thisForCtor()),
traceListHead(nullptr),
@@ -725,17 +794,44 @@ Parser<ParseHandler>::Parser(ExclusiveContext* cx, LifoAlloc& alloc,
#endif
abortedSyntaxParse(false),
isUnexpectedEOF_(false),
- handler(cx, alloc, tokenStream, syntaxParser, lazyOuterFunction)
+ awaitIsKeyword_(false)
{
cx->perThreadData->frontendCollectionPool.addActiveCompilation();
+ tempPoolMark = alloc.mark();
+}
+
+ParserBase::~ParserBase()
+{
+ alloc.release(tempPoolMark);
+
+ /*
+ * The parser can allocate enormous amounts of memory for large functions.
+ * Eagerly free the memory now (which otherwise won't be freed until the
+ * next GC) to avoid unnecessary OOMs.
+ */
+ alloc.freeAllIfHugeAndUnused();
+
+ context->perThreadData->frontendCollectionPool.removeActiveCompilation();
+}
+template <typename ParseHandler>
+Parser<ParseHandler>::Parser(ExclusiveContext* cx, LifoAlloc& alloc,
+ const ReadOnlyCompileOptions& options,
+ const char16_t* chars, size_t length,
+ bool foldConstants,
+ UsedNameTracker& usedNames,
+ Parser<SyntaxParseHandler>* syntaxParser,
+ LazyScript* lazyOuterFunction)
+ : ParserBase(cx, alloc, options, chars, length, foldConstants, usedNames, syntaxParser,
+ lazyOuterFunction),
+ AutoGCRooter(cx, PARSER),
+ handler(cx, alloc, tokenStream, syntaxParser, lazyOuterFunction)
+{
// The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
// which are not generated if functions are parsed lazily. Note that the
// standard "use strict" does not inhibit lazy parsing.
if (options.extraWarningsOption)
handler.disableSyntaxParser();
-
- tempPoolMark = alloc.mark();
}
template<typename ParseHandler>
@@ -746,26 +842,29 @@ Parser<ParseHandler>::checkOptions()
checkOptionsCalled = true;
#endif
- if (!tokenStream.checkOptions())
- return false;
-
- return true;
+ return tokenStream.checkOptions();
}
template <typename ParseHandler>
Parser<ParseHandler>::~Parser()
{
MOZ_ASSERT(checkOptionsCalled);
- alloc.release(tempPoolMark);
+}
- /*
- * The parser can allocate enormous amounts of memory for large functions.
- * Eagerly free the memory now (which otherwise won't be freed until the
- * next GC) to avoid unnecessary OOMs.
- */
- alloc.freeAllIfHugeAndUnused();
+template <>
+void
+Parser<SyntaxParseHandler>::setAwaitIsKeyword(bool isKeyword)
+{
+ awaitIsKeyword_ = isKeyword;
+}
- context->perThreadData->frontendCollectionPool.removeActiveCompilation();
+template <>
+void
+Parser<FullParseHandler>::setAwaitIsKeyword(bool isKeyword)
+{
+ awaitIsKeyword_ = isKeyword;
+ if (Parser<SyntaxParseHandler>* parser = handler.syntaxParser)
+ parser->setAwaitIsKeyword(isKeyword);
}
template <typename ParseHandler>
@@ -795,7 +894,7 @@ Parser<ParseHandler>::newObjectBox(JSObject* obj)
template <typename ParseHandler>
FunctionBox*
-Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun, uint32_t preludeStart,
+Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart,
Directives inheritedDirectives,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
bool tryAnnexB)
@@ -811,7 +910,7 @@ Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun, uint32_t preludeS
* function.
*/
FunctionBox* funbox =
- alloc.new_<FunctionBox>(context, alloc, traceListHead, fun, preludeStart,
+ alloc.new_<FunctionBox>(context, alloc, traceListHead, fun, toStringStart,
inheritedDirectives, options().extraWarningsOption,
generatorKind, asyncKind);
if (!funbox) {
@@ -894,38 +993,17 @@ Parser<ParseHandler>::parse()
/*
* Strict mode forbids introducing new definitions for 'eval', 'arguments', or
- * for any strict mode reserved keyword.
+ * for any strict mode reserved word.
*/
-template <typename ParseHandler>
bool
-Parser<ParseHandler>::isValidStrictBinding(PropertyName* name)
+ParserBase::isValidStrictBinding(PropertyName* name)
{
return name != context->names().eval &&
name != context->names().arguments &&
name != context->names().let &&
name != context->names().static_ &&
- !(IsKeyword(name) && name != context->names().await);
-}
-
-/*
- * Check that it is permitted to introduce a binding for |name|. Use |pos| for
- * reporting error locations.
- */
-template <typename ParseHandler>
-bool
-Parser<ParseHandler>::checkStrictBinding(PropertyName* name, TokenPos pos)
-{
- if (!pc->sc()->needStrictChecks())
- return true;
-
- if (!isValidStrictBinding(name)) {
- JSAutoByteString bytes;
- if (!AtomToPrintableString(context, name, &bytes))
- return false;
- return strictModeErrorAt(pos.begin, JSMSG_BAD_BINDING, bytes.ptr());
- }
-
- return true;
+ name != context->names().yield &&
+ !IsStrictReservedWord(name);
}
/*
@@ -952,13 +1030,71 @@ Parser<ParseHandler>::hasValidSimpleStrictParameterNames()
template <typename ParseHandler>
void
-Parser<ParseHandler>::reportRedeclaration(HandlePropertyName name, DeclarationKind kind,
- TokenPos pos)
+Parser<ParseHandler>::reportMissingClosing(unsigned errorNumber, unsigned noteNumber,
+ uint32_t openedPos)
+{
+ auto notes = MakeUnique<JSErrorNotes>();
+ if (!notes)
+ return;
+
+ uint32_t line, column;
+ tokenStream.srcCoords.lineNumAndColumnIndex(openedPos, &line, &column);
+
+ const size_t MaxWidth = sizeof("4294967295");
+ char columnNumber[MaxWidth];
+ SprintfLiteral(columnNumber, "%" PRIu32, column);
+ char lineNumber[MaxWidth];
+ SprintfLiteral(lineNumber, "%" PRIu32, line);
+
+ if (!notes->addNoteASCII(pc->sc()->context,
+ getFilename(), line, column,
+ GetErrorMessage, nullptr,
+ noteNumber, lineNumber, columnNumber))
+ {
+ return;
+ }
+
+ errorWithNotes(Move(notes), errorNumber);
+}
+
+template <typename ParseHandler>
+void
+Parser<ParseHandler>::reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind,
+ TokenPos pos, uint32_t prevPos)
{
JSAutoByteString bytes;
if (!AtomToPrintableString(context, name, &bytes))
return;
- errorAt(pos.begin, JSMSG_REDECLARED_VAR, DeclarationKindString(kind), bytes.ptr());
+
+ if (prevPos == DeclaredNameInfo::npos) {
+ errorAt(pos.begin, JSMSG_REDECLARED_VAR, DeclarationKindString(prevKind), bytes.ptr());
+ return;
+ }
+
+ auto notes = MakeUnique<JSErrorNotes>();
+ if (!notes)
+ return;
+
+ uint32_t line, column;
+ tokenStream.srcCoords.lineNumAndColumnIndex(prevPos, &line, &column);
+
+ const size_t MaxWidth = sizeof("4294967295");
+ char columnNumber[MaxWidth];
+ SprintfLiteral(columnNumber, "%" PRIu32, column);
+ char lineNumber[MaxWidth];
+ SprintfLiteral(lineNumber, "%" PRIu32, line);
+
+ if (!notes->addNoteASCII(pc->sc()->context,
+ getFilename(), line, column,
+ GetErrorMessage, nullptr,
+ JSMSG_REDECLARED_PREV,
+ lineNumber, columnNumber))
+ {
+ return;
+ }
+
+ errorWithNotesAt(Move(notes), pos.begin, JSMSG_REDECLARED_VAR,
+ DeclarationKindString(prevKind), bytes.ptr());
}
// notePositionalFormalParameter is called for both the arguments of a regular
@@ -973,6 +1109,7 @@ Parser<ParseHandler>::reportRedeclaration(HandlePropertyName name, DeclarationKi
template <typename ParseHandler>
bool
Parser<ParseHandler>::notePositionalFormalParameter(Node fn, HandlePropertyName name,
+ uint32_t beginPos,
bool disallowDuplicateParams,
bool* duplicatedParam)
{
@@ -997,7 +1134,7 @@ Parser<ParseHandler>::notePositionalFormalParameter(Node fn, HandlePropertyName
*duplicatedParam = true;
} else {
DeclarationKind kind = DeclarationKind::PositionalFormalParameter;
- if (!pc->functionScope().addDeclaredName(pc, p, name, kind))
+ if (!pc->functionScope().addDeclaredName(pc, p, name, kind, beginPos))
return false;
}
@@ -1010,9 +1147,6 @@ Parser<ParseHandler>::notePositionalFormalParameter(Node fn, HandlePropertyName
if (!paramNode)
return false;
- if (!checkStrictBinding(name, pos()))
- return false;
-
handler.addFunctionFormalParameter(fn, paramNode);
return true;
}
@@ -1104,8 +1238,8 @@ DeclarationKindIsParameter(DeclarationKind kind)
template <typename ParseHandler>
bool
-Parser<ParseHandler>::tryDeclareVar(HandlePropertyName name, DeclarationKind kind,
- Maybe<DeclarationKind>* redeclaredKind)
+Parser<ParseHandler>::tryDeclareVar(HandlePropertyName name, DeclarationKind kind, uint32_t beginPos,
+ Maybe<DeclarationKind>* redeclaredKind, uint32_t* prevPos)
{
MOZ_ASSERT(DeclarationKindIsVar(kind));
@@ -1178,17 +1312,29 @@ Parser<ParseHandler>::tryDeclareVar(HandlePropertyName name, DeclarationKind kin
if (!annexB35Allowance && !annexB33Allowance) {
*redeclaredKind = Some(declaredKind);
+ *prevPos = p->value()->pos();
return true;
}
+ } else if (kind == DeclarationKind::VarForAnnexBLexicalFunction) {
+ MOZ_ASSERT(DeclarationKindIsParameter(declaredKind));
+
+ // Annex B.3.3.1 disallows redeclaring parameter names.
+ // We don't need to set *prevPos here since this case is not
+ // an error.
+ *redeclaredKind = Some(declaredKind);
+ return true;
}
} else {
- if (!scope->addDeclaredName(pc, p, name, kind))
+ if (!scope->addDeclaredName(pc, p, name, kind, beginPos))
return false;
}
}
- if (!pc->sc()->strict() && pc->sc()->isEvalContext())
+ if (!pc->sc()->strict() && pc->sc()->isEvalContext()) {
*redeclaredKind = isVarRedeclaredInEval(name, kind);
+ // We don't have position information at runtime.
+ *prevPos = DeclaredNameInfo::npos;
+ }
return true;
}
@@ -1196,11 +1342,34 @@ Parser<ParseHandler>::tryDeclareVar(HandlePropertyName name, DeclarationKind kin
template <typename ParseHandler>
bool
Parser<ParseHandler>::tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name,
- bool* tryAnnexB)
+ uint32_t beginPos, bool* tryAnnexB)
{
Maybe<DeclarationKind> redeclaredKind;
- if (!tryDeclareVar(name, DeclarationKind::VarForAnnexBLexicalFunction, &redeclaredKind))
+ uint32_t unused;
+ if (!tryDeclareVar(name, DeclarationKind::VarForAnnexBLexicalFunction, beginPos,
+ &redeclaredKind, &unused))
+ {
return false;
+ }
+
+ if (!redeclaredKind && pc->isFunctionBox()) {
+ ParseContext::Scope& funScope = pc->functionScope();
+ ParseContext::Scope& varScope = pc->varScope();
+ if (&funScope != &varScope) {
+ // Annex B.3.3.1 disallows redeclaring parameter names. In the
+ // presence of parameter expressions, parameter names are on the
+ // function scope, which encloses the var scope. This means
+ // tryDeclareVar call above would not catch this case, so test it
+ // manually.
+ if (AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(name)) {
+ DeclarationKind declaredKind = p->value()->kind();
+ if (DeclarationKindIsParameter(declaredKind))
+ redeclaredKind = Some(declaredKind);
+ else
+ MOZ_ASSERT(FunctionScope::isSpecialName(context, name));
+ }
+ }
+ }
if (redeclaredKind) {
// If an early error would have occurred, undo all the
@@ -1248,25 +1417,41 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind
if (pc->useAsmOrInsideUseAsm())
return true;
- if (!checkStrictBinding(name, pos))
- return false;
-
switch (kind) {
case DeclarationKind::Var:
case DeclarationKind::BodyLevelFunction:
case DeclarationKind::ForOfVar: {
Maybe<DeclarationKind> redeclaredKind;
- if (!tryDeclareVar(name, kind, &redeclaredKind))
+ uint32_t prevPos;
+ if (!tryDeclareVar(name, kind, pos.begin, &redeclaredKind, &prevPos))
return false;
if (redeclaredKind) {
- reportRedeclaration(name, *redeclaredKind, pos);
+ reportRedeclaration(name, *redeclaredKind, pos, prevPos);
return false;
}
break;
}
+ case DeclarationKind::ModuleBodyLevelFunction: {
+ MOZ_ASSERT(pc->atModuleLevel());
+
+ AddDeclaredNamePtr p = pc->varScope().lookupDeclaredNameForAdd(name);
+ if (p) {
+ reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos());
+ return false;
+ }
+
+ if (!pc->varScope().addDeclaredName(pc, p, name, kind, pos.begin))
+ return false;
+
+ // Body-level functions in modules are always closed over.
+ pc->varScope().lookupDeclaredName(name)->value()->setClosedOver();
+
+ break;
+ }
+
case DeclarationKind::FormalParameter: {
// It is an early error if any non-positional formal parameter name
// (e.g., destructuring formal parameter) is duplicated.
@@ -1277,7 +1462,7 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind
return false;
}
- if (!pc->functionScope().addDeclaredName(pc, p, name, kind))
+ if (!pc->functionScope().addDeclaredName(pc, p, name, kind, pos.begin))
return false;
break;
@@ -1300,7 +1485,7 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind
(p->value()->kind() != DeclarationKind::LexicalFunction &&
p->value()->kind() != DeclarationKind::VarForAnnexBLexicalFunction))
{
- reportRedeclaration(name, p->value()->kind(), pos);
+ reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos());
return false;
}
@@ -1308,7 +1493,7 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind
// declaration that shadows the VarForAnnexBLexicalFunction.
p->value()->alterKind(kind);
} else {
- if (!scope->addDeclaredName(pc, p, name, kind))
+ if (!scope->addDeclaredName(pc, p, name, kind, pos.begin))
return false;
}
@@ -1348,7 +1533,7 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind
if (pc->isFunctionExtraBodyVarScopeInnermost()) {
DeclaredNamePtr p = pc->functionScope().lookupDeclaredName(name);
if (p && DeclarationKindIsParameter(p->value()->kind())) {
- reportRedeclaration(name, p->value()->kind(), pos);
+ reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos());
return false;
}
}
@@ -1365,12 +1550,12 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind
p = scope->lookupDeclaredNameForAdd(name);
MOZ_ASSERT(!p);
} else {
- reportRedeclaration(name, p->value()->kind(), pos);
+ reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos());
return false;
}
}
- if (!p && !scope->addDeclaredName(pc, p, name, kind))
+ if (!p && !scope->addDeclaredName(pc, p, name, kind, pos.begin))
return false;
break;
@@ -1742,11 +1927,8 @@ Parser<FullParseHandler>::newFunctionScopeData(ParseContext::Scope& scope, bool
case BindingKind::Var:
// The only vars in the function scope when there are parameter
// exprs, which induces a separate var environment, should be the
- // special internal bindings.
- MOZ_ASSERT_IF(hasParameterExprs,
- bi.name() == context->names().arguments ||
- bi.name() == context->names().dotThis ||
- bi.name() == context->names().dotGenerator);
+ // special bindings.
+ MOZ_ASSERT_IF(hasParameterExprs, FunctionScope::isSpecialName(context, bi.name()));
if (!vars.append(binding))
return Nothing();
break;
@@ -2031,7 +2213,7 @@ Parser<FullParseHandler>::moduleBody(ModuleSharedContext* modulesc)
if (!mn)
return null();
- AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, true);
+ AutoAwaitIsKeyword<FullParseHandler> awaitIsKeyword(this, true);
ParseNode* pn = statementList(YieldIsKeyword);
if (!pn)
return null();
@@ -2123,8 +2305,11 @@ Parser<ParseHandler>::declareFunctionThis()
ParseContext::Scope& funScope = pc->functionScope();
AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotThis);
MOZ_ASSERT(!p);
- if (!funScope.addDeclaredName(pc, p, dotThis, DeclarationKind::Var))
+ if (!funScope.addDeclaredName(pc, p, dotThis, DeclarationKind::Var,
+ DeclaredNameInfo::npos))
+ {
return false;
+ }
funbox->setHasThisBinding();
}
@@ -2166,14 +2351,17 @@ Parser<ParseHandler>::declareDotGeneratorName()
ParseContext::Scope& funScope = pc->functionScope();
HandlePropertyName dotGenerator = context->names().dotGenerator;
AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotGenerator);
- if (!p && !funScope.addDeclaredName(pc, p, dotGenerator, DeclarationKind::Var))
+ if (!p && !funScope.addDeclaredName(pc, p, dotGenerator, DeclarationKind::Var,
+ DeclaredNameInfo::npos))
+ {
return false;
+ }
return true;
}
template <typename ParseHandler>
bool
-Parser<ParseHandler>::finishFunctionScopes()
+Parser<ParseHandler>::finishFunctionScopes(bool isStandaloneFunction)
{
FunctionBox* funbox = pc->functionBox();
@@ -2182,7 +2370,7 @@ Parser<ParseHandler>::finishFunctionScopes()
return false;
}
- if (funbox->function()->isNamedLambda()) {
+ if (funbox->function()->isNamedLambda() && !isStandaloneFunction) {
if (!propagateFreeNamesAndMarkClosedOverBindings(pc->namedLambdaScope()))
return false;
}
@@ -2192,9 +2380,9 @@ Parser<ParseHandler>::finishFunctionScopes()
template <>
bool
-Parser<FullParseHandler>::finishFunction()
+Parser<FullParseHandler>::finishFunction(bool isStandaloneFunction /* = false */)
{
- if (!finishFunctionScopes())
+ if (!finishFunctionScopes(isStandaloneFunction))
return false;
FunctionBox* funbox = pc->functionBox();
@@ -2215,7 +2403,7 @@ Parser<FullParseHandler>::finishFunction()
funbox->functionScopeBindings().set(*bindings);
}
- if (funbox->function()->isNamedLambda()) {
+ if (funbox->function()->isNamedLambda() && !isStandaloneFunction) {
Maybe<LexicalScope::Data*> bindings = newLexicalScopeData(pc->namedLambdaScope());
if (!bindings)
return false;
@@ -2227,14 +2415,14 @@ Parser<FullParseHandler>::finishFunction()
template <>
bool
-Parser<SyntaxParseHandler>::finishFunction()
+Parser<SyntaxParseHandler>::finishFunction(bool isStandaloneFunction /* = false */)
{
// The LazyScript for a lazily parsed function needs to know its set of
// free variables and inner functions so that when it is fully parsed, we
// can skip over any already syntax parsed inner functions and still
// retain correct scope information.
- if (!finishFunctionScopes())
+ if (!finishFunctionScopes(isStandaloneFunction))
return false;
// There are too many bindings or inner functions to be saved into the
@@ -2251,7 +2439,7 @@ Parser<SyntaxParseHandler>::finishFunction()
LazyScript* lazy = LazyScript::Create(context, fun, pc->closedOverBindingsForLazy(),
pc->innerFunctionsForLazy, versionNumber(),
funbox->bufStart, funbox->bufEnd,
- funbox->preludeStart,
+ funbox->toStringStart,
funbox->startLine, funbox->startColumn);
if (!lazy)
return false;
@@ -2264,6 +2452,8 @@ Parser<SyntaxParseHandler>::finishFunction()
lazy->setAsyncKind(funbox->asyncKind());
if (funbox->hasRest())
lazy->setHasRest();
+ if (funbox->isExprBody())
+ lazy->setIsExprBody();
if (funbox->isLikelyConstructorWrapper())
lazy->setLikelyConstructorWrapper();
if (funbox->isDerivedClassConstructor())
@@ -2340,7 +2530,7 @@ Parser<FullParseHandler>::standaloneFunction(HandleFunction fun,
return null();
fn->pn_body = argsbody;
- FunctionBox* funbox = newFunctionBox(fn, fun, /* preludeStart = */ 0, inheritedDirectives,
+ FunctionBox* funbox = newFunctionBox(fn, fun, /* toStringStart = */ 0, inheritedDirectives,
generatorKind, asyncKind, /* tryAnnexB = */ false);
if (!funbox)
return null();
@@ -2352,9 +2542,9 @@ Parser<FullParseHandler>::standaloneFunction(HandleFunction fun,
funpc.setIsStandaloneFunctionBody();
YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
- AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, asyncKind == AsyncFunction);
+ AutoAwaitIsKeyword<FullParseHandler> awaitIsKeyword(this, asyncKind == AsyncFunction);
if (!functionFormalParametersAndBody(InAllowed, yieldHandling, fn, Statement,
- parameterListEnd))
+ parameterListEnd, /* isStandaloneFunction = */ true))
{
return null();
}
@@ -2414,8 +2604,11 @@ Parser<ParseHandler>::declareFunctionArgumentsObject()
if (tryDeclareArguments) {
AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(argumentsName);
if (!p) {
- if (!funScope.addDeclaredName(pc, p, argumentsName, DeclarationKind::Var))
+ if (!funScope.addDeclaredName(pc, p, argumentsName, DeclarationKind::Var,
+ DeclaredNameInfo::npos))
+ {
return false;
+ }
funbox->declaredArguments = true;
funbox->usesArguments = true;
} else if (hasExtraBodyVarScope) {
@@ -2631,39 +2824,59 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
/*
* WARNING: Do not call this function directly.
- * Call either MatchOrInsertSemicolonAfterExpression or
- * MatchOrInsertSemicolonAfterNonExpression instead, depending on context.
+ * Call either matchOrInsertSemicolonAfterExpression or
+ * matchOrInsertSemicolonAfterNonExpression instead, depending on context.
*/
-static bool
-MatchOrInsertSemicolonHelper(TokenStream& ts, TokenStream::Modifier modifier)
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::matchOrInsertSemicolonHelper(TokenStream::Modifier modifier)
{
TokenKind tt = TOK_EOF;
- if (!ts.peekTokenSameLine(&tt, modifier))
+ if (!tokenStream.peekTokenSameLine(&tt, modifier))
return false;
if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
+ /*
+ * When current token is `await` and it's outside of async function,
+ * it's possibly intended to be an await expression.
+ *
+ * await f();
+ * ^
+ * |
+ * tried to insert semicolon here
+ *
+ * Detect this situation and throw an understandable error. Otherwise
+ * we'd throw a confusing "missing ; before statement" error.
+ */
+ if (!pc->isAsync() && tokenStream.currentToken().type == TOK_AWAIT) {
+ error(JSMSG_AWAIT_OUTSIDE_ASYNC);
+ return false;
+ }
+
/* Advance the scanner for proper error location reporting. */
- ts.consumeKnownToken(tt, modifier);
- ts.reportError(JSMSG_SEMI_BEFORE_STMNT);
+ tokenStream.consumeKnownToken(tt, modifier);
+ error(JSMSG_SEMI_BEFORE_STMNT);
return false;
}
bool matched;
- if (!ts.matchToken(&matched, TOK_SEMI, modifier))
+ if (!tokenStream.matchToken(&matched, TOK_SEMI, modifier))
return false;
if (!matched && modifier == TokenStream::None)
- ts.addModifierException(TokenStream::OperandIsNone);
+ tokenStream.addModifierException(TokenStream::OperandIsNone);
return true;
}
-static bool
-MatchOrInsertSemicolonAfterExpression(TokenStream& ts)
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::matchOrInsertSemicolonAfterExpression()
{
- return MatchOrInsertSemicolonHelper(ts, TokenStream::None);
+ return matchOrInsertSemicolonHelper(TokenStream::None);
}
-static bool
-MatchOrInsertSemicolonAfterNonExpression(TokenStream& ts)
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::matchOrInsertSemicolonAfterNonExpression()
{
- return MatchOrInsertSemicolonHelper(ts, TokenStream::Operand);
+ return matchOrInsertSemicolonHelper(TokenStream::Operand);
}
template <typename ParseHandler>
@@ -2757,7 +2970,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
firstTokenModifier = funbox->isAsync() ? TokenStream::None : TokenStream::Operand;
if (!tokenStream.peekToken(&tt, firstTokenModifier))
return false;
- if (tt == TOK_NAME || tt == TOK_YIELD) {
+ if (TokenKindIsPossibleIdentifier(tt)) {
parenFreeArrow = true;
argModifier = firstTokenModifier;
}
@@ -2813,7 +3026,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
if (!tokenStream.getToken(&tt, argModifier))
return false;
argModifier = TokenStream::Operand;
- MOZ_ASSERT_IF(parenFreeArrow, tt == TOK_NAME || tt == TOK_YIELD);
+ MOZ_ASSERT_IF(parenFreeArrow, TokenKindIsPossibleIdentifier(tt));
if (tt == TOK_TRIPLEDOT) {
if (IsSetterKind(kind)) {
@@ -2834,7 +3047,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
if (!tokenStream.getToken(&tt))
return false;
- if (tt != TOK_NAME && tt != TOK_YIELD && tt != TOK_LB && tt != TOK_LC) {
+ if (!TokenKindIsPossibleIdentifier(tt) && tt != TOK_LB && tt != TOK_LC) {
error(JSMSG_NO_REST_NAME);
return false;
}
@@ -2864,26 +3077,21 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
break;
}
- case TOK_NAME:
- case TOK_YIELD: {
- if (parenFreeArrow)
- funbox->setStart(tokenStream);
-
- if (funbox->isAsync() && tokenStream.currentName() == context->names().await) {
- // `await` is already gotten as TOK_NAME for the following
- // case:
- //
- // async await => 1
- error(JSMSG_RESERVED_ID, "await");
+ default: {
+ if (!TokenKindIsPossibleIdentifier(tt)) {
+ error(JSMSG_MISSING_FORMAL);
return false;
}
+ if (parenFreeArrow)
+ funbox->setStart(tokenStream);
+
RootedPropertyName name(context, bindingIdentifier(yieldHandling));
if (!name)
return false;
- if (!notePositionalFormalParameter(funcpn, name, disallowDuplicateParams,
- &duplicatedParam))
+ if (!notePositionalFormalParameter(funcpn, name, pos().begin,
+ disallowDuplicateParams, &duplicatedParam))
{
return false;
}
@@ -2892,10 +3100,6 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
break;
}
-
- default:
- error(JSMSG_MISSING_FORMAL);
- return false;
}
if (positionalFormals.length() >= ARGNO_LIMIT) {
@@ -2989,7 +3193,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
template <>
bool
-Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, uint32_t preludeStart,
+Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, uint32_t toStringStart,
FunctionSyntaxKind kind, bool tryAnnexB)
{
// When a lazily-parsed function is called, we only fully parse (and emit)
@@ -2999,7 +3203,7 @@ Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, uint32_t preludeS
RootedFunction fun(context, handler.nextLazyInnerFunction());
MOZ_ASSERT(!fun->isLegacyGenerator());
- FunctionBox* funbox = newFunctionBox(pn, fun, preludeStart, Directives(/* strict = */ false),
+ FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, Directives(/* strict = */ false),
fun->generatorKind(), fun->asyncKind(), tryAnnexB);
if (!funbox)
return false;
@@ -3007,6 +3211,8 @@ Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, uint32_t preludeS
LazyScript* lazy = fun->lazyScript();
if (lazy->needsHomeObject())
funbox->setNeedsHomeObject();
+ if (lazy->isExprBody())
+ funbox->setIsExprBody();
PropagateTransitiveParseFlags(lazy, pc->sc());
@@ -3019,17 +3225,22 @@ Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, uint32_t preludeS
if (!tokenStream.advance(fun->lazyScript()->end() - userbufBase))
return false;
- if (kind == Statement && fun->isExprBody()) {
- if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+#if JS_HAS_EXPR_CLOSURES
+ // Only expression closure can be Statement kind.
+ // If we remove expression closure, we can remove isExprBody flag from
+ // LazyScript and JSScript.
+ if (kind == Statement && funbox->isExprBody()) {
+ if (!matchOrInsertSemicolonAfterExpression())
return false;
}
+#endif
return true;
}
template <>
bool
-Parser<SyntaxParseHandler>::skipLazyInnerFunction(Node pn, uint32_t preludeStart,
+Parser<SyntaxParseHandler>::skipLazyInnerFunction(Node pn, uint32_t toStringStart,
FunctionSyntaxKind kind, bool tryAnnexB)
{
MOZ_CRASH("Cannot skip lazy inner functions when syntax parsing");
@@ -3082,7 +3293,7 @@ template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::templateLiteral(YieldHandling yieldHandling)
{
- Node pn = noSubstitutionTemplate();
+ Node pn = noSubstitutionUntaggedTemplate();
if (!pn)
return null();
@@ -3095,7 +3306,7 @@ Parser<ParseHandler>::templateLiteral(YieldHandling yieldHandling)
if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt))
return null();
- pn = noSubstitutionTemplate();
+ pn = noSubstitutionUntaggedTemplate();
if (!pn)
return null();
@@ -3106,7 +3317,7 @@ Parser<ParseHandler>::templateLiteral(YieldHandling yieldHandling)
template <typename ParseHandler>
typename ParseHandler::Node
-Parser<ParseHandler>::functionDefinition(uint32_t preludeStart, Node pn, InHandling inHandling,
+Parser<ParseHandler>::functionDefinition(uint32_t toStringStart, Node pn, InHandling inHandling,
YieldHandling yieldHandling,
HandleAtom funName, FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
@@ -3119,7 +3330,7 @@ Parser<ParseHandler>::functionDefinition(uint32_t preludeStart, Node pn, InHandl
// functions, which are also lazy. Instead, their free variables and
// source extents are recorded and may be skipped.
if (handler.canSkipLazyInnerFunctions()) {
- if (!skipLazyInnerFunction(pn, preludeStart, kind, tryAnnexB))
+ if (!skipLazyInnerFunction(pn, toStringStart, kind, tryAnnexB))
return null();
return pn;
}
@@ -3152,7 +3363,7 @@ Parser<ParseHandler>::functionDefinition(uint32_t preludeStart, Node pn, InHandl
// reparse a function due to failed syntax parsing and encountering new
// "use foo" directives.
while (true) {
- if (trySyntaxParseInnerFunction(pn, fun, preludeStart, inHandling, yieldHandling, kind,
+ if (trySyntaxParseInnerFunction(pn, fun, toStringStart, inHandling, yieldHandling, kind,
generatorKind, asyncKind, tryAnnexB, directives,
&newDirectives))
{
@@ -3181,7 +3392,7 @@ Parser<ParseHandler>::functionDefinition(uint32_t preludeStart, Node pn, InHandl
template <>
bool
Parser<FullParseHandler>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunction fun,
- uint32_t preludeStart,
+ uint32_t toStringStart,
InHandling inHandling,
YieldHandling yieldHandling,
FunctionSyntaxKind kind,
@@ -3215,13 +3426,13 @@ Parser<FullParseHandler>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunct
// Make a FunctionBox before we enter the syntax parser, because |pn|
// still expects a FunctionBox to be attached to it during BCE, and
// the syntax parser cannot attach one to it.
- FunctionBox* funbox = newFunctionBox(pn, fun, preludeStart, inheritedDirectives,
+ FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, inheritedDirectives,
generatorKind, asyncKind, tryAnnexB);
if (!funbox)
return false;
funbox->initWithEnclosingParseContext(pc, kind);
- if (!parser->innerFunction(SyntaxParseHandler::NodeGeneric, pc, funbox, preludeStart,
+ if (!parser->innerFunction(SyntaxParseHandler::NodeGeneric, pc, funbox, toStringStart,
inHandling, yieldHandling, kind,
inheritedDirectives, newDirectives))
{
@@ -3249,14 +3460,14 @@ Parser<FullParseHandler>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunct
} while (false);
// We failed to do a syntax parse above, so do the full parse.
- return innerFunction(pn, pc, fun, preludeStart, inHandling, yieldHandling, kind,
+ return innerFunction(pn, pc, fun, toStringStart, inHandling, yieldHandling, kind,
generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives);
}
template <>
bool
Parser<SyntaxParseHandler>::trySyntaxParseInnerFunction(Node pn, HandleFunction fun,
- uint32_t preludeStart,
+ uint32_t toStringStart,
InHandling inHandling,
YieldHandling yieldHandling,
FunctionSyntaxKind kind,
@@ -3267,14 +3478,14 @@ Parser<SyntaxParseHandler>::trySyntaxParseInnerFunction(Node pn, HandleFunction
Directives* newDirectives)
{
// This is already a syntax parser, so just parse the inner function.
- return innerFunction(pn, pc, fun, preludeStart, inHandling, yieldHandling, kind,
+ return innerFunction(pn, pc, fun, toStringStart, inHandling, yieldHandling, kind,
generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives);
}
template <typename ParseHandler>
bool
Parser<ParseHandler>::innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox,
- uint32_t preludeStart,
+ uint32_t toStringStart,
InHandling inHandling, YieldHandling yieldHandling,
FunctionSyntaxKind kind, Directives inheritedDirectives,
Directives* newDirectives)
@@ -3298,7 +3509,7 @@ Parser<ParseHandler>::innerFunction(Node pn, ParseContext* outerpc, FunctionBox*
template <typename ParseHandler>
bool
Parser<ParseHandler>::innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun,
- uint32_t preludeStart,
+ uint32_t toStringStart,
InHandling inHandling, YieldHandling yieldHandling,
FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
@@ -3310,13 +3521,13 @@ Parser<ParseHandler>::innerFunction(Node pn, ParseContext* outerpc, HandleFuncti
// parser. In that case, outerpc is a ParseContext from the full parser
// instead of the current top of the stack of the syntax parser.
- FunctionBox* funbox = newFunctionBox(pn, fun, preludeStart, inheritedDirectives,
+ FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, inheritedDirectives,
generatorKind, asyncKind, tryAnnexB);
if (!funbox)
return false;
funbox->initWithEnclosingParseContext(outerpc, kind);
- return innerFunction(pn, outerpc, funbox, preludeStart, inHandling, yieldHandling, kind,
+ return innerFunction(pn, outerpc, funbox, toStringStart, inHandling, yieldHandling, kind,
inheritedDirectives, newDirectives);
}
@@ -3324,7 +3535,7 @@ template <typename ParseHandler>
bool
Parser<ParseHandler>::appendToCallSiteObj(Node callSiteObj)
{
- Node cookedNode = noSubstitutionTemplate();
+ Node cookedNode = noSubstitutionTaggedTemplate();
if (!cookedNode)
return false;
@@ -3352,7 +3563,7 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict
return null();
Directives directives(strict);
- FunctionBox* funbox = newFunctionBox(pn, fun, /* preludeStart = */ 0, directives,
+ FunctionBox* funbox = newFunctionBox(pn, fun, /* toStringStart = */ 0, directives,
generatorKind, asyncKind, /* tryAnnexB = */ false);
if (!funbox)
return null();
@@ -3402,7 +3613,8 @@ bool
Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
YieldHandling yieldHandling,
Node pn, FunctionSyntaxKind kind,
- Maybe<uint32_t> parameterListEnd /* = Nothing() */)
+ Maybe<uint32_t> parameterListEnd /* = Nothing() */,
+ bool isStandaloneFunction /* = false */)
{
// Given a properly initialized parse context, try to parse an actual
// function without concern for conversion to strict mode, use of lazy
@@ -3411,9 +3623,14 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
FunctionBox* funbox = pc->functionBox();
RootedFunction fun(context, funbox->function());
- AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, funbox->isAsync());
- if (!functionArguments(yieldHandling, kind, pn))
- return false;
+ // See below for an explanation why arrow function parameters and arrow
+ // function bodies are parsed with different yield/await settings.
+ {
+ bool asyncOrArrowInAsync = funbox->isAsync() || (kind == Arrow && awaitIsKeyword());
+ AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, asyncOrArrowInAsync);
+ if (!functionArguments(yieldHandling, kind, pn))
+ return false;
+ }
Maybe<ParseContext::VarScope> varScope;
if (funbox->hasParameterExprs) {
@@ -3446,6 +3663,7 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
TokenKind tt;
if (!tokenStream.getToken(&tt, TokenStream::Operand))
return false;
+ uint32_t openedPos = 0;
if (tt != TOK_LC) {
if ((funbox->isStarGenerator() && !funbox->isAsync()) || kind == Method ||
kind == GetterNoExpressionClosure || kind == SetterNoExpressionClosure ||
@@ -3466,9 +3684,9 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
tokenStream.ungetToken();
bodyType = ExpressionBody;
-#if JS_HAS_EXPR_CLOSURES
- fun->setIsExprBody();
-#endif
+ funbox->setIsExprBody();
+ } else {
+ openedPos = pos().begin;
}
// Arrow function parameters inherit yieldHandling from the enclosing
@@ -3476,41 +3694,59 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
// |yield| in the parameters is either a name or keyword, depending on
// whether the arrow function is enclosed in a generator function or not.
// Whereas the |yield| in the function body is always parsed as a name.
+ // The same goes when parsing |await| in arrow functions.
YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind(), pc->asyncKind());
- Node body = functionBody(inHandling, bodyYieldHandling, kind, bodyType);
- if (!body)
- return false;
+ Node body;
+ {
+ AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, funbox->isAsync());
+ body = functionBody(inHandling, bodyYieldHandling, kind, bodyType);
+ if (!body)
+ return false;
+ }
- if ((kind != Method && !IsConstructorKind(kind)) && fun->explicitName()) {
+ if ((kind == Statement || kind == Expression) && fun->explicitName()) {
RootedPropertyName propertyName(context, fun->explicitName()->asPropertyName());
- if (!checkStrictBinding(propertyName, handler.getPosition(pn)))
+ YieldHandling nameYieldHandling;
+ if (kind == Expression) {
+ // Named lambda has binding inside it.
+ nameYieldHandling = bodyYieldHandling;
+ } else {
+ // Otherwise YieldHandling cannot be checked at this point
+ // because of different context.
+ // It should already be checked before this point.
+ nameYieldHandling = YieldIsName;
+ }
+
+ // We already use the correct await-handling at this point, therefore
+ // we don't need call AutoAwaitIsKeyword here.
+
+ if (!checkBindingIdentifier(propertyName, handler.getPosition(pn).begin,
+ nameYieldHandling))
+ {
return false;
+ }
}
if (bodyType == StatementListBody) {
- bool matched;
- if (!tokenStream.matchToken(&matched, TOK_RC, TokenStream::Operand))
- return false;
- if (!matched) {
- error(JSMSG_CURLY_AFTER_BODY);
- return false;
- }
- funbox->bufEnd = pos().end;
+ MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
+ reportMissingClosing(JSMSG_CURLY_AFTER_BODY,
+ JSMSG_CURLY_OPENED, openedPos));
+ funbox->setEnd(pos().end);
} else {
#if !JS_HAS_EXPR_CLOSURES
MOZ_ASSERT(kind == Arrow);
#endif
if (tokenStream.hadError())
return false;
- funbox->bufEnd = pos().end;
- if (kind == Statement && !MatchOrInsertSemicolonAfterExpression(tokenStream))
+ funbox->setEnd(pos().end);
+ if (kind == Statement && !matchOrInsertSemicolonAfterExpression())
return false;
}
if (IsMethodDefinitionKind(kind) && pc->superScopeNeedsHomeObject())
funbox->setNeedsHomeObject();
- if (!finishFunction())
+ if (!finishFunction(isStandaloneFunction))
return false;
handler.setEndPosition(body, pos().begin);
@@ -3522,26 +3758,11 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
template <typename ParseHandler>
typename ParseHandler::Node
-Parser<ParseHandler>::functionStmt(uint32_t preludeStart, YieldHandling yieldHandling,
+Parser<ParseHandler>::functionStmt(uint32_t toStringStart, YieldHandling yieldHandling,
DefaultHandling defaultHandling, FunctionAsyncKind asyncKind)
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
- // Annex B.3.4 says we can parse function declarations unbraced under if
- // or else as if it were braced. That is, |if (x) function f() {}| is
- // parsed as |if (x) { function f() {} }|.
- Maybe<ParseContext::Statement> synthesizedStmtForAnnexB;
- Maybe<ParseContext::Scope> synthesizedScopeForAnnexB;
- if (!pc->sc()->strict()) {
- ParseContext::Statement* stmt = pc->innermostStatement();
- if (stmt && stmt->kind() == StatementKind::If) {
- synthesizedStmtForAnnexB.emplace(pc, StatementKind::Block);
- synthesizedScopeForAnnexB.emplace(this);
- if (!synthesizedScopeForAnnexB->init(pc))
- return null();
- }
- }
-
// In sloppy mode, Annex B.3.2 allows labelled function declarations.
// Otherwise it's a parse error.
ParseContext::Statement* declaredInStmt = pc->innermostStatement();
@@ -3577,7 +3798,7 @@ Parser<ParseHandler>::functionStmt(uint32_t preludeStart, YieldHandling yieldHan
}
RootedPropertyName name(context);
- if (tt == TOK_NAME || tt == TOK_YIELD) {
+ if (TokenKindIsPossibleIdentifier(tt)) {
name = bindingIdentifier(yieldHandling);
if (!name)
return null();
@@ -3602,19 +3823,18 @@ Parser<ParseHandler>::functionStmt(uint32_t preludeStart, YieldHandling yieldHan
// early error, do so. This 'var' binding would be assigned
// the function object when its declaration is reached, not at
// the start of the block.
- if (!tryDeclareVarForAnnexBLexicalFunction(name, &tryAnnexB))
+ if (!tryDeclareVarForAnnexBLexicalFunction(name, pos().begin, &tryAnnexB))
return null();
}
if (!noteDeclaredName(name, DeclarationKind::LexicalFunction, pos()))
return null();
} else {
- if (!noteDeclaredName(name, DeclarationKind::BodyLevelFunction, pos()))
+ DeclarationKind kind = pc->atModuleLevel()
+ ? DeclarationKind::ModuleBodyLevelFunction
+ : DeclarationKind::BodyLevelFunction;
+ if (!noteDeclaredName(name, kind, pos()))
return null();
-
- // Body-level functions in modules are always closed over.
- if (pc->atModuleLevel())
- pc->varScope().lookupDeclaredName(name)->value()->setClosedOver();
}
Node pn = handler.newFunctionStatement();
@@ -3622,30 +3842,18 @@ Parser<ParseHandler>::functionStmt(uint32_t preludeStart, YieldHandling yieldHan
return null();
YieldHandling newYieldHandling = GetYieldHandling(generatorKind, asyncKind);
- Node fun = functionDefinition(preludeStart, pn, InAllowed, newYieldHandling,
+ return functionDefinition(toStringStart, pn, InAllowed, newYieldHandling,
name, Statement, generatorKind, asyncKind, tryAnnexB);
- if (!fun)
- return null();
-
- if (synthesizedStmtForAnnexB) {
- Node synthesizedStmtList = handler.newStatementList(handler.getPosition(fun));
- if (!synthesizedStmtList)
- return null();
- handler.addStatementToList(synthesizedStmtList, fun);
- return finishLexicalScope(*synthesizedScopeForAnnexB, synthesizedStmtList);
- }
-
- return fun;
}
template <typename ParseHandler>
typename ParseHandler::Node
-Parser<ParseHandler>::functionExpr(uint32_t preludeStart, InvokedPrediction invoked,
+Parser<ParseHandler>::functionExpr(uint32_t toStringStart, InvokedPrediction invoked,
FunctionAsyncKind asyncKind)
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
- AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, asyncKind == AsyncFunction);
+ AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, asyncKind == AsyncFunction);
GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator;
TokenKind tt;
if (!tokenStream.getToken(&tt))
@@ -3664,7 +3872,7 @@ Parser<ParseHandler>::functionExpr(uint32_t preludeStart, InvokedPrediction invo
YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
RootedPropertyName name(context);
- if (tt == TOK_NAME || tt == TOK_YIELD) {
+ if (TokenKindIsPossibleIdentifier(tt)) {
name = bindingIdentifier(yieldHandling);
if (!name)
return null();
@@ -3679,7 +3887,7 @@ Parser<ParseHandler>::functionExpr(uint32_t preludeStart, InvokedPrediction invo
if (invoked)
pn = handler.setLikelyIIFE(pn);
- return functionDefinition(preludeStart, pn, InAllowed, yieldHandling, name, Expression,
+ return functionDefinition(toStringStart, pn, InAllowed, yieldHandling, name, Expression,
generatorKind, asyncKind);
}
@@ -3701,18 +3909,6 @@ IsEscapeFreeStringLiteral(const TokenPos& pos, JSAtom* str)
return pos.begin + str->length() + 2 == pos.end;
}
-template <typename ParseHandler>
-bool
-Parser<ParseHandler>::checkUnescapedName()
-{
- const Token& token = tokenStream.currentToken();
- if (!token.nameContainsEscape())
- return true;
-
- errorAt(token.pos.begin, JSMSG_ESCAPED_KEYWORD);
- return false;
-}
-
template <>
bool
Parser<SyntaxParseHandler>::asmJS(Node list)
@@ -3931,7 +4127,7 @@ Parser<ParseHandler>::matchLabel(YieldHandling yieldHandling, MutableHandle<Prop
if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
return false;
- if (tt == TOK_NAME || tt == TOK_YIELD) {
+ if (TokenKindIsPossibleIdentifier(tt)) {
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
label.set(labelIdentifier(yieldHandling));
@@ -3954,8 +4150,17 @@ Parser<ParseHandler>::PossibleError::error(ErrorKind kind)
{
if (kind == ErrorKind::Expression)
return exprError_;
- MOZ_ASSERT(kind == ErrorKind::Destructuring);
- return destructuringError_;
+ if (kind == ErrorKind::Destructuring)
+ return destructuringError_;
+ MOZ_ASSERT(kind == ErrorKind::DestructuringWarning);
+ return destructuringWarning_;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::PossibleError::hasPendingDestructuringError()
+{
+ return hasError(ErrorKind::Destructuring);
}
template <typename ParseHandler>
@@ -3999,6 +4204,14 @@ Parser<ParseHandler>::PossibleError::setPendingDestructuringErrorAt(const TokenP
template <typename ParseHandler>
void
+Parser<ParseHandler>::PossibleError::setPendingDestructuringWarningAt(const TokenPos& pos,
+ unsigned errorNumber)
+{
+ setPending(ErrorKind::DestructuringWarning, pos, errorNumber);
+}
+
+template <typename ParseHandler>
+void
Parser<ParseHandler>::PossibleError::setPendingExpressionErrorAt(const TokenPos& pos,
unsigned errorNumber)
{
@@ -4019,23 +4232,36 @@ Parser<ParseHandler>::PossibleError::checkForError(ErrorKind kind)
template <typename ParseHandler>
bool
-Parser<ParseHandler>::PossibleError::checkForDestructuringError()
+Parser<ParseHandler>::PossibleError::checkForWarning(ErrorKind kind)
+{
+ if (!hasError(kind))
+ return true;
+
+ Error& err = error(kind);
+ return parser_.extraWarningAt(err.offset_, err.errorNumber_);
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::PossibleError::checkForDestructuringErrorOrWarning()
{
// Clear pending expression error, because we're definitely not in an
// expression context.
setResolved(ErrorKind::Expression);
- // Report any pending destructuring error.
- return checkForError(ErrorKind::Destructuring);
+ // Report any pending destructuring error or warning.
+ return checkForError(ErrorKind::Destructuring) &&
+ checkForWarning(ErrorKind::DestructuringWarning);
}
template <typename ParseHandler>
bool
Parser<ParseHandler>::PossibleError::checkForExpressionError()
{
- // Clear pending destructuring error, because we're definitely not in a
- // destructuring context.
+ // Clear pending destructuring error or warning, because we're definitely
+ // not in a destructuring context.
setResolved(ErrorKind::Destructuring);
+ setResolved(ErrorKind::DestructuringWarning);
// Report any pending expression error.
return checkForError(ErrorKind::Expression);
@@ -4067,191 +4293,272 @@ Parser<ParseHandler>::PossibleError::transferErrorsTo(PossibleError* other)
transferErrorTo(ErrorKind::Expression, other);
}
-template <>
-bool
-Parser<FullParseHandler>::checkDestructuringName(ParseNode* expr, Maybe<DeclarationKind> maybeDecl)
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::bindingInitializer(Node lhs, DeclarationKind kind,
+ YieldHandling yieldHandling)
{
- MOZ_ASSERT(!handler.isUnparenthesizedDestructuringPattern(expr));
-
- // Parentheses are forbidden around destructuring *patterns* (but allowed
- // around names). Use our nicer error message for parenthesized, nested
- // patterns.
- if (handler.isParenthesizedDestructuringPattern(expr)) {
- errorAt(expr->pn_pos.begin, JSMSG_BAD_DESTRUCT_PARENS);
- return false;
- }
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_ASSIGN));
- // This expression might be in a variable-binding pattern where only plain,
- // unparenthesized names are permitted.
- if (maybeDecl) {
- // Destructuring patterns in declarations must only contain
- // unparenthesized names.
- if (!handler.isUnparenthesizedName(expr)) {
- errorAt(expr->pn_pos.begin, JSMSG_NO_VARIABLE_NAME);
- return false;
- }
+ if (kind == DeclarationKind::FormalParameter)
+ pc->functionBox()->hasParameterExprs = true;
- RootedPropertyName name(context, expr->name());
- return noteDeclaredName(name, *maybeDecl, expr->pn_pos);
- }
+ Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
+ if (!rhs)
+ return null();
- // Otherwise this is an expression in destructuring outside a declaration.
- if (handler.isNameAnyParentheses(expr)) {
- if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(expr, context)) {
- if (!strictModeErrorAt(expr->pn_pos.begin, JSMSG_BAD_STRICT_ASSIGN, chars))
- return false;
- }
+ handler.checkAndSetIsDirectRHSAnonFunction(rhs);
- return true;
- }
+ Node assign = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP);
+ if (!assign)
+ return null();
- if (handler.isPropertyAccess(expr))
- return true;
+ if (foldConstants && !FoldConstants(context, &assign, this))
+ return null();
- errorAt(expr->pn_pos.begin, JSMSG_BAD_DESTRUCT_TARGET);
- return false;
+ return assign;
}
-template <>
-bool
-Parser<FullParseHandler>::checkDestructuringPattern(ParseNode* pattern,
- Maybe<DeclarationKind> maybeDecl,
- PossibleError* possibleError /* = nullptr */);
-
-template <>
-bool
-Parser<FullParseHandler>::checkDestructuringObject(ParseNode* objectPattern,
- Maybe<DeclarationKind> maybeDecl)
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::bindingIdentifier(DeclarationKind kind, YieldHandling yieldHandling)
{
- MOZ_ASSERT(objectPattern->isKind(PNK_OBJECT));
+ RootedPropertyName name(context, bindingIdentifier(yieldHandling));
+ if (!name)
+ return null();
- for (ParseNode* member = objectPattern->pn_head; member; member = member->pn_next) {
- ParseNode* target;
- if (member->isKind(PNK_MUTATEPROTO)) {
- target = member->pn_kid;
- } else {
- MOZ_ASSERT(member->isKind(PNK_COLON) || member->isKind(PNK_SHORTHAND));
- MOZ_ASSERT_IF(member->isKind(PNK_SHORTHAND),
- member->pn_left->isKind(PNK_OBJECT_PROPERTY_NAME) &&
- member->pn_right->isKind(PNK_NAME) &&
- member->pn_left->pn_atom == member->pn_right->pn_atom);
+ Node binding = newName(name);
+ if (!binding || !noteDeclaredName(name, kind, pos()))
+ return null();
- target = member->pn_right;
- }
- if (handler.isUnparenthesizedAssignment(target))
- target = target->pn_left;
+ return binding;
+}
- if (handler.isUnparenthesizedDestructuringPattern(target)) {
- if (!checkDestructuringPattern(target, maybeDecl))
- return false;
- } else {
- if (!checkDestructuringName(target, maybeDecl))
- return false;
- }
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::bindingIdentifierOrPattern(DeclarationKind kind, YieldHandling yieldHandling,
+ TokenKind tt)
+{
+ if (tt == TOK_LB)
+ return arrayBindingPattern(kind, yieldHandling);
+
+ if (tt == TOK_LC)
+ return objectBindingPattern(kind, yieldHandling);
+
+ if (!TokenKindIsPossibleIdentifierName(tt)) {
+ error(JSMSG_NO_VARIABLE_NAME);
+ return null();
}
- return true;
+ return bindingIdentifier(kind, yieldHandling);
}
-template <>
-bool
-Parser<FullParseHandler>::checkDestructuringArray(ParseNode* arrayPattern,
- Maybe<DeclarationKind> maybeDecl)
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::objectBindingPattern(DeclarationKind kind, YieldHandling yieldHandling)
{
- MOZ_ASSERT(arrayPattern->isKind(PNK_ARRAY));
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
- for (ParseNode* element = arrayPattern->pn_head; element; element = element->pn_next) {
- if (element->isKind(PNK_ELISION))
- continue;
+ JS_CHECK_RECURSION(context, return null());
- ParseNode* target;
- if (element->isKind(PNK_SPREAD)) {
- if (element->pn_next) {
- errorAt(element->pn_next->pn_pos.begin, JSMSG_PARAMETER_AFTER_REST);
- return false;
- }
- target = element->pn_kid;
- } else if (handler.isUnparenthesizedAssignment(element)) {
- target = element->pn_left;
+ uint32_t begin = pos().begin;
+ Node literal = handler.newObjectLiteral(begin);
+ if (!literal)
+ return null();
+
+ Maybe<DeclarationKind> declKind = Some(kind);
+ RootedAtom propAtom(context);
+ for (;;) {
+ TokenKind tt;
+ if (!tokenStream.peekToken(&tt))
+ return null();
+ if (tt == TOK_RC)
+ break;
+
+ if (tt == TOK_TRIPLEDOT) {
+ // rest-binding property
+ tokenStream.consumeKnownToken(TOK_TRIPLEDOT);
+ uint32_t begin = pos().begin;
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt))
+ return null();
+
+ Node inner = bindingIdentifierOrPattern(kind, yieldHandling, tt);
+ if (!inner)
+ return null();
+
+ if (!handler.addSpreadProperty(literal, begin, inner))
+ return null();
} else {
- target = element;
+ TokenPos namePos = tokenStream.nextToken().pos;
+
+ PropertyType propType;
+ Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom);
+ if (!propName)
+ return null();
+ if (propType == PropertyType::Normal) {
+ // Handle e.g., |var {p: x} = o| and |var {p: x=0} = o|.
+
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
+
+ Node binding = bindingIdentifierOrPattern(kind, yieldHandling, tt);
+ if (!binding)
+ return null();
+
+ bool hasInitializer;
+ if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN))
+ return null();
+
+ Node bindingExpr = hasInitializer
+ ? bindingInitializer(binding, kind, yieldHandling)
+ : binding;
+ if (!bindingExpr)
+ return null();
+
+ if (!handler.addPropertyDefinition(literal, propName, bindingExpr))
+ return null();
+ } else if (propType == PropertyType::Shorthand) {
+ // Handle e.g., |var {x, y} = o| as destructuring shorthand
+ // for |var {x: x, y: y} = o|.
+ MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt));
+
+ Node binding = bindingIdentifier(kind, yieldHandling);
+ if (!binding)
+ return null();
+
+ if (!handler.addShorthand(literal, propName, binding))
+ return null();
+ } else if (propType == PropertyType::CoverInitializedName) {
+ // Handle e.g., |var {x=1, y=2} = o| as destructuring
+ // shorthand with default values.
+ MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt));
+
+ Node binding = bindingIdentifier(kind, yieldHandling);
+ if (!binding)
+ return null();
+
+ tokenStream.consumeKnownToken(TOK_ASSIGN);
+
+ Node bindingExpr = bindingInitializer(binding, kind, yieldHandling);
+ if (!bindingExpr)
+ return null();
+
+ if (!handler.addPropertyDefinition(literal, propName, bindingExpr))
+ return null();
+ } else {
+ errorAt(namePos.begin, JSMSG_NO_VARIABLE_NAME);
+ return null();
+
+ }
}
- if (handler.isUnparenthesizedDestructuringPattern(target)) {
- if (!checkDestructuringPattern(target, maybeDecl))
- return false;
- } else {
- if (!checkDestructuringName(target, maybeDecl))
- return false;
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_COMMA))
+ return null();
+ if (!matched)
+ break;
+ if (tt == TOK_TRIPLEDOT) {
+ error(JSMSG_REST_WITH_COMMA);
+ return null();
}
}
- return true;
+ MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None,
+ reportMissingClosing(JSMSG_CURLY_AFTER_LIST,
+ JSMSG_CURLY_OPENED, begin));
+
+ handler.setEndPosition(literal, pos().end);
+ return literal;
}
-/*
- * Destructuring patterns can appear in two kinds of contexts:
- *
- * - assignment-like: assignment expressions and |for| loop heads. In
- * these cases, the patterns' property value positions can be
- * arbitrary lvalue expressions; the destructuring is just a fancy
- * assignment.
- *
- * - binding-like: |var| and |let| declarations, functions' formal
- * parameter lists, |catch| clauses, and comprehension tails. In
- * these cases, the patterns' property value positions must be
- * simple names; the destructuring defines them as new variables.
- *
- * In both cases, other code parses the pattern as an arbitrary
- * primaryExpr, and then, here in checkDestructuringPattern, verify
- * that the tree is a valid AssignmentPattern or BindingPattern.
- *
- * In assignment-like contexts, we parse the pattern with
- * pc->inDestructuringDecl clear, so the lvalue expressions in the
- * pattern are parsed normally. primaryExpr links variable references
- * into the appropriate use chains; creates placeholder definitions;
- * and so on. checkDestructuringPattern won't bind any new names and
- * we specialize lvalues as appropriate.
- *
- * In declaration-like contexts, the normal variable reference
- * processing would just be an obstruction, because we're going to
- * define the names that appear in the property value positions as new
- * variables anyway. In this case, we parse the pattern with
- * pc->inDestructuringDecl set, which directs primaryExpr to leave
- * whatever name nodes it creates unconnected. Then, here in
- * checkDestructuringPattern, we require the pattern's property value
- * positions to be simple names, and define them as appropriate to the
- * context.
- */
-template <>
-bool
-Parser<FullParseHandler>::checkDestructuringPattern(ParseNode* pattern,
- Maybe<DeclarationKind> maybeDecl,
- PossibleError* possibleError /* = nullptr */)
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling)
{
- if (pattern->isKind(PNK_ARRAYCOMP)) {
- errorAt(pattern->pn_pos.begin, JSMSG_ARRAY_COMP_LEFTSIDE);
- return false;
- }
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
- bool isDestructuring = pattern->isKind(PNK_ARRAY)
- ? checkDestructuringArray(pattern, maybeDecl)
- : checkDestructuringObject(pattern, maybeDecl);
+ JS_CHECK_RECURSION(context, return null());
- // Report any pending destructuring error.
- if (isDestructuring && possibleError && !possibleError->checkForDestructuringError())
- return false;
+ uint32_t begin = pos().begin;
+ Node literal = handler.newArrayLiteral(begin);
+ if (!literal)
+ return null();
- return isDestructuring;
-}
+ uint32_t index = 0;
+ TokenStream::Modifier modifier = TokenStream::Operand;
+ for (; ; index++) {
+ if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
+ error(JSMSG_ARRAY_INIT_TOO_BIG);
+ return null();
+ }
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
+
+ if (tt == TOK_RB) {
+ tokenStream.ungetToken();
+ break;
+ }
+
+ if (tt == TOK_COMMA) {
+ if (!handler.addElision(literal, pos()))
+ return null();
+ } else if (tt == TOK_TRIPLEDOT) {
+ uint32_t begin = pos().begin;
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
+
+ Node inner = bindingIdentifierOrPattern(kind, yieldHandling, tt);
+ if (!inner)
+ return null();
+
+ if (!handler.addSpreadElement(literal, begin, inner))
+ return null();
+ } else {
+ Node binding = bindingIdentifierOrPattern(kind, yieldHandling, tt);
+ if (!binding)
+ return null();
+
+ bool hasInitializer;
+ if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN))
+ return null();
+
+ Node element = hasInitializer
+ ? bindingInitializer(binding, kind, yieldHandling)
+ : binding;
+ if (!element)
+ return null();
+
+ handler.addArrayElement(literal, element);
+ }
+
+ if (tt != TOK_COMMA) {
+ // If we didn't already match TOK_COMMA in above case.
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_COMMA))
+ return null();
+ if (!matched) {
+ modifier = TokenStream::None;
+ break;
+ }
+ if (tt == TOK_TRIPLEDOT) {
+ error(JSMSG_REST_WITH_COMMA);
+ return null();
+ }
+ }
+ }
+
+ MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, modifier,
+ reportMissingClosing(JSMSG_BRACKET_AFTER_LIST,
+ JSMSG_BRACKET_OPENED, begin));
-template <>
-bool
-Parser<SyntaxParseHandler>::checkDestructuringPattern(Node pattern,
- Maybe<DeclarationKind> maybeDecl,
- PossibleError* possibleError /* = nullptr */)
-{
- return abortIfSyntaxParser();
+ handler.setEndPosition(literal, pos().end);
+ return literal;
}
template <typename ParseHandler>
@@ -4262,18 +4569,9 @@ Parser<ParseHandler>::destructuringDeclaration(DeclarationKind kind, YieldHandli
MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
MOZ_ASSERT(tt == TOK_LB || tt == TOK_LC);
- PossibleError possibleError(*this);
- Node pattern;
- {
- pc->inDestructuringDecl = Some(kind);
- pattern = primaryExpr(yieldHandling, TripledotProhibited, tt, &possibleError);
- pc->inDestructuringDecl = Nothing();
- }
-
- if (!pattern || !checkDestructuringPattern(pattern, Some(kind), &possibleError))
- return null();
-
- return pattern;
+ return tt == TOK_LB
+ ? arrayBindingPattern(kind, yieldHandling)
+ : objectBindingPattern(kind, yieldHandling);
}
template <typename ParseHandler>
@@ -4303,6 +4601,7 @@ typename ParseHandler::Node
Parser<ParseHandler>::blockStatement(YieldHandling yieldHandling, unsigned errorNumber)
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
+ uint32_t openedPos = pos().begin;
ParseContext::Statement stmt(pc, StatementKind::Block);
ParseContext::Scope scope(this);
@@ -4313,7 +4612,9 @@ Parser<ParseHandler>::blockStatement(YieldHandling yieldHandling, unsigned error
if (!list)
return null();
- MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, errorNumber);
+ MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
+ reportMissingClosing(errorNumber, JSMSG_CURLY_OPENED,
+ openedPos));
return finishLexicalScope(scope, list);
}
@@ -4384,6 +4685,12 @@ Parser<ParseHandler>::declarationPattern(Node decl, DeclarationKind declKind, To
// binary operator (examined with modifier None) terminated |init|.
// For all other declarations, through ASI's infinite majesty, a next
// token on a new line would begin an expression.
+ // Similar to the case in initializerInNameDeclaration(), we need to
+ // peek at the next token when assignExpr() is a lazily parsed arrow
+ // function.
+ TokenKind ignored;
+ if (!tokenStream.peekToken(&ignored))
+ return null();
tokenStream.addModifierException(TokenStream::OperandIsNone);
}
@@ -4484,7 +4791,8 @@ Parser<ParseHandler>::declarationName(Node decl, DeclarationKind declKind, Token
ParseNodeKind* forHeadKind, Node* forInOrOfExpression)
{
// Anything other than TOK_YIELD or TOK_NAME is an error.
- if (tt != TOK_NAME && tt != TOK_YIELD) {
+ // Anything other than possible identifier is an error.
+ if (!TokenKindIsPossibleIdentifier(tt)) {
error(JSMSG_NO_VARIABLE_NAME);
return null();
}
@@ -4625,8 +4933,10 @@ Parser<ParseHandler>::declarationList(YieldHandling yieldHandling,
template <typename ParseHandler>
typename ParseHandler::Node
-Parser<ParseHandler>::lexicalDeclaration(YieldHandling yieldHandling, bool isConst)
+Parser<ParseHandler>::lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind)
{
+ MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let);
+
/*
* Parse body-level lets without a new block object. ES6 specs
* that an execution environment's initial lexical environment
@@ -4638,8 +4948,9 @@ Parser<ParseHandler>::lexicalDeclaration(YieldHandling yieldHandling, bool isCon
*
* See 8.1.1.1.6 and the note in 13.2.1.
*/
- Node decl = declarationList(yieldHandling, isConst ? PNK_CONST : PNK_LET);
- if (!decl || !MatchOrInsertSemicolonAfterExpression(tokenStream))
+ Node decl = declarationList(yieldHandling,
+ kind == DeclarationKind::Const ? PNK_CONST : PNK_LET);
+ if (!decl || !matchOrInsertSemicolonAfterExpression())
return null();
return decl;
@@ -4650,41 +4961,34 @@ bool
Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet)
{
if (tt == TOK_LC) {
- TokenStream::Modifier modifier = TokenStream::KeywordIsName;
while (true) {
// Handle the forms |import {} from 'a'| and
// |import { ..., } from 'a'| (where ... is non empty), by
// escaping the loop early if the next token is }.
- if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName))
+ if (!tokenStream.getToken(&tt))
return false;
if (tt == TOK_RC)
break;
- // If the next token is a keyword, the previous call to
- // peekToken matched it as a TOK_NAME, and put it in the
- // lookahead buffer, so this call will match keywords as well.
- MUST_MATCH_TOKEN_MOD(TOK_NAME, TokenStream::KeywordIsName, JSMSG_NO_IMPORT_NAME);
+ if (!TokenKindIsPossibleIdentifierName(tt)) {
+ error(JSMSG_NO_IMPORT_NAME);
+ return false;
+ }
+
Rooted<PropertyName*> importName(context, tokenStream.currentName());
TokenPos importNamePos = pos();
- TokenKind maybeAs;
- if (!tokenStream.peekToken(&maybeAs))
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_AS))
return null();
- if (maybeAs == TOK_NAME &&
- tokenStream.nextName() == context->names().as)
- {
- tokenStream.consumeKnownToken(TOK_NAME);
-
- if (!checkUnescapedName())
- return false;
-
+ if (matched) {
TokenKind afterAs;
if (!tokenStream.getToken(&afterAs))
return false;
- if (afterAs != TOK_NAME && afterAs != TOK_YIELD) {
+ if (!TokenKindIsPossibleIdentifierName(afterAs)) {
error(JSMSG_NO_BINDING_NAME);
return false;
}
@@ -4694,10 +4998,7 @@ Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node impor
// by the keyword 'as'.
// See the ImportSpecifier production in ES6 section 15.2.2.
if (IsKeyword(importName)) {
- JSAutoByteString bytes;
- if (!AtomToPrintableString(context, importName, &bytes))
- return false;
- error(JSMSG_AS_AFTER_RESERVED_WORD, bytes.ptr());
+ error(JSMSG_AS_AFTER_RESERVED_WORD, ReservedWordToCharZ(importName));
return false;
}
}
@@ -4722,31 +5023,24 @@ Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node impor
handler.addList(importSpecSet, importSpec);
- bool matched;
- if (!tokenStream.matchToken(&matched, TOK_COMMA))
+ TokenKind next;
+ if (!tokenStream.getToken(&next))
return false;
- if (!matched) {
- modifier = TokenStream::None;
+ if (next == TOK_RC)
break;
+
+ if (next != TOK_COMMA) {
+ error(JSMSG_RC_AFTER_IMPORT_SPEC_LIST);
+ return false;
}
}
-
- MUST_MATCH_TOKEN_MOD(TOK_RC, modifier, JSMSG_RC_AFTER_IMPORT_SPEC_LIST);
} else {
MOZ_ASSERT(tt == TOK_MUL);
- if (!tokenStream.getToken(&tt))
- return false;
- if (tt != TOK_NAME || tokenStream.currentName() != context->names().as) {
- error(JSMSG_AS_AFTER_IMPORT_STAR);
- return false;
- }
-
- if (!checkUnescapedName())
- return false;
+ MUST_MATCH_TOKEN(TOK_AS, JSMSG_AS_AFTER_IMPORT_STAR);
- MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME);
+ MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_BINDING_NAME);
Node importName = newName(context->names().star);
if (!importName)
@@ -4780,14 +5074,6 @@ Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node impor
}
template<>
-bool
-Parser<SyntaxParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet)
-{
- MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
- return false;
-}
-
-template<>
ParseNode*
Parser<FullParseHandler>::importDeclaration()
{
@@ -4807,8 +5093,15 @@ Parser<FullParseHandler>::importDeclaration()
if (!importSpecSet)
return null();
- if (tt == TOK_NAME || tt == TOK_LC || tt == TOK_MUL) {
- if (tt == TOK_NAME) {
+ if (tt == TOK_STRING) {
+ // Handle the form |import 'a'| by leaving the list empty. This is
+ // equivalent to |import {} from 'a'|.
+ importSpecSet->pn_pos.end = importSpecSet->pn_pos.begin;
+ } else {
+ if (tt == TOK_LC || tt == TOK_MUL) {
+ if (!namedImportsOrNamespaceImport(tt, importSpecSet))
+ return null();
+ } else if (TokenKindIsPossibleIdentifierName(tt)) {
// Handle the form |import a from 'b'|, by adding a single import
// specifier to the list, with 'default' as the import name and
// 'a' as the binding name. This is equivalent to
@@ -4851,36 +5144,20 @@ Parser<FullParseHandler>::importDeclaration()
return null();
}
} else {
- if (!namedImportsOrNamespaceImport(tt, importSpecSet))
- return null();
- }
-
- if (!tokenStream.getToken(&tt))
- return null();
-
- if (tt != TOK_NAME || tokenStream.currentName() != context->names().from) {
- error(JSMSG_FROM_AFTER_IMPORT_CLAUSE);
+ error(JSMSG_DECLARATION_AFTER_IMPORT);
return null();
}
- if (!checkUnescapedName())
- return null();
+ MUST_MATCH_TOKEN(TOK_FROM, JSMSG_FROM_AFTER_IMPORT_CLAUSE);
MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
- } else if (tt == TOK_STRING) {
- // Handle the form |import 'a'| by leaving the list empty. This is
- // equivalent to |import {} from 'a'|.
- importSpecSet->pn_pos.end = importSpecSet->pn_pos.begin;
- } else {
- error(JSMSG_DECLARATION_AFTER_IMPORT);
- return null();
}
Node moduleSpec = stringLiteral();
if (!moduleSpec)
return null();
- if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterNonExpression())
return null();
ParseNode* node =
@@ -4947,284 +5224,539 @@ Parser<SyntaxParseHandler>::checkExportedNamesForDeclaration(Node node)
}
template<>
-ParseNode*
-Parser<FullParseHandler>::exportDeclaration()
+bool
+Parser<FullParseHandler>::checkExportedNameForClause(ParseNode* node)
{
- MOZ_ASSERT(tokenStream.currentToken().type == TOK_EXPORT);
+ return checkExportedName(node->pn_atom);
+}
- if (!pc->atModuleLevel()) {
- error(JSMSG_EXPORT_DECL_AT_TOP_LEVEL);
+template<>
+bool
+Parser<SyntaxParseHandler>::checkExportedNameForClause(Node node)
+{
+ MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+ return false;
+}
+
+template<>
+bool
+Parser<FullParseHandler>::checkExportedNameForFunction(ParseNode* node)
+{
+ return checkExportedName(node->pn_funbox->function()->explicitName());
+}
+
+template<>
+bool
+Parser<SyntaxParseHandler>::checkExportedNameForFunction(Node node)
+{
+ MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+ return false;
+}
+
+template<>
+bool
+Parser<FullParseHandler>::checkExportedNameForClass(ParseNode* node)
+{
+ const ClassNode& cls = node->as<ClassNode>();
+ MOZ_ASSERT(cls.names());
+ return checkExportedName(cls.names()->innerBinding()->pn_atom);
+}
+
+template<>
+bool
+Parser<SyntaxParseHandler>::checkExportedNameForClass(Node node)
+{
+ MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+ return false;
+}
+
+template<>
+bool
+Parser<FullParseHandler>::processExport(ParseNode* node)
+{
+ return pc->sc()->asModuleContext()->builder.processExport(node);
+}
+
+template<>
+bool
+Parser<SyntaxParseHandler>::processExport(Node node)
+{
+ MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+ return false;
+}
+
+template<>
+bool
+Parser<FullParseHandler>::processExportFrom(ParseNode* node)
+{
+ return pc->sc()->asModuleContext()->builder.processExportFrom(node);
+}
+
+template<>
+bool
+Parser<SyntaxParseHandler>::processExportFrom(Node node)
+{
+ MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+ return false;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportFrom(uint32_t begin, Node specList)
+{
+ if (!abortIfSyntaxParser())
return null();
- }
- uint32_t begin = pos().begin;
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FROM));
- Node kid;
- TokenKind tt;
- if (!tokenStream.getToken(&tt))
+ if (!abortIfSyntaxParser())
return null();
- switch (tt) {
- case TOK_LC: {
- kid = handler.newList(PNK_EXPORT_SPEC_LIST);
- if (!kid)
- return null();
- while (true) {
- // Handle the forms |export {}| and |export { ..., }| (where ...
- // is non empty), by escaping the loop early if the next token
- // is }.
- if (!tokenStream.peekToken(&tt))
- return null();
- if (tt == TOK_RC)
- break;
+ MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
- MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME);
- Node bindingName = newName(tokenStream.currentName());
- if (!bindingName)
- return null();
+ Node moduleSpec = stringLiteral();
+ if (!moduleSpec)
+ return null();
- bool foundAs;
- if (!tokenStream.matchContextualKeyword(&foundAs, context->names().as))
- return null();
- if (foundAs)
- MUST_MATCH_TOKEN_MOD(TOK_NAME, TokenStream::KeywordIsName, JSMSG_NO_EXPORT_NAME);
+ if (!matchOrInsertSemicolonAfterNonExpression())
+ return null();
- Node exportName = newName(tokenStream.currentName());
- if (!exportName)
- return null();
+ Node node = handler.newExportFromDeclaration(begin, specList, moduleSpec);
+ if (!node)
+ return null();
- if (!checkExportedName(exportName->pn_atom))
- return null();
+ if (!processExportFrom(node))
+ return null();
- Node exportSpec = handler.newBinary(PNK_EXPORT_SPEC, bindingName, exportName);
- if (!exportSpec)
- return null();
+ return node;
+}
- handler.addList(kid, exportSpec);
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportBatch(uint32_t begin)
+{
+ if (!abortIfSyntaxParser())
+ return null();
- bool matched;
- if (!tokenStream.matchToken(&matched, TOK_COMMA))
- return null();
- if (!matched)
- break;
- }
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_MUL));
- MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_EXPORT_SPEC_LIST);
+ Node kid = handler.newList(PNK_EXPORT_SPEC_LIST);
+ if (!kid)
+ return null();
- // Careful! If |from| follows, even on a new line, it must start a
- // FromClause:
- //
- // export { x }
- // from "foo"; // a single ExportDeclaration
- //
- // But if it doesn't, we might have an ASI opportunity in Operand
- // context, so simply matching a contextual keyword won't work:
- //
- // export { x } // ExportDeclaration, terminated by ASI
- // fro\u006D // ExpressionStatement, the name "from"
- //
- // In that case let MatchOrInsertSemicolonAfterNonExpression sort out
- // ASI or any necessary error.
- TokenKind tt;
- if (!tokenStream.getToken(&tt, TokenStream::Operand))
- return null();
+ // Handle the form |export *| by adding a special export batch
+ // specifier to the list.
+ Node exportSpec = handler.newNullary(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos());
+ if (!exportSpec)
+ return null();
- if (tt == TOK_NAME &&
- tokenStream.currentToken().name() == context->names().from &&
- !tokenStream.currentToken().nameContainsEscape())
- {
- MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
+ handler.addList(kid, exportSpec);
- Node moduleSpec = stringLiteral();
- if (!moduleSpec)
- return null();
+ MUST_MATCH_TOKEN(TOK_FROM, JSMSG_FROM_AFTER_EXPORT_STAR);
- if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
- return null();
+ return exportFrom(begin, kid);
+}
- ParseNode* node = handler.newExportFromDeclaration(begin, kid, moduleSpec);
- if (!node || !pc->sc()->asModuleContext()->builder.processExportFrom(node))
- return null();
+template<>
+bool
+Parser<FullParseHandler>::checkLocalExportNames(ParseNode* node)
+{
+ // ES 2017 draft 15.2.3.1.
+ for (ParseNode* next = node->pn_head; next; next = next->pn_next) {
+ ParseNode* name = next->pn_left;
+ MOZ_ASSERT(name->isKind(PNK_NAME));
- return node;
- }
+ RootedPropertyName ident(context, name->pn_atom->asPropertyName());
+ if (!checkLocalExportName(ident, name->pn_pos.begin))
+ return false;
+ }
- tokenStream.ungetToken();
+ return true;
+}
- if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
- return null();
- break;
- }
+template<>
+bool
+Parser<SyntaxParseHandler>::checkLocalExportNames(Node node)
+{
+ MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+ return false;
+}
- case TOK_MUL: {
- kid = handler.newList(PNK_EXPORT_SPEC_LIST);
- if (!kid)
- return null();
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportClause(uint32_t begin)
+{
+ if (!abortIfSyntaxParser())
+ return null();
- // Handle the form |export *| by adding a special export batch
- // specifier to the list.
- Node exportSpec = handler.newNullary(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos());
- if (!exportSpec)
- return null();
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
- handler.addList(kid, exportSpec);
+ Node kid = handler.newList(PNK_EXPORT_SPEC_LIST);
+ if (!kid)
+ return null();
+ TokenKind tt;
+ while (true) {
+ // Handle the forms |export {}| and |export { ..., }| (where ... is non
+ // empty), by escaping the loop early if the next token is }.
if (!tokenStream.getToken(&tt))
return null();
- if (tt != TOK_NAME || tokenStream.currentName() != context->names().from) {
- error(JSMSG_FROM_AFTER_EXPORT_STAR);
+
+ if (tt == TOK_RC)
+ break;
+
+ if (!TokenKindIsPossibleIdentifierName(tt)) {
+ error(JSMSG_NO_BINDING_NAME);
return null();
}
- if (!checkUnescapedName())
+ Node bindingName = newName(tokenStream.currentName());
+ if (!bindingName)
return null();
- MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
-
- Node moduleSpec = stringLiteral();
- if (!moduleSpec)
+ bool foundAs;
+ if (!tokenStream.matchToken(&foundAs, TOK_AS))
return null();
+ if (foundAs)
+ MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_EXPORT_NAME);
- if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ Node exportName = newName(tokenStream.currentName());
+ if (!exportName)
return null();
- ParseNode* node = handler.newExportFromDeclaration(begin, kid, moduleSpec);
- if (!node || !pc->sc()->asModuleContext()->builder.processExportFrom(node))
+ if (!checkExportedNameForClause(exportName))
return null();
- return node;
+ Node exportSpec = handler.newBinary(PNK_EXPORT_SPEC, bindingName, exportName);
+ if (!exportSpec)
+ return null();
- }
+ handler.addList(kid, exportSpec);
- case TOK_FUNCTION:
- kid = functionStmt(pos().begin, YieldIsKeyword, NameRequired);
- if (!kid)
+ TokenKind next;
+ if (!tokenStream.getToken(&next))
return null();
- if (!checkExportedName(kid->pn_funbox->function()->explicitName()))
- return null();
- break;
+ if (next == TOK_RC)
+ break;
- case TOK_CLASS: {
- kid = classDefinition(YieldIsKeyword, ClassStatement, NameRequired);
- if (!kid)
+ if (next != TOK_COMMA) {
+ error(JSMSG_RC_AFTER_EXPORT_SPEC_LIST);
return null();
+ }
+ }
- const ClassNode& cls = kid->as<ClassNode>();
- MOZ_ASSERT(cls.names());
- if (!checkExportedName(cls.names()->innerBinding()->pn_atom))
- return null();
- break;
- }
+ // Careful! If |from| follows, even on a new line, it must start a
+ // FromClause:
+ //
+ // export { x }
+ // from "foo"; // a single ExportDeclaration
+ //
+ // But if it doesn't, we might have an ASI opportunity in Operand context:
+ //
+ // export { x } // ExportDeclaration, terminated by ASI
+ // fro\u006D // ExpressionStatement, the name "from"
+ //
+ // In that case let matchOrInsertSemicolonAfterNonExpression sort out ASI
+ // or any necessary error.
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_FROM, TokenStream::Operand))
+ return null();
- case TOK_VAR:
- kid = declarationList(YieldIsName, PNK_VAR);
- if (!kid)
- return null();
- if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
- return null();
- if (!checkExportedNamesForDeclaration(kid))
- return null();
- break;
+ if (matched)
+ return exportFrom(begin, kid);
- case TOK_DEFAULT: {
- if (!tokenStream.getToken(&tt, TokenStream::Operand))
- return null();
+ if (!matchOrInsertSemicolonAfterNonExpression())
+ return null();
- if (!checkExportedName(context->names().default_))
- return null();
+ if (!checkLocalExportNames(kid))
+ return null();
- ParseNode* nameNode = nullptr;
- switch (tt) {
- case TOK_FUNCTION:
- kid = functionStmt(pos().begin, YieldIsKeyword, AllowDefaultName);
- if (!kid)
- return null();
- break;
- case TOK_CLASS:
- kid = classDefinition(YieldIsKeyword, ClassStatement, AllowDefaultName);
- if (!kid)
- return null();
- break;
- default: {
- if (tt == TOK_NAME && tokenStream.currentName() == context->names().async) {
- TokenKind nextSameLine = TOK_EOF;
- if (!tokenStream.peekTokenSameLine(&nextSameLine))
- return null();
+ Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
+ if (!node)
+ return null();
- if (nextSameLine == TOK_FUNCTION) {
- tokenStream.consumeKnownToken(nextSameLine);
- kid = functionStmt(pos().begin, YieldIsName, AllowDefaultName, AsyncFunction);
- if (!kid)
- return null();
- break;
- }
- }
+ if (!processExport(node))
+ return null();
- tokenStream.ungetToken();
- RootedPropertyName name(context, context->names().starDefaultStar);
- nameNode = newName(name);
- if (!nameNode)
- return null();
- if (!noteDeclaredName(name, DeclarationKind::Const, pos()))
- return null();
- kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
- if (!kid)
- return null();
- if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
- return null();
- break;
- }
- }
+ return node;
+}
- ParseNode* node = handler.newExportDefaultDeclaration(kid, nameNode,
- TokenPos(begin, pos().end));
- if (!node || !pc->sc()->asModuleContext()->builder.processExport(node))
- return null();
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportVariableStatement(uint32_t begin)
+{
+ if (!abortIfSyntaxParser())
+ return null();
- return node;
- }
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_VAR));
- case TOK_CONST:
- kid = lexicalDeclaration(YieldIsName, /* isConst = */ true);
- if (!kid)
- return null();
- if (!checkExportedNamesForDeclaration(kid))
- return null();
- break;
+ Node kid = declarationList(YieldIsName, PNK_VAR);
+ if (!kid)
+ return null();
+ if (!matchOrInsertSemicolonAfterExpression())
+ return null();
+ if (!checkExportedNamesForDeclaration(kid))
+ return null();
- case TOK_NAME:
- if (tokenStream.currentName() == context->names().let) {
- if (!checkUnescapedName())
- return null();
+ Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
+ if (!node)
+ return null();
- kid = lexicalDeclaration(YieldIsName, /* isConst = */ false);
- if (!kid)
- return null();
- if (!checkExportedNamesForDeclaration(kid))
- return null();
- break;
- }
- MOZ_FALLTHROUGH;
+ if (!processExport(node))
+ return null();
- default:
- error(JSMSG_DECLARATION_AFTER_EXPORT);
+ return node;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportFunctionDeclaration(uint32_t begin)
+{
+ if (!abortIfSyntaxParser())
+ return null();
+
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
+
+ Node kid = functionStmt(pos().begin, YieldIsKeyword, NameRequired);
+ if (!kid)
+ return null();
+
+ if (!checkExportedNameForFunction(kid))
return null();
- }
- ParseNode* node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
- if (!node || !pc->sc()->asModuleContext()->builder.processExport(node))
+ Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
+ if (!node)
+ return null();
+
+ if (!processExport(node))
return null();
return node;
}
-template<>
-SyntaxParseHandler::Node
-Parser<SyntaxParseHandler>::exportDeclaration()
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportClassDeclaration(uint32_t begin)
{
- JS_ALWAYS_FALSE(abortIfSyntaxParser());
- return SyntaxParseHandler::NodeFailure;
+ if (!abortIfSyntaxParser())
+ return null();
+
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
+
+ Node kid = classDefinition(YieldIsKeyword, ClassStatement, NameRequired);
+ if (!kid)
+ return null();
+
+ if (!checkExportedNameForClass(kid))
+ return null();
+
+ Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
+ if (!node)
+ return null();
+
+ if (!processExport(node))
+ return null();
+
+ return node;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportLexicalDeclaration(uint32_t begin, DeclarationKind kind)
+{
+ if (!abortIfSyntaxParser())
+ return null();
+
+ MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let);
+ MOZ_ASSERT_IF(kind == DeclarationKind::Const, tokenStream.isCurrentTokenType(TOK_CONST));
+ MOZ_ASSERT_IF(kind == DeclarationKind::Let, tokenStream.isCurrentTokenType(TOK_LET));
+
+ Node kid = lexicalDeclaration(YieldIsName, kind);
+ if (!kid)
+ return null();
+ if (!checkExportedNamesForDeclaration(kid))
+ return null();
+
+ Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
+ if (!node)
+ return null();
+
+ if (!processExport(node))
+ return null();
+
+ return node;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportDefaultFunctionDeclaration(uint32_t begin,
+ FunctionAsyncKind asyncKind
+ /* = SyncFunction */)
+{
+ if (!abortIfSyntaxParser())
+ return null();
+
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
+
+ Node kid = functionStmt(pos().begin, YieldIsKeyword, AllowDefaultName, asyncKind);
+ if (!kid)
+ return null();
+
+ Node node = handler.newExportDefaultDeclaration(kid, null(), TokenPos(begin, pos().end));
+ if (!node)
+ return null();
+
+ if (!processExport(node))
+ return null();
+
+ return node;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportDefaultClassDeclaration(uint32_t begin)
+{
+ if (!abortIfSyntaxParser())
+ return null();
+
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
+
+ Node kid = classDefinition(YieldIsKeyword, ClassStatement, AllowDefaultName);
+ if (!kid)
+ return null();
+
+ Node node = handler.newExportDefaultDeclaration(kid, null(), TokenPos(begin, pos().end));
+ if (!node)
+ return null();
+
+ if (!processExport(node))
+ return null();
+
+ return node;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportDefaultAssignExpr(uint32_t begin)
+{
+ if (!abortIfSyntaxParser())
+ return null();
+
+ RootedPropertyName name(context, context->names().starDefaultStar);
+ Node nameNode = newName(name);
+ if (!nameNode)
+ return null();
+ if (!noteDeclaredName(name, DeclarationKind::Const, pos()))
+ return null();
+
+ Node kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
+ if (!kid)
+ return null();
+ if (!matchOrInsertSemicolonAfterExpression())
+ return null();
+
+ Node node = handler.newExportDefaultDeclaration(kid, nameNode, TokenPos(begin, pos().end));
+ if (!node)
+ return null();
+
+ if (!processExport(node))
+ return null();
+
+ return node;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportDefault(uint32_t begin)
+{
+ if (!abortIfSyntaxParser())
+ return null();
+
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_DEFAULT));
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
+
+ if (!checkExportedName(context->names().default_))
+ return null();
+
+ switch (tt) {
+ case TOK_FUNCTION:
+ return exportDefaultFunctionDeclaration(begin);
+
+ case TOK_ASYNC: {
+ TokenKind nextSameLine = TOK_EOF;
+ if (!tokenStream.peekTokenSameLine(&nextSameLine))
+ return null();
+
+ if (nextSameLine == TOK_FUNCTION) {
+ tokenStream.consumeKnownToken(TOK_FUNCTION);
+ return exportDefaultFunctionDeclaration(begin, AsyncFunction);
+ }
+
+ tokenStream.ungetToken();
+ return exportDefaultAssignExpr(begin);
+ }
+
+ case TOK_CLASS:
+ return exportDefaultClassDeclaration(begin);
+
+ default:
+ tokenStream.ungetToken();
+ return exportDefaultAssignExpr(begin);
+ }
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportDeclaration()
+{
+ if (!abortIfSyntaxParser())
+ return null();
+
+ MOZ_ASSERT(tokenStream.currentToken().type == TOK_EXPORT);
+
+ if (!pc->atModuleLevel()) {
+ error(JSMSG_EXPORT_DECL_AT_TOP_LEVEL);
+ return null();
+ }
+
+ uint32_t begin = pos().begin;
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt))
+ return null();
+ switch (tt) {
+ case TOK_MUL:
+ return exportBatch(begin);
+
+ case TOK_LC:
+ return exportClause(begin);
+
+ case TOK_VAR:
+ return exportVariableStatement(begin);
+
+ case TOK_FUNCTION:
+ return exportFunctionDeclaration(begin);
+
+ case TOK_CLASS:
+ return exportClassDeclaration(begin);
+
+ case TOK_CONST:
+ return exportLexicalDeclaration(begin, DeclarationKind::Const);
+
+ case TOK_LET:
+ return exportLexicalDeclaration(begin, DeclarationKind::Let);
+
+ case TOK_DEFAULT:
+ return exportDefault(begin);
+
+ default:
+ error(JSMSG_DECLARATION_AFTER_EXPORT);
+ return null();
+ }
}
template <typename ParseHandler>
@@ -5236,7 +5768,7 @@ Parser<ParseHandler>::expressionStatement(YieldHandling yieldHandling, InvokedPr
/* possibleError = */ nullptr, invoked);
if (!pnexpr)
return null();
- if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterExpression())
return null();
return handler.newExprStatement(pnexpr, pos().end);
}
@@ -5249,14 +5781,71 @@ Parser<ParseHandler>::consequentOrAlternative(YieldHandling yieldHandling)
if (!tokenStream.peekToken(&next, TokenStream::Operand))
return null();
- if (next == TOK_FUNCTION) {
- // Apply Annex B.3.4 in non-strict code to allow FunctionDeclaration as
- // the consequent/alternative of an |if| or |else|. Parser::statement
- // will report the strict mode error.
- if (!pc->sc()->strict()) {
- tokenStream.consumeKnownToken(next, TokenStream::Operand);
- return functionStmt(pos().begin, yieldHandling, NameRequired);
+ // Annex B.3.4 says that unbraced FunctionDeclarations under if/else in
+ // non-strict code act as if they were braced: |if (x) function f() {}|
+ // parses as |if (x) { function f() {} }|.
+ //
+ // Careful! FunctionDeclaration doesn't include generators or async
+ // functions.
+ if (next == TOK_ASYNC) {
+ tokenStream.consumeKnownToken(next, TokenStream::Operand);
+
+ // Peek only on the same line: ExpressionStatement's lookahead
+ // restriction is phrased as
+ //
+ // [lookahead ∉ { {, function, async [no LineTerminator here] function, class, let [ }]
+ //
+ // meaning that code like this is valid:
+ //
+ // if (true)
+ // async // ASI opportunity
+ // function clownshoes() {}
+ TokenKind maybeFunction;
+ if (!tokenStream.peekTokenSameLine(&maybeFunction))
+ return null();
+
+ if (maybeFunction == TOK_FUNCTION) {
+ error(JSMSG_FORBIDDEN_AS_STATEMENT, "async function declarations");
+ return null();
}
+
+ // Otherwise this |async| begins an ExpressionStatement.
+ tokenStream.ungetToken();
+ } else if (next == TOK_FUNCTION) {
+ tokenStream.consumeKnownToken(next, TokenStream::Operand);
+
+ // Parser::statement would handle this, but as this function handles
+ // every other error case, it seems best to handle this.
+ if (pc->sc()->strict()) {
+ error(JSMSG_FORBIDDEN_AS_STATEMENT, "function declarations");
+ return null();
+ }
+
+ TokenKind maybeStar;
+ if (!tokenStream.peekToken(&maybeStar))
+ return null();
+
+ if (maybeStar == TOK_MUL) {
+ error(JSMSG_FORBIDDEN_AS_STATEMENT, "generator declarations");
+ return null();
+ }
+
+ ParseContext::Statement stmt(pc, StatementKind::Block);
+ ParseContext::Scope scope(this);
+ if (!scope.init(pc))
+ return null();
+
+ TokenPos funcPos = pos();
+ Node fun = functionStmt(pos().begin, yieldHandling, NameRequired);
+ if (!fun)
+ return null();
+
+ Node block = handler.newStatementList(funcPos);
+ if (!block)
+ return null();
+
+ handler.addStatementToList(block, fun);
+ return finishLexicalScope(scope, block);
}
return statement(yieldHandling);
@@ -5371,13 +5960,9 @@ Parser<ParseHandler>::matchInOrOf(bool* isForInp, bool* isForOfp)
return false;
*isForInp = tt == TOK_IN;
- *isForOfp = tt == TOK_NAME && tokenStream.currentToken().name() == context->names().of;
- if (!*isForInp && !*isForOfp) {
+ *isForOfp = tt == TOK_OF;
+ if (!*isForInp && !*isForOfp)
tokenStream.ungetToken();
- } else {
- if (tt == TOK_NAME && !checkUnescapedName())
- return false;
- }
MOZ_ASSERT_IF(*isForInp || *isForOfp, *isForInp != *isForOfp);
return true;
@@ -5427,15 +6012,12 @@ Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling,
if (tt == TOK_CONST) {
parsingLexicalDeclaration = true;
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
- } else if (tt == TOK_NAME &&
- tokenStream.nextName() == context->names().let &&
- !tokenStream.nextNameContainsEscape())
- {
+ } else if (tt == TOK_LET) {
// We could have a {For,Lexical}Declaration, or we could have a
// LeftHandSideExpression with lookahead restrictions so it's not
// ambiguous with the former. Check for a continuation of the former
// to decide which we have.
- tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand);
+ tokenStream.consumeKnownToken(TOK_LET, TokenStream::Operand);
TokenKind next;
if (!tokenStream.peekToken(&next))
@@ -5512,7 +6094,7 @@ Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling,
// Verify the left-hand side expression doesn't have a forbidden form.
if (handler.isUnparenthesizedDestructuringPattern(*forInitialPart)) {
- if (!checkDestructuringPattern(*forInitialPart, Nothing(), &possibleError))
+ if (!possibleError.checkForDestructuringErrorOrWarning())
return false;
} else if (handler.isNameAnyParentheses(*forInitialPart)) {
const char* chars = handler.nameIsArgumentsEvalAnyParentheses(*forInitialPart, context);
@@ -5556,7 +6138,7 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling)
if (allowsForEachIn()) {
bool matched;
- if (!tokenStream.matchContextualKeyword(&matched, context->names().each))
+ if (!tokenStream.matchToken(&matched, TOK_EACH))
return null();
if (matched) {
iflags = JSITER_FOREACH;
@@ -5867,7 +6449,7 @@ Parser<ParseHandler>::continueStatement(YieldHandling yieldHandling)
return null();
}
- if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterNonExpression())
return null();
return handler.newContinueStatement(label, TokenPos(begin, pos().end));
@@ -5907,7 +6489,7 @@ Parser<ParseHandler>::breakStatement(YieldHandling yieldHandling)
}
}
- if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterNonExpression())
return null();
return handler.newBreakStatement(label, TokenPos(begin, pos().end));
@@ -5947,10 +6529,10 @@ Parser<ParseHandler>::returnStatement(YieldHandling yieldHandling)
}
if (exprNode) {
- if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterExpression())
return null();
} else {
- if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterNonExpression())
return null();
}
@@ -6070,7 +6652,7 @@ Parser<ParseHandler>::yieldExpression(InHandling inHandling)
if (pc->funHasReturnExpr
#if JS_HAS_EXPR_CLOSURES
- || pc->functionBox()->function()->isExprBody()
+ || pc->functionBox()->isExprBody()
#endif
)
{
@@ -6247,7 +6829,7 @@ Parser<ParseHandler>::throwStatement(YieldHandling yieldHandling)
if (!throwExpr)
return null();
- if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterExpression())
return null();
return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end));
@@ -6267,12 +6849,12 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
* kid3 is the finally statement
*
* catch nodes are ternary.
- * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
+ * kid1 is the lvalue (possible identifier, TOK_LB, or TOK_LC)
* kid2 is the catch guard or null if no guard
* kid3 is the catch block
*
* catch lvalue nodes are either:
- * TOK_NAME for a single identifier
+ * a single identifier
* TOK_RB or TOK_RC for a destructuring left-hand side
*
* finally nodes are TOK_LC statement lists.
@@ -6282,6 +6864,8 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
{
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
+ uint32_t openedPos = pos().begin;
+
ParseContext::Statement stmt(pc, StatementKind::Try);
ParseContext::Scope scope(this);
if (!scope.init(pc))
@@ -6295,7 +6879,9 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
if (!innerBlock)
return null();
- MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_TRY);
+ MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
+ reportMissingClosing(JSMSG_CURLY_AFTER_TRY,
+ JSMSG_CURLY_OPENED, openedPos));
}
bool hasUnconditionalCatch = false;
@@ -6347,22 +6933,18 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
return null();
break;
- case TOK_NAME:
- case TOK_YIELD: {
- RootedPropertyName param(context, bindingIdentifier(yieldHandling));
- if (!param)
+ default: {
+ if (!TokenKindIsPossibleIdentifierName(tt)) {
+ error(JSMSG_CATCH_IDENTIFIER);
return null();
- catchName = newName(param);
+ }
+
+ catchName = bindingIdentifier(DeclarationKind::SimpleCatchParameter,
+ yieldHandling);
if (!catchName)
return null();
- if (!noteDeclaredName(param, DeclarationKind::SimpleCatchParameter, pos()))
- return null();
break;
}
-
- default:
- error(JSMSG_CATCH_IDENTIFIER);
- return null();
}
Node catchGuard = null();
@@ -6411,6 +6993,8 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
if (tt == TOK_FINALLY) {
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
+ uint32_t openedPos = pos().begin;
+
ParseContext::Statement stmt(pc, StatementKind::Finally);
ParseContext::Scope scope(this);
if (!scope.init(pc))
@@ -6424,7 +7008,9 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
if (!finallyBlock)
return null();
- MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_FINALLY);
+ MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
+ reportMissingClosing(JSMSG_CURLY_AFTER_FINALLY,
+ JSMSG_CURLY_OPENED, openedPos));
} else {
tokenStream.ungetToken();
}
@@ -6441,6 +7027,8 @@ typename ParseHandler::Node
Parser<ParseHandler>::catchBlockStatement(YieldHandling yieldHandling,
ParseContext::Scope& catchParamScope)
{
+ uint32_t openedPos = pos().begin;
+
ParseContext::Statement stmt(pc, StatementKind::Block);
// ES 13.15.7 CatchClauseEvaluation
@@ -6460,7 +7048,9 @@ Parser<ParseHandler>::catchBlockStatement(YieldHandling yieldHandling,
if (!list)
return null();
- MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_CATCH);
+ MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
+ reportMissingClosing(JSMSG_CURLY_AFTER_CATCH,
+ JSMSG_CURLY_OPENED, openedPos));
// The catch parameter names are not bound in the body scope, so remove
// them before generating bindings.
@@ -6474,7 +7064,7 @@ Parser<ParseHandler>::debuggerStatement()
{
TokenPos p;
p.begin = pos().begin;
- if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterNonExpression())
return null();
p.end = pos().end;
@@ -6514,6 +7104,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
+ uint32_t classStartOffset = pos().begin;
bool savedStrictness = setLocalStrictMode(true);
TokenKind tt;
@@ -6521,7 +7112,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
return null();
RootedPropertyName name(context);
- if (tt == TOK_NAME || tt == TOK_YIELD) {
+ if (TokenKindIsPossibleIdentifier(tt)) {
name = bindingIdentifier(yieldHandling);
if (!name)
return null();
@@ -6539,16 +7130,20 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
tokenStream.ungetToken();
}
+ // Push a ParseContext::ClassStatement to keep track of the constructor
+ // funbox.
+ ParseContext::ClassStatement classStmt(pc);
+
RootedAtom propAtom(context);
// A named class creates a new lexical scope with a const binding of the
- // class name.
- Maybe<ParseContext::Statement> classStmt;
- Maybe<ParseContext::Scope> classScope;
+ // class name for the "inner name".
+ Maybe<ParseContext::Statement> innerScopeStmt;
+ Maybe<ParseContext::Scope> innerScope;
if (name) {
- classStmt.emplace(pc, StatementKind::Block);
- classScope.emplace(this);
- if (!classScope->init(pc))
+ innerScopeStmt.emplace(pc, StatementKind::Block);
+ innerScope.emplace(this);
+ if (!innerScope->init(pc))
return null();
}
@@ -6575,10 +7170,10 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
if (!classMethods)
return null();
- bool seenConstructor = false;
+ Maybe<DeclarationKind> declKind = Nothing();
for (;;) {
TokenKind tt;
- if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
+ if (!tokenStream.getToken(&tt))
return null();
if (tt == TOK_RC)
break;
@@ -6587,22 +7182,18 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
continue;
bool isStatic = false;
- if (tt == TOK_NAME && tokenStream.currentName() == context->names().static_) {
- if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName))
+ if (tt == TOK_STATIC) {
+ if (!tokenStream.peekToken(&tt))
return null();
if (tt == TOK_RC) {
- tokenStream.consumeKnownToken(tt, TokenStream::KeywordIsName);
+ tokenStream.consumeKnownToken(tt);
error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt));
return null();
}
if (tt != TOK_LP) {
- if (!checkUnescapedName())
- return null();
-
isStatic = true;
} else {
- tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName);
tokenStream.ungetToken();
}
} else {
@@ -6614,7 +7205,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
return null();
PropertyType propType;
- Node propName = propertyName(yieldHandling, classMethods, &propType, &propAtom);
+ Node propName = propertyName(yieldHandling, declKind, classMethods, &propType, &propAtom);
if (!propName)
return null();
@@ -6631,16 +7222,17 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
propType = PropertyType::GetterNoExpressionClosure;
if (propType == PropertyType::Setter)
propType = PropertyType::SetterNoExpressionClosure;
- if (!isStatic && propAtom == context->names().constructor) {
+
+ bool isConstructor = !isStatic && propAtom == context->names().constructor;
+ if (isConstructor) {
if (propType != PropertyType::Method) {
errorAt(nameOffset, JSMSG_BAD_METHOD_DEF);
return null();
}
- if (seenConstructor) {
+ if (classStmt.constructorBox) {
errorAt(nameOffset, JSMSG_DUPLICATE_PROPERTY, "constructor");
return null();
}
- seenConstructor = true;
propType = hasHeritage ? PropertyType::DerivedConstructor : PropertyType::Constructor;
} else if (isStatic && propAtom == context->names().prototype) {
errorAt(nameOffset, JSMSG_BAD_METHOD_DEF);
@@ -6665,7 +7257,12 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
if (!tokenStream.isCurrentTokenType(TOK_RB))
funName = propAtom;
}
- Node fn = methodDefinition(nameOffset, propType, funName);
+
+ // Calling toString on constructors need to return the source text for
+ // the entire class. The end offset is unknown at this point in
+ // parsing and will be amended when class parsing finishes below.
+ Node fn = methodDefinition(isConstructor ? classStartOffset : nameOffset,
+ propType, funName);
if (!fn)
return null();
@@ -6676,6 +7273,15 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
return null();
}
+ // Amend the toStringEnd offset for the constructor now that we've
+ // finished parsing the class.
+ uint32_t classEndOffset = pos().end;
+ if (FunctionBox* ctorbox = classStmt.constructorBox) {
+ if (ctorbox->function()->isInterpretedLazy())
+ ctorbox->function()->lazyScript()->setToStringEnd(classEndOffset);
+ ctorbox->toStringEnd = classEndOffset;
+ }
+
Node nameNode = null();
Node methodsOrBlock = classMethods;
if (name) {
@@ -6687,15 +7293,15 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
if (!innerName)
return null();
- Node classBlock = finishLexicalScope(*classScope, classMethods);
+ Node classBlock = finishLexicalScope(*innerScope, classMethods);
if (!classBlock)
return null();
methodsOrBlock = classBlock;
// Pop the inner scope.
- classScope.reset();
- classStmt.reset();
+ innerScope.reset();
+ innerScopeStmt.reset();
Node outerName = null();
if (classContext == ClassStatement) {
@@ -6715,16 +7321,15 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
MOZ_ALWAYS_TRUE(setLocalStrictMode(savedStrictness));
- return handler.newClass(nameNode, classHeritage, methodsOrBlock);
+ return handler.newClass(nameNode, classHeritage, methodsOrBlock,
+ TokenPos(classStartOffset, classEndOffset));
}
template <class ParseHandler>
bool
Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHandling yieldHandling)
{
- MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME));
- MOZ_ASSERT(tokenStream.currentName() == context->names().let);
- MOZ_ASSERT(!tokenStream.currentToken().nameContainsEscape());
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LET));
#ifdef DEBUG
TokenKind verify;
@@ -6736,18 +7341,19 @@ Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHand
if (next == TOK_LB || next == TOK_LC)
return true;
- // Otherwise a let declaration must have a name.
- if (next == TOK_NAME) {
- if (tokenStream.nextName() == context->names().yield) {
- MOZ_ASSERT(tokenStream.nextNameContainsEscape(),
- "token stream should interpret unescaped 'yield' as TOK_YIELD");
-
- // Same as |next == TOK_YIELD|.
- return yieldHandling == YieldIsName;
- }
+ // If we have the name "yield", the grammar parameter exactly states
+ // whether this is okay. (This wasn't true for SpiderMonkey's ancient
+ // legacy generator syntax, but that's dead now.) If YieldIsName,
+ // declaration-parsing code will (if necessary) enforce a strict mode
+ // restriction on defining "yield". If YieldIsKeyword, consider this the
+ // end of the declaration, in case ASI induces a semicolon that makes the
+ // "yield" valid.
+ if (next == TOK_YIELD)
+ return yieldHandling == YieldIsName;
- // One non-"yield" TOK_NAME edge case deserves special comment.
- // Consider this:
+ // Otherwise a let declaration must have a name.
+ if (TokenKindIsPossibleIdentifier(next)) {
+ // A "let" edge case deserves special comment. Consider this:
//
// let // not an ASI opportunity
// let;
@@ -6760,16 +7366,6 @@ Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHand
return true;
}
- // If we have the name "yield", the grammar parameter exactly states
- // whether this is okay. (This wasn't true for SpiderMonkey's ancient
- // legacy generator syntax, but that's dead now.) If YieldIsName,
- // declaration-parsing code will (if necessary) enforce a strict mode
- // restriction on defining "yield". If YieldIsKeyword, consider this the
- // end of the declaration, in case ASI induces a semicolon that makes the
- // "yield" valid.
- if (next == TOK_YIELD)
- return yieldHandling == YieldIsName;
-
// Otherwise not a let declaration.
return false;
}
@@ -6781,7 +7377,7 @@ Parser<ParseHandler>::variableStatement(YieldHandling yieldHandling)
Node vars = declarationList(yieldHandling, PNK_VAR);
if (!vars)
return null();
- if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterExpression())
return null();
return vars;
}
@@ -6832,16 +7428,21 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
return expressionStatement(yieldHandling);
}
- case TOK_NAME: {
+ default: {
+ // Avoid getting next token with None.
+ if (tt == TOK_AWAIT && pc->isAsync())
+ return expressionStatement(yieldHandling);
+
+ if (!TokenKindIsPossibleIdentifier(tt))
+ return expressionStatement(yieldHandling);
+
TokenKind next;
if (!tokenStream.peekToken(&next))
return null();
// |let| here can only be an Identifier, not a declaration. Give nicer
// errors for declaration-looking typos.
- if (!tokenStream.currentToken().nameContainsEscape() &&
- tokenStream.currentName() == context->names().let)
- {
+ if (tt == TOK_LET) {
bool forbiddenLetDeclaration = false;
if (pc->sc()->strict() || versionNumber() >= JSVERSION_1_7) {
@@ -6851,7 +7452,7 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
} else if (next == TOK_LB) {
// Enforce ExpressionStatement's 'let [' lookahead restriction.
forbiddenLetDeclaration = true;
- } else if (next == TOK_LC || next == TOK_NAME) {
+ } else if (next == TOK_LC || TokenKindIsPossibleIdentifier(next)) {
// 'let {' and 'let foo' aren't completely forbidden, if ASI
// causes 'let' to be the entire Statement. But if they're
// same-line, we can aggressively give a better error message.
@@ -6862,7 +7463,7 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
if (!tokenStream.peekTokenSameLine(&nextSameLine))
return null();
- MOZ_ASSERT(nextSameLine == TOK_NAME ||
+ MOZ_ASSERT(TokenKindIsPossibleIdentifier(nextSameLine) ||
nextSameLine == TOK_LC ||
nextSameLine == TOK_EOL);
@@ -6886,9 +7487,6 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
case TOK_NEW:
return expressionStatement(yieldHandling, PredictInvoked);
- default:
- return expressionStatement(yieldHandling);
-
// IfStatement[?Yield, ?Return]
case TOK_IF:
return ifStatement(yieldHandling);
@@ -6934,7 +7532,7 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
return withStatement(yieldHandling);
// LabelledStatement[?Yield, ?Return]
- // This is really handled by TOK_NAME and TOK_YIELD cases above.
+ // This is really handled by default and TOK_YIELD cases above.
// ThrowStatement[?Yield]
case TOK_THROW:
@@ -7040,26 +7638,29 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
return expressionStatement(yieldHandling);
}
- case TOK_NAME: {
+ default: {
+ // Avoid getting next token with None.
+ if (tt == TOK_AWAIT && pc->isAsync())
+ return expressionStatement(yieldHandling);
+
+ if (!TokenKindIsPossibleIdentifier(tt))
+ return expressionStatement(yieldHandling);
+
TokenKind next;
if (!tokenStream.peekToken(&next))
return null();
- if (!tokenStream.currentToken().nameContainsEscape() &&
- tokenStream.currentName() == context->names().let &&
- nextTokenContinuesLetDeclaration(next, yieldHandling))
- {
- return lexicalDeclaration(yieldHandling, /* isConst = */ false);
- }
+ if (tt == TOK_LET && nextTokenContinuesLetDeclaration(next, yieldHandling))
+ return lexicalDeclaration(yieldHandling, DeclarationKind::Let);
- if (tokenStream.currentName() == context->names().async) {
+ if (tt == TOK_ASYNC) {
TokenKind nextSameLine = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&nextSameLine))
return null();
if (nextSameLine == TOK_FUNCTION) {
- uint32_t preludeStart = pos().begin;
+ uint32_t toStringStart = pos().begin;
tokenStream.consumeKnownToken(TOK_FUNCTION);
- return functionStmt(preludeStart, yieldHandling, NameRequired, AsyncFunction);
+ return functionStmt(toStringStart, yieldHandling, NameRequired, AsyncFunction);
}
}
@@ -7072,9 +7673,6 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
case TOK_NEW:
return expressionStatement(yieldHandling, PredictInvoked);
- default:
- return expressionStatement(yieldHandling);
-
// IfStatement[?Yield, ?Return]
case TOK_IF:
return ifStatement(yieldHandling);
@@ -7120,7 +7718,7 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
return withStatement(yieldHandling);
// LabelledStatement[?Yield, ?Return]
- // This is really handled by TOK_NAME and TOK_YIELD cases above.
+ // This is really handled by default and TOK_YIELD cases above.
// ThrowStatement[?Yield]
case TOK_THROW:
@@ -7149,7 +7747,7 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
case TOK_CONST:
// [In] is the default behavior, because for-loops specially parse
// their heads to handle |in| in this situation.
- return lexicalDeclaration(yieldHandling, /* isConst = */ true);
+ return lexicalDeclaration(yieldHandling, DeclarationKind::Const);
// ImportDeclaration (only inside modules)
case TOK_IMPORT:
@@ -7438,26 +8036,6 @@ Parser<ParseHandler>::condExpr1(InHandling inHandling, YieldHandling yieldHandli
return handler.newConditional(condition, thenExpr, elseExpr);
}
-class AutoClearInDestructuringDecl
-{
- ParseContext* pc_;
- Maybe<DeclarationKind> saved_;
-
- public:
- explicit AutoClearInDestructuringDecl(ParseContext* pc)
- : pc_(pc),
- saved_(pc->inDestructuringDecl)
- {
- pc->inDestructuringDecl = Nothing();
- if (saved_ && *saved_ == DeclarationKind::FormalParameter)
- pc->functionBox()->hasParameterExprs = true;
- }
-
- ~AutoClearInDestructuringDecl() {
- pc_->inDestructuringDecl = saved_;
- }
-};
-
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
@@ -7482,10 +8060,13 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
if (!tokenStream.getToken(&tt, TokenStream::Operand))
return null();
- uint32_t exprOffset = pos().begin;
+ TokenPos exprPos = pos();
bool endsExpr;
+ // This only handles identifiers that *never* have special meaning anywhere
+ // in the language. Contextual keywords, reserved words in strict mode,
+ // and other hard cases are handled outside this fast path.
if (tt == TOK_NAME) {
if (!tokenStream.nextTokenEndsExpr(&endsExpr))
return null();
@@ -7516,12 +8097,12 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
return yieldExpression(inHandling);
bool maybeAsyncArrow = false;
- if (tt == TOK_NAME && tokenStream.currentName() == context->names().async) {
+ if (tt == TOK_ASYNC) {
TokenKind nextSameLine = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&nextSameLine))
return null();
- if (nextSameLine == TOK_NAME || nextSameLine == TOK_YIELD)
+ if (TokenKindIsPossibleIdentifier(nextSameLine))
maybeAsyncArrow = true;
}
@@ -7535,13 +8116,12 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
PossibleError possibleErrorInner(*this);
Node lhs;
if (maybeAsyncArrow) {
- tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand);
- MOZ_ASSERT(tokenStream.currentName() == context->names().async);
+ tokenStream.consumeKnownToken(TOK_ASYNC, TokenStream::Operand);
TokenKind tt;
if (!tokenStream.getToken(&tt))
return null();
- MOZ_ASSERT(tt == TOK_NAME || tt == TOK_YIELD);
+ MOZ_ASSERT(TokenKindIsPossibleIdentifier(tt));
// Check yield validity here.
RootedPropertyName name(context, bindingIdentifier(yieldHandling));
@@ -7604,28 +8184,24 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
if (!tokenStream.getToken(&next, TokenStream::Operand))
return null();
- uint32_t preludeStart = pos().begin;
+ uint32_t toStringStart = pos().begin;
tokenStream.ungetToken();
GeneratorKind generatorKind = NotGenerator;
FunctionAsyncKind asyncKind = SyncFunction;
- if (next == TOK_NAME) {
+ if (next == TOK_ASYNC) {
tokenStream.consumeKnownToken(next, TokenStream::Operand);
- if (tokenStream.currentName() == context->names().async) {
- TokenKind nextSameLine = TOK_EOF;
- if (!tokenStream.peekTokenSameLine(&nextSameLine))
- return null();
+ TokenKind nextSameLine = TOK_EOF;
+ if (!tokenStream.peekTokenSameLine(&nextSameLine))
+ return null();
- if (nextSameLine == TOK_ARROW) {
- tokenStream.ungetToken();
- } else {
- generatorKind = StarGenerator;
- asyncKind = AsyncFunction;
- }
- } else {
+ if (nextSameLine == TOK_ARROW) {
tokenStream.ungetToken();
+ } else {
+ generatorKind = StarGenerator;
+ asyncKind = AsyncFunction;
}
}
@@ -7633,7 +8209,7 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
if (!pn)
return null();
- Node arrowFunc = functionDefinition(preludeStart, pn, inHandling, yieldHandling, nullptr,
+ Node arrowFunc = functionDefinition(toStringStart, pn, inHandling, yieldHandling, nullptr,
Arrow, generatorKind, asyncKind);
if (!arrowFunc)
return null();
@@ -7690,12 +8266,12 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
return null();
}
- if (!checkDestructuringPattern(lhs, Nothing(), &possibleErrorInner))
+ if (!possibleErrorInner.checkForDestructuringErrorOrWarning())
return null();
} else if (handler.isNameAnyParentheses(lhs)) {
if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(lhs, context)) {
// |chars| is "arguments" or "eval" here.
- if (!strictModeErrorAt(exprOffset, JSMSG_BAD_STRICT_ASSIGN, chars))
+ if (!strictModeErrorAt(exprPos.begin, JSMSG_BAD_STRICT_ASSIGN, chars))
return null();
}
@@ -7703,23 +8279,22 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
} else if (handler.isPropertyAccess(lhs)) {
// Permitted: no additional testing/fixup needed.
} else if (handler.isFunctionCall(lhs)) {
- if (!strictModeErrorAt(exprOffset, JSMSG_BAD_LEFTSIDE_OF_ASS))
+ if (!strictModeErrorAt(exprPos.begin, JSMSG_BAD_LEFTSIDE_OF_ASS))
return null();
+
+ if (possibleError)
+ possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET);
} else {
- errorAt(exprOffset, JSMSG_BAD_LEFTSIDE_OF_ASS);
+ errorAt(exprPos.begin, JSMSG_BAD_LEFTSIDE_OF_ASS);
return null();
}
if (!possibleErrorInner.checkForExpressionError())
return null();
- Node rhs;
- {
- AutoClearInDestructuringDecl autoClear(pc);
- rhs = assignExpr(inHandling, yieldHandling, TripledotProhibited);
- if (!rhs)
- return null();
- }
+ Node rhs = assignExpr(inHandling, yieldHandling, TripledotProhibited);
+ if (!rhs)
+ return null();
if (kind == PNK_ASSIGN)
handler.checkAndSetIsDirectRHSAnonFunction(rhs);
@@ -7874,20 +8449,17 @@ Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling t
}
case TOK_AWAIT: {
- if (!pc->isAsync()) {
- // TOK_AWAIT can be returned in module, even if it's not inside
- // async function.
- error(JSMSG_RESERVED_ID, "await");
- return null();
+ if (pc->isAsync()) {
+ Node kid = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked);
+ if (!kid)
+ return null();
+ pc->lastAwaitOffset = begin;
+ return newAwaitExpression(begin, kid);
}
-
- Node kid = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked);
- if (!kid)
- return null();
- pc->lastAwaitOffset = begin;
- return newAwaitExpression(begin, kid);
}
+ MOZ_FALLTHROUGH;
+
default: {
Node expr = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true,
possibleError, invoked);
@@ -7949,7 +8521,7 @@ Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin)
// Create box for fun->object early to root it.
Directives directives(/* strict = */ outerpc->sc()->strict());
- FunctionBox* genFunbox = newFunctionBox(genfn, fun, /* preludeStart = */ 0, directives,
+ FunctionBox* genFunbox = newFunctionBox(genfn, fun, /* toStringStart = */ 0, directives,
StarGenerator, SyncFunction, /* tryAnnexB = */ false);
if (!genFunbox)
return null();
@@ -7985,7 +8557,7 @@ Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin)
uint32_t end = pos().end;
handler.setBeginPosition(comp, begin);
handler.setEndPosition(comp, end);
- genFunbox->bufEnd = end;
+ genFunbox->setEnd(end);
handler.addStatementToList(body, comp);
handler.setEndPosition(body, end);
handler.setBeginPosition(genfn, begin);
@@ -8025,8 +8597,10 @@ Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind)
// FIXME: Destructuring binding (bug 980828).
- MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME);
- RootedPropertyName name(context, tokenStream.currentName());
+ MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifier, JSMSG_NO_VARIABLE_NAME);
+ RootedPropertyName name(context, bindingIdentifier(YieldIsKeyword));
+ if (!name)
+ return null();
if (name == context->names().let) {
error(JSMSG_LET_COMP_BINDING);
return null();
@@ -8036,7 +8610,7 @@ Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind)
if (!lhs)
return null();
bool matched;
- if (!tokenStream.matchContextualKeyword(&matched, context->names().of))
+ if (!tokenStream.matchToken(&matched, TOK_OF))
return null();
if (!matched) {
error(JSMSG_OF_AFTER_FOR_NAME);
@@ -8371,9 +8945,9 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
Node nextMember;
if (tt == TOK_DOT) {
- if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
+ if (!tokenStream.getToken(&tt))
return null();
- if (tt == TOK_NAME) {
+ if (TokenKindIsPossibleIdentifierName(tt)) {
PropertyName* field = tokenStream.currentName();
if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
error(JSMSG_BAD_SUPERPROP, "property");
@@ -8448,8 +9022,27 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
JSOp op = JSOP_CALL;
bool maybeAsyncArrow = false;
- if (tt == TOK_LP && handler.isNameAnyParentheses(lhs)) {
- if (handler.nameIsEvalAnyParentheses(lhs, context)) {
+ if (PropertyName* prop = handler.maybeDottedProperty(lhs)) {
+ // Use the JSOP_FUN{APPLY,CALL} optimizations given the
+ // right syntax.
+ if (prop == context->names().apply) {
+ op = JSOP_FUNAPPLY;
+ if (pc->isFunctionBox()) {
+ pc->functionBox()->usesApply = true;
+ }
+ } else if (prop == context->names().call) {
+ op = JSOP_FUNCALL;
+ }
+ } else if (tt == TOK_LP) {
+ if (handler.isAsyncKeyword(lhs, context)) {
+ // |async (| can be the start of an async arrow
+ // function, so we need to defer reporting possible
+ // errors from destructuring syntax. To give better
+ // error messages, we only allow the AsyncArrowHead
+ // part of the CoverCallExpressionAndAsyncArrowHead
+ // syntax when the initial name is "async".
+ maybeAsyncArrow = true;
+ } else if (handler.isEvalAnyParentheses(lhs, context)) {
// Select the right EVAL op and flag pc as having a
// direct eval.
op = pc->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
@@ -8466,24 +9059,6 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
// it. (If we're not in a method, that's fine, so
// ignore the return value.)
checkAndMarkSuperScope();
- } else if (handler.nameIsUnparenthesizedAsync(lhs, context)) {
- // |async (| can be the start of an async arrow
- // function, so we need to defer reporting possible
- // errors from destructuring syntax. To give better
- // error messages, we only allow the AsyncArrowHead
- // part of the CoverCallExpressionAndAsyncArrowHead
- // syntax when the initial name is "async".
- maybeAsyncArrow = true;
- }
- } else if (PropertyName* prop = handler.maybeDottedProperty(lhs)) {
- // Use the JSOP_FUN{APPLY,CALL} optimizations given the
- // right syntax.
- if (prop == context->names().apply) {
- op = JSOP_FUNAPPLY;
- if (pc->isFunctionBox())
- pc->functionBox()->usesApply = true;
- } else if (prop == context->names().call) {
- op = JSOP_FUNCALL;
}
}
@@ -8542,106 +9117,117 @@ Parser<ParseHandler>::newName(PropertyName* name, TokenPos pos)
}
template <typename ParseHandler>
-PropertyName*
-Parser<ParseHandler>::labelOrIdentifierReference(YieldHandling yieldHandling,
- bool yieldTokenizedAsName)
-{
- PropertyName* ident;
- bool isYield;
- const Token& tok = tokenStream.currentToken();
- if (tok.type == TOK_NAME) {
- MOZ_ASSERT(tok.name() != context->names().yield ||
- tok.nameContainsEscape() ||
- yieldTokenizedAsName,
- "tokenizer should have treated unescaped 'yield' as TOK_YIELD");
- MOZ_ASSERT_IF(yieldTokenizedAsName, tok.name() == context->names().yield);
-
- ident = tok.name();
- isYield = ident == context->names().yield;
- } else {
- MOZ_ASSERT(tok.type == TOK_YIELD && !yieldTokenizedAsName);
+bool
+Parser<ParseHandler>::checkLabelOrIdentifierReference(HandlePropertyName ident,
+ uint32_t offset,
+ YieldHandling yieldHandling)
+{
+ if (ident == context->names().yield) {
+ if (yieldHandling == YieldIsKeyword ||
+ versionNumber() >= JSVERSION_1_7)
+ {
+ errorAt(offset, JSMSG_RESERVED_ID, "yield");
+ return false;
+ }
+ if (pc->sc()->needStrictChecks()) {
+ if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "yield"))
+ return false;
+ }
- ident = context->names().yield;
- isYield = true;
+ return true;
}
- if (!isYield) {
- if (pc->sc()->strict()) {
- const char* badName = ident == context->names().let
- ? "let"
- : ident == context->names().static_
- ? "static"
- : nullptr;
- if (badName) {
- error(JSMSG_RESERVED_ID, badName);
- return nullptr;
- }
+ if (ident == context->names().await) {
+ if (awaitIsKeyword()) {
+ errorAt(offset, JSMSG_RESERVED_ID, "await");
+ return false;
}
- } else {
- if (yieldHandling == YieldIsKeyword ||
- pc->sc()->strict() ||
- versionNumber() >= JSVERSION_1_7)
- {
- error(JSMSG_RESERVED_ID, "yield");
- return nullptr;
+ return true;
+ }
+
+ if (IsKeyword(ident) || IsReservedWordLiteral(ident)) {
+ errorAt(offset, JSMSG_INVALID_ID, ReservedWordToCharZ(ident));
+ return false;
+ }
+
+ if (IsFutureReservedWord(ident)) {
+ errorAt(offset, JSMSG_RESERVED_ID, ReservedWordToCharZ(ident));
+ return false;
+ }
+
+ if (pc->sc()->needStrictChecks()) {
+ if (IsStrictReservedWord(ident)) {
+ if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, ReservedWordToCharZ(ident)))
+ return false;
+ return true;
+ }
+
+ if (ident == context->names().let) {
+ if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "let"))
+ return false;
+ return true;
+ }
+
+ if (ident == context->names().static_) {
+ if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "static"))
+ return false;
+ return true;
}
}
- return ident;
+ return true;
}
template <typename ParseHandler>
-PropertyName*
-Parser<ParseHandler>::bindingIdentifier(YieldHandling yieldHandling)
+bool
+Parser<ParseHandler>::checkBindingIdentifier(HandlePropertyName ident,
+ uint32_t offset,
+ YieldHandling yieldHandling)
{
- PropertyName* ident;
- bool isYield;
- const Token& tok = tokenStream.currentToken();
- if (tok.type == TOK_NAME) {
- MOZ_ASSERT(tok.name() != context->names().yield || tok.nameContainsEscape(),
- "tokenizer should have treated unescaped 'yield' as TOK_YIELD");
+ if (!checkLabelOrIdentifierReference(ident, offset, yieldHandling))
+ return false;
- ident = tok.name();
- isYield = ident == context->names().yield;
- } else {
- MOZ_ASSERT(tok.type == TOK_YIELD);
+ if (pc->sc()->needStrictChecks()) {
+ if (ident == context->names().arguments) {
+ if (!strictModeErrorAt(offset, JSMSG_BAD_STRICT_ASSIGN, "arguments"))
+ return false;
+ return true;
+ }
- ident = context->names().yield;
- isYield = true;
+ if (ident == context->names().eval) {
+ if (!strictModeErrorAt(offset, JSMSG_BAD_STRICT_ASSIGN, "eval"))
+ return false;
+ return true;
+ }
}
- if (!isYield) {
- if (pc->sc()->strict()) {
- const char* badName = ident == context->names().arguments
- ? "arguments"
- : ident == context->names().eval
- ? "eval"
- : nullptr;
- if (badName) {
- error(JSMSG_BAD_STRICT_ASSIGN, badName);
- return nullptr;
- }
+ return true;
+}
- badName = ident == context->names().let
- ? "let"
- : ident == context->names().static_
- ? "static"
- : nullptr;
- if (badName) {
- error(JSMSG_RESERVED_ID, badName);
- return nullptr;
- }
- }
- } else {
- if (yieldHandling == YieldIsKeyword ||
- pc->sc()->strict() ||
- versionNumber() >= JSVERSION_1_7)
- {
- error(JSMSG_RESERVED_ID, "yield");
- return nullptr;
- }
- }
+template <typename ParseHandler>
+PropertyName*
+Parser<ParseHandler>::labelOrIdentifierReference(YieldHandling yieldHandling)
+{
+ // ES 2017 draft 12.1.1.
+ // StringValue of IdentifierName normalizes any Unicode escape sequences
+ // in IdentifierName hence such escapes cannot be used to write an
+ // Identifier whose code point sequence is the same as a ReservedWord.
+ //
+ // Use PropertyName* instead of TokenKind to reflect the normalization.
+ RootedPropertyName ident(context, tokenStream.currentName());
+ if (!checkLabelOrIdentifierReference(ident, pos().begin, yieldHandling))
+ return nullptr;
+ return ident;
+}
+
+template <typename ParseHandler>
+PropertyName*
+Parser<ParseHandler>::bindingIdentifier(YieldHandling yieldHandling)
+{
+ RootedPropertyName ident(context, tokenStream.currentName());
+ if (!checkBindingIdentifier(ident, pos().begin, yieldHandling))
+ return nullptr;
return ident;
}
@@ -8653,7 +9239,7 @@ Parser<ParseHandler>::identifierReference(Handle<PropertyName*> name)
if (!pn)
return null();
- if (!pc->inDestructuringDecl && !noteUsedName(name))
+ if (!noteUsedName(name))
return null();
return pn;
@@ -8668,8 +9254,23 @@ Parser<ParseHandler>::stringLiteral()
template <typename ParseHandler>
typename ParseHandler::Node
-Parser<ParseHandler>::noSubstitutionTemplate()
+Parser<ParseHandler>::noSubstitutionTaggedTemplate()
+{
+ if (tokenStream.hasInvalidTemplateEscape()) {
+ tokenStream.clearInvalidTemplateEscape();
+ return handler.newRawUndefinedLiteral(pos());
+ }
+
+ return handler.newTemplateStringLiteral(stopStringCompression(), pos());
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::noSubstitutionUntaggedTemplate()
{
+ if (!tokenStream.checkForInvalidTemplateEscapeError())
+ return null();
+
return handler.newTemplateStringLiteral(stopStringCompression(), pos());
}
@@ -8705,6 +9306,74 @@ Parser<ParseHandler>::newRegExp()
}
template <typename ParseHandler>
+void
+Parser<ParseHandler>::checkDestructuringAssignmentTarget(Node expr, TokenPos exprPos,
+ PossibleError* possibleError)
+{
+ // Return early if a pending destructuring error is already present.
+ if (possibleError->hasPendingDestructuringError())
+ return;
+
+ if (pc->sc()->needStrictChecks()) {
+ if (handler.isArgumentsAnyParentheses(expr, context)) {
+ if (pc->sc()->strict()) {
+ possibleError->setPendingDestructuringErrorAt(exprPos,
+ JSMSG_BAD_STRICT_ASSIGN_ARGUMENTS);
+ } else {
+ possibleError->setPendingDestructuringWarningAt(exprPos,
+ JSMSG_BAD_STRICT_ASSIGN_ARGUMENTS);
+ }
+ return;
+ }
+
+ if (handler.isEvalAnyParentheses(expr, context)) {
+ if (pc->sc()->strict()) {
+ possibleError->setPendingDestructuringErrorAt(exprPos,
+ JSMSG_BAD_STRICT_ASSIGN_EVAL);
+ } else {
+ possibleError->setPendingDestructuringWarningAt(exprPos,
+ JSMSG_BAD_STRICT_ASSIGN_EVAL);
+ }
+ return;
+ }
+ }
+
+ // The expression must be either a simple assignment target, i.e. a name
+ // or a property accessor, or a nested destructuring pattern.
+ if (!handler.isUnparenthesizedDestructuringPattern(expr) &&
+ !handler.isNameAnyParentheses(expr) &&
+ !handler.isPropertyAccess(expr))
+ {
+ // Parentheses are forbidden around destructuring *patterns* (but
+ // allowed around names). Use our nicer error message for
+ // parenthesized, nested patterns.
+ if (handler.isParenthesizedDestructuringPattern(expr))
+ possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_PARENS);
+ else
+ possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET);
+ }
+}
+
+template <typename ParseHandler>
+void
+Parser<ParseHandler>::checkDestructuringAssignmentElement(Node expr, TokenPos exprPos,
+ PossibleError* possibleError)
+{
+ // ES2018 draft rev 0719f44aab93215ed9a626b2f45bd34f36916834
+ // 12.15.5 Destructuring Assignment
+ //
+ // AssignmentElement[Yield, Await]:
+ // DestructuringAssignmentTarget[?Yield, ?Await]
+ // DestructuringAssignmentTarget[?Yield, ?Await] Initializer[+In, ?Yield, ?Await]
+
+ // If |expr| is an assignment element with an initializer expression, its
+ // destructuring assignment target was already validated in assignExpr().
+ // Otherwise we need to check that |expr| is a valid destructuring target.
+ if (!handler.isUnparenthesizedAssignment(expr))
+ checkDestructuringAssignmentTarget(expr, exprPos, possibleError);
+}
+
+template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::arrayInitializer(YieldHandling yieldHandling, PossibleError* possibleError)
{
@@ -8753,17 +9422,29 @@ Parser<ParseHandler>::arrayInitializer(YieldHandling yieldHandling, PossibleErro
} else if (tt == TOK_TRIPLEDOT) {
tokenStream.consumeKnownToken(TOK_TRIPLEDOT, TokenStream::Operand);
uint32_t begin = pos().begin;
+
+ TokenPos innerPos;
+ if (!tokenStream.peekTokenPos(&innerPos, TokenStream::Operand))
+ return null();
+
Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
possibleError);
if (!inner)
return null();
+ if (possibleError)
+ checkDestructuringAssignmentTarget(inner, innerPos, possibleError);
if (!handler.addSpreadElement(literal, begin, inner))
return null();
} else {
+ TokenPos elementPos;
+ if (!tokenStream.peekTokenPos(&elementPos, TokenStream::Operand))
+ return null();
Node element = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
possibleError);
if (!element)
return null();
+ if (possibleError)
+ checkDestructuringAssignmentElement(element, elementPos, possibleError);
if (foldConstants && !FoldConstants(context, &element, this))
return null();
handler.addArrayElement(literal, element);
@@ -8783,7 +9464,9 @@ Parser<ParseHandler>::arrayInitializer(YieldHandling yieldHandling, PossibleErro
}
}
- MUST_MATCH_TOKEN_MOD(TOK_RB, modifier, JSMSG_BRACKET_AFTER_LIST);
+ MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, modifier,
+ reportMissingClosing(JSMSG_BRACKET_AFTER_LIST,
+ JSMSG_BRACKET_OPENED, begin));
}
handler.setEndPosition(literal, pos().end);
return literal;
@@ -8799,11 +9482,12 @@ DoubleToAtom(ExclusiveContext* cx, double value)
template <typename ParseHandler>
typename ParseHandler::Node
-Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
+Parser<ParseHandler>::propertyName(YieldHandling yieldHandling,
+ const Maybe<DeclarationKind>& maybeDecl, Node propList,
PropertyType* propType, MutableHandleAtom propAtom)
{
TokenKind ltok;
- if (!tokenStream.getToken(&ltok, TokenStream::KeywordIsName))
+ if (!tokenStream.getToken(&ltok))
return null();
MOZ_ASSERT(ltok != TOK_RC, "caller should have handled TOK_RC");
@@ -8812,11 +9496,11 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
bool isAsync = false;
if (ltok == TOK_MUL) {
isGenerator = true;
- if (!tokenStream.getToken(&ltok, TokenStream::KeywordIsName))
+ if (!tokenStream.getToken(&ltok))
return null();
}
- if (ltok == TOK_NAME && tokenStream.currentName() == context->names().async) {
+ if (ltok == TOK_ASYNC) {
// AsyncMethod[Yield, Await]:
// async [no LineTerminator here] PropertyName[?Yield, ?Await] ...
//
@@ -8832,16 +9516,13 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
// ComputedPropertyName[Yield, Await]:
// [ ...
TokenKind tt = TOK_EOF;
- if (!tokenStream.peekTokenSameLine(&tt, TokenStream::KeywordIsName))
+ if (!tokenStream.getToken(&tt))
return null();
- if (tt == TOK_STRING || tt == TOK_NUMBER || tt == TOK_LB ||
- tt == TOK_NAME || tt == TOK_YIELD)
- {
+ if (tt != TOK_LP && tt != TOK_COLON && tt != TOK_RC && tt != TOK_ASSIGN) {
isAsync = true;
- tokenStream.consumeKnownToken(tt, TokenStream::KeywordIsName);
ltok = tt;
} else {
- tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName);
+ tokenStream.ungetToken();
}
}
@@ -8863,46 +9544,41 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
break;
case TOK_LB:
- propName = computedPropertyName(yieldHandling, propList);
+ propName = computedPropertyName(yieldHandling, maybeDecl, propList);
if (!propName)
return null();
break;
- case TOK_NAME: {
+ default: {
+ if (!TokenKindIsPossibleIdentifierName(ltok)) {
+ error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(ltok));
+ return null();
+ }
+
propAtom.set(tokenStream.currentName());
// Do not look for accessor syntax on generators
- if (isGenerator || isAsync ||
- !(propAtom.get() == context->names().get ||
- propAtom.get() == context->names().set))
- {
+ if (isGenerator || isAsync || !(ltok == TOK_GET || ltok == TOK_SET)) {
propName = handler.newObjectLiteralPropertyName(propAtom, pos());
if (!propName)
return null();
break;
}
- *propType = propAtom.get() == context->names().get ? PropertyType::Getter
- : PropertyType::Setter;
+ *propType = ltok == TOK_GET ? PropertyType::Getter : PropertyType::Setter;
// We have parsed |get| or |set|. Look for an accessor property
// name next.
TokenKind tt;
- if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName))
+ if (!tokenStream.peekToken(&tt))
return null();
- if (tt == TOK_NAME) {
- if (!checkUnescapedName())
- return null();
-
- tokenStream.consumeKnownToken(TOK_NAME, TokenStream::KeywordIsName);
+ if (TokenKindIsPossibleIdentifierName(tt)) {
+ tokenStream.consumeKnownToken(tt);
propAtom.set(tokenStream.currentName());
return handler.newObjectLiteralPropertyName(propAtom, pos());
}
if (tt == TOK_STRING) {
- if (!checkUnescapedName())
- return null();
-
- tokenStream.consumeKnownToken(TOK_STRING, TokenStream::KeywordIsName);
+ tokenStream.consumeKnownToken(TOK_STRING);
propAtom.set(tokenStream.currentToken().atom());
@@ -8916,10 +9592,7 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
return stringLiteral();
}
if (tt == TOK_NUMBER) {
- if (!checkUnescapedName())
- return null();
-
- tokenStream.consumeKnownToken(TOK_NUMBER, TokenStream::KeywordIsName);
+ tokenStream.consumeKnownToken(TOK_NUMBER);
propAtom.set(DoubleToAtom(context, tokenStream.currentToken().number()));
if (!propAtom.get())
@@ -8927,19 +9600,15 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
return newNumber(tokenStream.currentToken());
}
if (tt == TOK_LB) {
- if (!checkUnescapedName())
- return null();
+ tokenStream.consumeKnownToken(TOK_LB);
- tokenStream.consumeKnownToken(TOK_LB, TokenStream::KeywordIsName);
-
- return computedPropertyName(yieldHandling, propList);
+ return computedPropertyName(yieldHandling, maybeDecl, propList);
}
// Not an accessor property after all.
propName = handler.newObjectLiteralPropertyName(propAtom.get(), pos());
if (!propName)
return null();
- tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName);
break;
}
@@ -8957,10 +9626,6 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
return null();
break;
}
-
- default:
- error(JSMSG_BAD_PROP_ID);
- return null();
}
TokenKind tt;
@@ -8976,7 +9641,9 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
return propName;
}
- if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC || tt == TOK_ASSIGN)) {
+ if (TokenKindIsPossibleIdentifierName(ltok) &&
+ (tt == TOK_COMMA || tt == TOK_RC || tt == TOK_ASSIGN))
+ {
if (isGenerator) {
error(JSMSG_BAD_PROP_ID);
return null();
@@ -9005,28 +9672,25 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
template <typename ParseHandler>
typename ParseHandler::Node
-Parser<ParseHandler>::computedPropertyName(YieldHandling yieldHandling, Node literal)
+Parser<ParseHandler>::computedPropertyName(YieldHandling yieldHandling,
+ const Maybe<DeclarationKind>& maybeDecl,
+ Node literal)
{
uint32_t begin = pos().begin;
- Node assignNode;
- {
- // Turn off the inDestructuringDecl flag when parsing computed property
- // names. In short, when parsing 'let {[x + y]: z} = obj;', noteUsedName()
- // should be called on x and y, but not on z. See the comment on
- // Parser<>::checkDestructuringPattern() for details.
- AutoClearInDestructuringDecl autoClear(pc);
- assignNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
- if (!assignNode)
- return null();
+ if (maybeDecl) {
+ if (*maybeDecl == DeclarationKind::FormalParameter)
+ pc->functionBox()->hasParameterExprs = true;
+ } else {
+ handler.setListFlag(literal, PNX_NONCONST);
}
- MUST_MATCH_TOKEN(TOK_RB, JSMSG_COMP_PROP_UNTERM_EXPR);
- Node propname = handler.newComputedName(assignNode, begin, pos().end);
- if (!propname)
+ Node assignNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
+ if (!assignNode)
return null();
- handler.setListFlag(literal, PNX_NONCONST);
- return propname;
+
+ MUST_MATCH_TOKEN(TOK_RB, JSMSG_COMP_PROP_UNTERM_EXPR);
+ return handler.newComputedName(assignNode, begin, pos().end);
}
template <typename ParseHandler>
@@ -9035,200 +9699,219 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
+ uint32_t openedPos = pos().begin;
+
Node literal = handler.newObjectLiteral(pos().begin);
if (!literal)
return null();
bool seenPrototypeMutation = false;
bool seenCoverInitializedName = false;
+ Maybe<DeclarationKind> declKind = Nothing();
RootedAtom propAtom(context);
for (;;) {
TokenKind tt;
- if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
+ if (!tokenStream.peekToken(&tt))
return null();
if (tt == TOK_RC)
break;
- TokenPos namePos = pos();
-
- tokenStream.ungetToken();
-
- PropertyType propType;
- Node propName = propertyName(yieldHandling, literal, &propType, &propAtom);
- if (!propName)
- return null();
+ if (tt == TOK_TRIPLEDOT) {
+ // object spread
+ tokenStream.consumeKnownToken(TOK_TRIPLEDOT);
+ uint32_t begin = pos().begin;
- if (propType == PropertyType::Normal) {
- Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
- possibleError);
- if (!propExpr)
+ TokenPos innerPos;
+ if (!tokenStream.peekTokenPos(&innerPos, TokenStream::Operand))
return null();
- handler.checkAndSetIsDirectRHSAnonFunction(propExpr);
-
- if (foldConstants && !FoldConstants(context, &propExpr, this))
+ Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
+ possibleError);
+ if (!inner)
+ return null();
+ if (possibleError)
+ checkDestructuringAssignmentTarget(inner, innerPos, possibleError);
+ if (!handler.addSpreadProperty(literal, begin, inner))
return null();
+ } else {
+ TokenPos namePos = tokenStream.nextToken().pos;
- if (propAtom == context->names().proto) {
- if (seenPrototypeMutation) {
- // Directly report the error when we're not in a
- // destructuring context.
- if (!possibleError) {
- errorAt(namePos.begin, JSMSG_DUPLICATE_PROTO_PROPERTY);
- return null();
- }
+ PropertyType propType;
+ Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom);
+ if (!propName)
- // Otherwise delay error reporting until we've determined
- // whether or not we're destructuring.
- possibleError->setPendingExpressionErrorAt(namePos,
- JSMSG_DUPLICATE_PROTO_PROPERTY);
- }
- seenPrototypeMutation = true;
+ return null();
- // Note: this occurs *only* if we observe TOK_COLON! Only
- // __proto__: v mutates [[Prototype]]. Getters, setters,
- // method/generator definitions, computed property name
- // versions of all of these, and shorthands do not.
- if (!handler.addPrototypeMutation(literal, namePos.begin, propExpr))
+ if (propType == PropertyType::Normal) {
+ TokenPos exprPos;
+ if (!tokenStream.peekTokenPos(&exprPos, TokenStream::Operand))
return null();
- } else {
- if (!handler.isConstant(propExpr))
- handler.setListFlag(literal, PNX_NONCONST);
- if (!handler.addPropertyDefinition(literal, propName, propExpr))
+ Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
+ possibleError);
+ if (!propExpr)
return null();
- }
- } else if (propType == PropertyType::Shorthand) {
- /*
- * Support, e.g., |var {x, y} = o| as destructuring shorthand
- * for |var {x: x, y: y} = o|, and |var o = {x, y}| as initializer
- * shorthand for |var o = {x: x, y: y}|.
- */
- TokenKind propToken = TOK_NAME;
- if (!tokenStream.checkForKeyword(propAtom, &propToken))
- return null();
- if (propToken != TOK_NAME && propToken != TOK_YIELD) {
- error(JSMSG_RESERVED_ID, TokenKindToDesc(propToken));
- return null();
- }
+ handler.checkAndSetIsDirectRHSAnonFunction(propExpr);
- Rooted<PropertyName*> name(context,
- identifierReference(yieldHandling, propToken == TOK_YIELD));
- if (!name)
- return null();
+ if (possibleError)
+ checkDestructuringAssignmentElement(propExpr, exprPos, possibleError);
- Node nameExpr = identifierReference(name);
- if (!nameExpr)
- return null();
+ if (foldConstants && !FoldConstants(context, &propExpr, this))
+ return null();
- if (!handler.addShorthand(literal, propName, nameExpr))
- return null();
- } else if (propType == PropertyType::CoverInitializedName) {
- /*
- * Support, e.g., |var {x=1, y=2} = o| as destructuring shorthand
- * with default values, as per ES6 12.14.5
- */
- TokenKind propToken = TOK_NAME;
- if (!tokenStream.checkForKeyword(propAtom, &propToken))
- return null();
+ if (propAtom == context->names().proto) {
+ if (seenPrototypeMutation) {
+ // Directly report the error when we're not in a
+ // destructuring context.
+ if (!possibleError) {
+ errorAt(namePos.begin, JSMSG_DUPLICATE_PROTO_PROPERTY);
+ return null();
+ }
- if (propToken != TOK_NAME && propToken != TOK_YIELD) {
- error(JSMSG_RESERVED_ID, TokenKindToDesc(propToken));
- return null();
- }
+ // Otherwise delay error reporting until we've
+ // determined whether or not we're destructuring.
+ possibleError->setPendingExpressionErrorAt(namePos,
+ JSMSG_DUPLICATE_PROTO_PROPERTY);
+ }
+ seenPrototypeMutation = true;
- Rooted<PropertyName*> name(context,
- identifierReference(yieldHandling, propToken == TOK_YIELD));
- if (!name)
- return null();
+ // Note: this occurs *only* if we observe TOK_COLON! Only
+ // __proto__: v mutates [[Prototype]]. Getters, setters,
+ // method/generator definitions, computed property name
+ // versions of all of these, and shorthands do not.
+ if (!handler.addPrototypeMutation(literal, namePos.begin, propExpr))
+ return null();
+ } else {
+ if (!handler.isConstant(propExpr))
+ handler.setListFlag(literal, PNX_NONCONST);
- Node lhs = identifierReference(name);
- if (!lhs)
- return null();
+ if (!handler.addPropertyDefinition(literal, propName, propExpr))
+ return null();
+ }
+ } else if (propType == PropertyType::Shorthand) {
+ /*
+ * Support, e.g., |({x, y} = o)| as destructuring shorthand
+ * for |({x: x, y: y} = o)|, and |var o = {x, y}| as
+ * initializer shorthand for |var o = {x: x, y: y}|.
+ */
+ Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
+ if (!name)
+ return null();
- tokenStream.consumeKnownToken(TOK_ASSIGN);
+ Node nameExpr = identifierReference(name);
+ if (!nameExpr)
+ return null();
- if (!seenCoverInitializedName) {
- // "shorthand default" or "CoverInitializedName" syntax is only
- // valid in the case of destructuring.
- seenCoverInitializedName = true;
+ if (possibleError)
+ checkDestructuringAssignmentTarget(nameExpr, namePos, possibleError);
- if (!possibleError) {
- // Destructuring defaults are definitely not allowed in this object literal,
- // because of something the caller knows about the preceding code.
- // For example, maybe the preceding token is an operator: `x + {y=z}`.
- error(JSMSG_COLON_AFTER_ID);
+ if (!handler.addShorthand(literal, propName, nameExpr))
return null();
+ } else if (propType == PropertyType::CoverInitializedName) {
+ /*
+ * Support, e.g., |({x=1, y=2} = o)| as destructuring
+ * shorthand with default values, as per ES6 12.14.5
+ */
+ Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
+ if (!name)
+ return null();
+
+ Node lhs = identifierReference(name);
+ if (!lhs)
+ return null();
+
+ tokenStream.consumeKnownToken(TOK_ASSIGN);
+
+ if (!seenCoverInitializedName) {
+ // "shorthand default" or "CoverInitializedName" syntax is
+ // only valid in the case of destructuring.
+ seenCoverInitializedName = true;
+
+ if (!possibleError) {
+ // Destructuring defaults are definitely not allowed
+ // in this object literal, because of something the
+ // caller knows about the preceding code. For example,
+ // maybe the preceding token is an operator:
+ // |x + {y=z}|.
+ error(JSMSG_COLON_AFTER_ID);
+ return null();
+ }
+
+ // Here we set a pending error so that later in the parse,
+ // once we've determined whether or not we're
+ // destructuring, the error can be reported or ignored
+ // appropriately.
+ possibleError->setPendingExpressionErrorAt(pos(), JSMSG_COLON_AFTER_ID);
}
- // Here we set a pending error so that later in the parse, once we've
- // determined whether or not we're destructuring, the error can be
- // reported or ignored appropriately.
- possibleError->setPendingExpressionErrorAt(pos(), JSMSG_COLON_AFTER_ID);
- }
+ if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(lhs, context)) {
+ // |chars| is "arguments" or "eval" here.
+ if (!strictModeErrorAt(namePos.begin, JSMSG_BAD_STRICT_ASSIGN, chars))
+ return null();
+ }
- Node rhs;
- {
- // Clearing `inDestructuringDecl` allows name use to be noted
- // in Parser::identifierReference. See bug 1255167.
- AutoClearInDestructuringDecl autoClear(pc);
- rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
+ Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
if (!rhs)
return null();
- }
- handler.checkAndSetIsDirectRHSAnonFunction(rhs);
+ handler.checkAndSetIsDirectRHSAnonFunction(rhs);
- Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP);
- if (!propExpr)
- return null();
-
- if (!handler.addPropertyDefinition(literal, propName, propExpr))
- return null();
+ Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP);
+ if (!propExpr)
+ return null();
- if (!abortIfSyntaxParser())
- return null();
- } else {
- RootedAtom funName(context);
- if (!tokenStream.isCurrentTokenType(TOK_RB)) {
- funName = propAtom;
+ if (!handler.addPropertyDefinition(literal, propName, propExpr))
+ return null();
+ } else {
+ RootedAtom funName(context);
+ if (!tokenStream.isCurrentTokenType(TOK_RB)) {
+ funName = propAtom;
- if (propType == PropertyType::Getter || propType == PropertyType::Setter) {
- funName = prefixAccessorName(propType, propAtom);
- if (!funName)
- return null();
+ if (propType == PropertyType::Getter || propType == PropertyType::Setter) {
+ funName = prefixAccessorName(propType, propAtom);
+ if (!funName)
+ return null();
+ }
}
- }
- Node fn = methodDefinition(namePos.begin, propType, funName);
- if (!fn)
- return null();
+ Node fn = methodDefinition(namePos.begin, propType, funName);
+ if (!fn)
+ return null();
- handler.checkAndSetIsDirectRHSAnonFunction(fn);
+ handler.checkAndSetIsDirectRHSAnonFunction(fn);
- JSOp op = JSOpFromPropertyType(propType);
- if (!handler.addObjectMethodDefinition(literal, propName, fn, op))
- return null();
+ JSOp op = JSOpFromPropertyType(propType);
+ if (!handler.addObjectMethodDefinition(literal, propName, fn, op))
+ return null();
+
+ if (possibleError) {
+ possibleError->setPendingDestructuringErrorAt(namePos,
+ JSMSG_BAD_DESTRUCT_TARGET);
+ }
+ }
}
- if (!tokenStream.getToken(&tt))
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_COMMA))
return null();
- if (tt == TOK_RC)
+ if (!matched)
break;
- if (tt != TOK_COMMA) {
- error(JSMSG_CURLY_AFTER_LIST);
- return null();
- }
+ if (tt == TOK_TRIPLEDOT && possibleError)
+ possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA);
}
+ MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None,
+ reportMissingClosing(JSMSG_CURLY_AFTER_LIST,
+ JSMSG_CURLY_OPENED, openedPos));
+
handler.setEndPosition(literal, pos().end);
return literal;
}
template <typename ParseHandler>
typename ParseHandler::Node
-Parser<ParseHandler>::methodDefinition(uint32_t preludeStart, PropertyType propType,
+Parser<ParseHandler>::methodDefinition(uint32_t toStringStart, PropertyType propType,
HandleAtom funName)
{
FunctionSyntaxKind kind;
@@ -9282,7 +9965,7 @@ Parser<ParseHandler>::methodDefinition(uint32_t preludeStart, PropertyType propT
if (!pn)
return null();
- return functionDefinition(preludeStart, pn, InAllowed, yieldHandling, funName,
+ return functionDefinition(toStringStart, pn, InAllowed, yieldHandling, funName,
kind, generatorKind, asyncKind);
}
@@ -9312,14 +9995,11 @@ Parser<ParseHandler>::tryNewTarget(Node &newTarget)
if (!tokenStream.getToken(&next))
return false;
- if (next != TOK_NAME || tokenStream.currentName() != context->names().target) {
+ if (next != TOK_TARGET) {
error(JSMSG_UNEXPECTED_TOKEN, "target", TokenKindToDesc(next));
return false;
}
- if (!checkUnescapedName())
- return false;
-
if (!pc->sc()->allowNewTarget()) {
errorAt(begin, JSMSG_BAD_NEWTARGET);
return false;
@@ -9389,7 +10069,6 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
if (!expr)
return null();
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
- handler.setEndPosition(expr, pos().end);
return handler.parenthesize(expr);
}
@@ -9397,22 +10076,26 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
return templateLiteral(yieldHandling);
case TOK_NO_SUBS_TEMPLATE:
- return noSubstitutionTemplate();
+ return noSubstitutionUntaggedTemplate();
case TOK_STRING:
return stringLiteral();
- case TOK_YIELD:
- case TOK_NAME: {
- if (tokenStream.currentName() == context->names().async) {
+ default: {
+ if (!TokenKindIsPossibleIdentifier(tt)) {
+ error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt));
+ return null();
+ }
+
+ if (tt == TOK_ASYNC) {
TokenKind nextSameLine = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&nextSameLine))
return null();
if (nextSameLine == TOK_FUNCTION) {
- uint32_t preludeStart = pos().begin;
+ uint32_t toStringStart = pos().begin;
tokenStream.consumeKnownToken(TOK_FUNCTION);
- return functionExpr(preludeStart, PredictUninvoked, AsyncFunction);
+ return functionExpr(toStringStart, PredictUninvoked, AsyncFunction);
}
}
@@ -9476,7 +10159,7 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
// the enclosing code is strict mode code, any of "let", "yield",
// or "arguments" should be prohibited. Argument-parsing code
// handles that.
- if (next != TOK_NAME && next != TOK_YIELD) {
+ if (!TokenKindIsPossibleIdentifier(next)) {
error(JSMSG_UNEXPECTED_TOKEN, "rest argument name", TokenKindToDesc(next));
return null();
}
@@ -9503,10 +10186,6 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
// Return an arbitrary expression node. See case TOK_RP above.
return handler.newNullLiteral(pos());
}
-
- default:
- error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt));
- return null();
}
}
@@ -9520,9 +10199,8 @@ Parser<ParseHandler>::exprInParens(InHandling inHandling, YieldHandling yieldHan
return expr(inHandling, yieldHandling, tripledotHandling, possibleError, PredictInvoked);
}
-template <typename ParseHandler>
bool
-Parser<ParseHandler>::warnOnceAboutExprClosure()
+ParserBase::warnOnceAboutExprClosure()
{
#ifndef RELEASE_OR_BETA
JSContext* cx = context->maybeJSContext();
@@ -9538,9 +10216,8 @@ Parser<ParseHandler>::warnOnceAboutExprClosure()
return true;
}
-template <typename ParseHandler>
bool
-Parser<ParseHandler>::warnOnceAboutForEach()
+ParserBase::warnOnceAboutForEach()
{
JSContext* cx = context->maybeJSContext();
if (!cx)
diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h
index 156a1c1b0..88d2dad18 100644
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -85,6 +85,16 @@ class ParseContext : public Nestable<ParseContext>
}
};
+ struct ClassStatement : public Statement
+ {
+ FunctionBox* constructorBox;
+
+ explicit ClassStatement(ParseContext* pc)
+ : Statement(pc, StatementKind::Class),
+ constructorBox(nullptr)
+ { }
+ };
+
// The intra-function scope stack.
//
// Tracks declared and used names within a scope.
@@ -146,9 +156,9 @@ class ParseContext : public Nestable<ParseContext>
}
MOZ_MUST_USE bool addDeclaredName(ParseContext* pc, AddDeclaredNamePtr& p, JSAtom* name,
- DeclarationKind kind)
+ DeclarationKind kind, uint32_t pos)
{
- return maybeReportOOM(pc, declared_->add(p, name, DeclaredNameInfo(kind)));
+ return maybeReportOOM(pc, declared_->add(p, name, DeclaredNameInfo(kind, pos)));
}
// Remove all VarForAnnexBLexicalFunction declarations of a certain
@@ -330,17 +340,6 @@ class ParseContext : public Nestable<ParseContext>
// pointer may be nullptr.
Directives* newDirectives;
- // Set when parsing a declaration-like destructuring pattern. This flag
- // causes PrimaryExpr to create PN_NAME parse nodes for variable references
- // which are not hooked into any definition's use chain, added to any tree
- // context's AtomList, etc. etc. checkDestructuring will do that work
- // later.
- //
- // The comments atop checkDestructuring explain the distinction between
- // assignment-like and declaration-like destructuring patterns, and why
- // they need to be treated differently.
- mozilla::Maybe<DeclarationKind> inDestructuringDecl;
-
// Set when parsing a function and it has 'return <expr>;'
bool funHasReturnExpr;
@@ -432,6 +431,11 @@ class ParseContext : public Nestable<ParseContext>
return Statement::findNearest<T>(innermostStatement_, predicate);
}
+ template <typename T>
+ T* findInnermostStatement() {
+ return Statement::findNearest<T>(innermostStatement_);
+ }
+
AtomVector& positionalFormalParameterNames() {
return *positionalFormalParameterNames_;
}
@@ -532,6 +536,13 @@ ParseContext::Statement::is<ParseContext::LabelStatement>() const
return kind_ == StatementKind::Label;
}
+template <>
+inline bool
+ParseContext::Statement::is<ParseContext::ClassStatement>() const
+{
+ return kind_ == StatementKind::Class;
+}
+
template <typename T>
inline T&
ParseContext::Statement::as()
@@ -735,7 +746,155 @@ class UsedNameTracker
};
template <typename ParseHandler>
-class Parser final : private JS::AutoGCRooter, public StrictModeGetter
+class AutoAwaitIsKeyword;
+
+class ParserBase : public StrictModeGetter
+{
+ private:
+ ParserBase* thisForCtor() { return this; }
+
+ public:
+ ExclusiveContext* const context;
+
+ LifoAlloc& alloc;
+
+ TokenStream tokenStream;
+ LifoAlloc::Mark tempPoolMark;
+
+ /* list of parsed objects for GC tracing */
+ ObjectBox* traceListHead;
+
+ /* innermost parse context (stack-allocated) */
+ ParseContext* pc;
+
+ // For tracking used names in this parsing session.
+ UsedNameTracker& usedNames;
+
+ /* Compression token for aborting. */
+ SourceCompressionTask* sct;
+
+ ScriptSource* ss;
+
+ /* Root atoms and objects allocated for the parsed tree. */
+ AutoKeepAtoms keepAtoms;
+
+ /* Perform constant-folding; must be true when interfacing with the emitter. */
+ const bool foldConstants:1;
+
+ protected:
+#if DEBUG
+ /* Our fallible 'checkOptions' member function has been called. */
+ bool checkOptionsCalled:1;
+#endif
+
+ /*
+ * Not all language constructs can be handled during syntax parsing. If it
+ * is not known whether the parse succeeds or fails, this bit is set and
+ * the parse will return false.
+ */
+ bool abortedSyntaxParse:1;
+
+ /* Unexpected end of input, i.e. TOK_EOF not at top-level. */
+ bool isUnexpectedEOF_:1;
+
+ bool awaitIsKeyword_:1;
+
+ public:
+ bool awaitIsKeyword() const {
+ return awaitIsKeyword_;
+ }
+
+ ParserBase(ExclusiveContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
+ const char16_t* chars, size_t length, bool foldConstants,
+ UsedNameTracker& usedNames, Parser<SyntaxParseHandler>* syntaxParser,
+ LazyScript* lazyOuterFunction);
+ ~ParserBase();
+
+ const char* getFilename() const { return tokenStream.getFilename(); }
+ JSVersion versionNumber() const { return tokenStream.versionNumber(); }
+ TokenPos pos() const { return tokenStream.currentToken().pos; }
+
+ // Determine whether |yield| is a valid name in the current context, or
+ // whether it's prohibited due to strictness, JS version, or occurrence
+ // inside a star generator.
+ bool yieldExpressionsSupported() {
+ return (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()) && !pc->isAsync();
+ }
+
+ virtual bool strictMode() { return pc->sc()->strict(); }
+ bool setLocalStrictMode(bool strict) {
+ MOZ_ASSERT(tokenStream.debugHasNoLookahead());
+ return pc->sc()->setLocalStrictMode(strict);
+ }
+
+ const ReadOnlyCompileOptions& options() const {
+ return tokenStream.options();
+ }
+
+ bool hadAbortedSyntaxParse() {
+ return abortedSyntaxParse;
+ }
+ void clearAbortedSyntaxParse() {
+ abortedSyntaxParse = false;
+ }
+
+ bool isUnexpectedEOF() const { return isUnexpectedEOF_; }
+
+ bool reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...);
+
+ /* Report the given error at the current offset. */
+ void error(unsigned errorNumber, ...);
+ void errorWithNotes(UniquePtr<JSErrorNotes> notes, unsigned errorNumber, ...);
+
+ /* Report the given error at the given offset. */
+ void errorAt(uint32_t offset, unsigned errorNumber, ...);
+ void errorWithNotesAt(UniquePtr<JSErrorNotes> notes, uint32_t offset,
+ unsigned errorNumber, ...);
+
+ /*
+ * Handle a strict mode error at the current offset. Report an error if in
+ * strict mode code, or warn if not, using the given error number and
+ * arguments.
+ */
+ MOZ_MUST_USE bool strictModeError(unsigned errorNumber, ...);
+
+ /*
+ * Handle a strict mode error at the given offset. Report an error if in
+ * strict mode code, or warn if not, using the given error number and
+ * arguments.
+ */
+ MOZ_MUST_USE bool strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...);
+
+ /* Report the given warning at the current offset. */
+ MOZ_MUST_USE bool warning(unsigned errorNumber, ...);
+
+ /* Report the given warning at the given offset. */
+ MOZ_MUST_USE bool warningAt(uint32_t offset, unsigned errorNumber, ...);
+
+ /*
+ * If extra warnings are enabled, report the given warning at the current
+ * offset.
+ */
+ MOZ_MUST_USE bool extraWarning(unsigned errorNumber, ...);
+
+ /*
+ * If extra warnings are enabled, report the given warning at the given
+ * offset.
+ */
+ MOZ_MUST_USE bool extraWarningAt(uint32_t offset, unsigned errorNumber, ...);
+
+ bool isValidStrictBinding(PropertyName* name);
+
+ bool warnOnceAboutExprClosure();
+ bool warnOnceAboutForEach();
+
+ protected:
+ enum InvokedPrediction { PredictUninvoked = false, PredictInvoked = true };
+ enum ForInitLocation { InForInit, NotInForInit };
+};
+
+template <typename ParseHandler>
+class Parser final : public ParserBase, private JS::AutoGCRooter
{
private:
using Node = typename ParseHandler::Node;
@@ -788,7 +947,7 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
class MOZ_STACK_CLASS PossibleError
{
private:
- enum class ErrorKind { Expression, Destructuring };
+ enum class ErrorKind { Expression, Destructuring, DestructuringWarning };
enum class ErrorState { None, Pending };
@@ -803,11 +962,12 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
Parser<ParseHandler>& parser_;
Error exprError_;
Error destructuringError_;
+ Error destructuringWarning_;
// Returns the error report.
Error& error(ErrorKind kind);
- // Return true if an error is pending without reporting
+ // Return true if an error is pending without reporting.
bool hasError(ErrorKind kind);
// Resolve any pending error.
@@ -819,7 +979,11 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
// If there is a pending error, report it and return false, otherwise
// return true.
- bool checkForError(ErrorKind kind);
+ MOZ_MUST_USE bool checkForError(ErrorKind kind);
+
+ // If there is a pending warning, report it and return either false or
+ // true depending on the werror option, otherwise return true.
+ MOZ_MUST_USE bool checkForWarning(ErrorKind kind);
// Transfer an existing error to another instance.
void transferErrorTo(ErrorKind kind, PossibleError* other);
@@ -827,23 +991,33 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
public:
explicit PossibleError(Parser<ParseHandler>& parser);
+ // Return true if a pending destructuring error is present.
+ bool hasPendingDestructuringError();
+
// Set a pending destructuring error. Only a single error may be set
// per instance, i.e. subsequent calls to this method are ignored and
// won't overwrite the existing pending error.
void setPendingDestructuringErrorAt(const TokenPos& pos, unsigned errorNumber);
+ // Set a pending destructuring warning. Only a single warning may be
+ // set per instance, i.e. subsequent calls to this method are ignored
+ // and won't overwrite the existing pending warning.
+ void setPendingDestructuringWarningAt(const TokenPos& pos, unsigned errorNumber);
+
// Set a pending expression error. Only a single error may be set per
// instance, i.e. subsequent calls to this method are ignored and won't
// overwrite the existing pending error.
void setPendingExpressionErrorAt(const TokenPos& pos, unsigned errorNumber);
- // If there is a pending destructuring error, report it and return
- // false, otherwise return true. Clears any pending expression error.
- bool checkForDestructuringError();
+ // If there is a pending destructuring error or warning, report it and
+ // return false, otherwise return true. Clears any pending expression
+ // error.
+ MOZ_MUST_USE bool checkForDestructuringErrorOrWarning();
// If there is a pending expression error, report it and return false,
- // otherwise return true. Clears any pending destructuring error.
- bool checkForExpressionError();
+ // otherwise return true. Clears any pending destructuring error or
+ // warning.
+ MOZ_MUST_USE bool checkForExpressionError();
// Pass pending errors between possible error instances. This is useful
// for extending the lifetime of a pending error beyond the scope of
@@ -853,50 +1027,6 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
};
public:
- ExclusiveContext* const context;
-
- LifoAlloc& alloc;
-
- TokenStream tokenStream;
- LifoAlloc::Mark tempPoolMark;
-
- /* list of parsed objects for GC tracing */
- ObjectBox* traceListHead;
-
- /* innermost parse context (stack-allocated) */
- ParseContext* pc;
-
- // For tracking used names in this parsing session.
- UsedNameTracker& usedNames;
-
- /* Compression token for aborting. */
- SourceCompressionTask* sct;
-
- ScriptSource* ss;
-
- /* Root atoms and objects allocated for the parsed tree. */
- AutoKeepAtoms keepAtoms;
-
- /* Perform constant-folding; must be true when interfacing with the emitter. */
- const bool foldConstants:1;
-
- private:
-#if DEBUG
- /* Our fallible 'checkOptions' member function has been called. */
- bool checkOptionsCalled:1;
-#endif
-
- /*
- * Not all language constructs can be handled during syntax parsing. If it
- * is not known whether the parse succeeds or fails, this bit is set and
- * the parse will return false.
- */
- bool abortedSyntaxParse:1;
-
- /* Unexpected end of input, i.e. TOK_EOF not at top-level. */
- bool isUnexpectedEOF_:1;
-
- public:
/* State specific to the kind of parse being performed. */
ParseHandler handler;
@@ -904,45 +1034,14 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
void freeTree(Node node) { handler.freeTree(node); }
public:
- bool reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...);
-
- /* Report the given error at the current offset. */
- void error(unsigned errorNumber, ...);
-
- /* Report the given error at the given offset. */
- void errorAt(uint32_t offset, unsigned errorNumber, ...);
-
- /*
- * Handle a strict mode error at the current offset. Report an error if in
- * strict mode code, or warn if not, using the given error number and
- * arguments.
- */
- MOZ_MUST_USE bool strictModeError(unsigned errorNumber, ...);
-
- /*
- * Handle a strict mode error at the given offset. Report an error if in
- * strict mode code, or warn if not, using the given error number and
- * arguments.
- */
- MOZ_MUST_USE bool strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...);
-
- /* Report the given warning at the current offset. */
- MOZ_MUST_USE bool warning(unsigned errorNumber, ...);
-
- /* Report the given warning at the given offset. */
- MOZ_MUST_USE bool warningAt(uint32_t offset, unsigned errorNumber, ...);
-
- /*
- * If extra warnings are enabled, report the given warning at the current
- * offset.
- */
- MOZ_MUST_USE bool extraWarning(unsigned errorNumber, ...);
-
Parser(ExclusiveContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
const char16_t* chars, size_t length, bool foldConstants, UsedNameTracker& usedNames,
Parser<SyntaxParseHandler>* syntaxParser, LazyScript* lazyOuterFunction);
~Parser();
+ friend class AutoAwaitIsKeyword<ParseHandler>;
+ void setAwaitIsKeyword(bool isKeyword);
+
bool checkOptions();
// A Parser::Mark is the extension of the LifoAlloc::Mark to the entire
@@ -967,9 +1066,6 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
friend void js::frontend::MarkParser(JSTracer* trc, JS::AutoGCRooter* parser);
- const char* getFilename() const { return tokenStream.getFilename(); }
- JSVersion versionNumber() const { return tokenStream.versionNumber(); }
-
/*
* Parse a top-level JS script.
*/
@@ -980,7 +1076,7 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
* cx->tempLifoAlloc.
*/
ObjectBox* newObjectBox(JSObject* obj);
- FunctionBox* newFunctionBox(Node fn, JSFunction* fun, uint32_t preludeStart,
+ FunctionBox* newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart,
Directives directives,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
bool tryAnnexB);
@@ -995,24 +1091,14 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
void trace(JSTracer* trc);
- bool hadAbortedSyntaxParse() {
- return abortedSyntaxParse;
- }
- void clearAbortedSyntaxParse() {
- abortedSyntaxParse = false;
- }
-
- bool isUnexpectedEOF() const { return isUnexpectedEOF_; }
-
- bool checkUnescapedName();
-
private:
Parser* thisForCtor() { return this; }
JSAtom* stopStringCompression();
Node stringLiteral();
- Node noSubstitutionTemplate();
+ Node noSubstitutionTaggedTemplate();
+ Node noSubstitutionUntaggedTemplate();
Node templateLiteral(YieldHandling yieldHandling);
bool taggedTemplate(YieldHandling yieldHandling, Node nodeList, TokenKind tt);
bool appendToCallSiteObj(Node callSiteObj);
@@ -1061,7 +1147,7 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
// Parse an inner function given an enclosing ParseContext and a
// FunctionBox for the inner function.
- bool innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox, uint32_t preludeStart,
+ bool innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox, uint32_t toStringStart,
InHandling inHandling, YieldHandling yieldHandling,
FunctionSyntaxKind kind,
Directives inheritedDirectives, Directives* newDirectives);
@@ -1070,35 +1156,14 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
// ParseContext is already on the stack.
bool functionFormalParametersAndBody(InHandling inHandling, YieldHandling yieldHandling,
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
- // inside a star generator.
- bool yieldExpressionsSupported() {
- return (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()) && !pc->isAsync();
- }
+ mozilla::Maybe<uint32_t> parameterListEnd = mozilla::Nothing(),
+ bool isStandaloneFunction = false);
// Match the current token against the BindingIdentifier production with
// the given Yield parameter. If there is no match, report a syntax
// error.
PropertyName* bindingIdentifier(YieldHandling yieldHandling);
- virtual bool strictMode() { return pc->sc()->strict(); }
- bool setLocalStrictMode(bool strict) {
- MOZ_ASSERT(tokenStream.debugHasNoLookahead());
- return pc->sc()->setLocalStrictMode(strict);
- }
-
- const ReadOnlyCompileOptions& options() const {
- return tokenStream.options();
- }
-
- private:
- enum InvokedPrediction { PredictUninvoked = false, PredictInvoked = true };
- enum ForInitLocation { InForInit, NotInForInit };
-
private:
/*
* JS parsers, from lowest to highest precedence.
@@ -1116,10 +1181,10 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
* Some parsers have two versions: an always-inlined version (with an 'i'
* suffix) and a never-inlined version (with an 'n' suffix).
*/
- Node functionStmt(uint32_t preludeStart,
+ Node functionStmt(uint32_t toStringStart,
YieldHandling yieldHandling, DefaultHandling defaultHandling,
FunctionAsyncKind asyncKind = SyncFunction);
- Node functionExpr(uint32_t preludeStart, InvokedPrediction invoked = PredictUninvoked,
+ Node functionExpr(uint32_t toStringStart, InvokedPrediction invoked = PredictUninvoked,
FunctionAsyncKind asyncKind = SyncFunction);
Node statementList(YieldHandling yieldHandling);
@@ -1160,9 +1225,27 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
// continues a LexicalDeclaration.
bool nextTokenContinuesLetDeclaration(TokenKind next, YieldHandling yieldHandling);
- Node lexicalDeclaration(YieldHandling yieldHandling, bool isConst);
+ Node lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind);
Node importDeclaration();
+
+ bool processExport(Node node);
+ bool processExportFrom(Node node);
+
+ Node exportFrom(uint32_t begin, Node specList);
+ Node exportBatch(uint32_t begin);
+ bool checkLocalExportNames(Node node);
+ Node exportClause(uint32_t begin);
+ Node exportFunctionDeclaration(uint32_t begin);
+ Node exportVariableStatement(uint32_t begin);
+ Node exportClassDeclaration(uint32_t begin);
+ Node exportLexicalDeclaration(uint32_t begin, DeclarationKind kind);
+ Node exportDefaultFunctionDeclaration(uint32_t begin,
+ FunctionAsyncKind asyncKind = SyncFunction);
+ Node exportDefaultClassDeclaration(uint32_t begin);
+ Node exportDefaultAssignExpr(uint32_t begin);
+ Node exportDefault(uint32_t begin);
+
Node exportDeclaration();
Node expressionStatement(YieldHandling yieldHandling,
InvokedPrediction invoked = PredictUninvoked);
@@ -1250,7 +1333,7 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
bool tryNewTarget(Node& newTarget);
bool checkAndMarkSuperScope();
- Node methodDefinition(uint32_t preludeStart, PropertyType propType, HandleAtom funName);
+ Node methodDefinition(uint32_t toStringStart, PropertyType propType, HandleAtom funName);
/*
* Additional JS parsers.
@@ -1258,7 +1341,7 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
bool functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind,
Node funcpn);
- Node functionDefinition(uint32_t preludeStart, Node pn,
+ Node functionDefinition(uint32_t toStringStart, Node pn,
InHandling inHandling, YieldHandling yieldHandling, HandleAtom name,
FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
@@ -1294,21 +1377,34 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
bool checkExportedName(JSAtom* exportName);
bool checkExportedNamesForDeclaration(Node node);
+ bool checkExportedNameForClause(Node node);
+ bool checkExportedNameForFunction(Node node);
+ bool checkExportedNameForClass(Node node);
+
enum ClassContext { ClassStatement, ClassExpression };
Node classDefinition(YieldHandling yieldHandling, ClassContext classContext,
DefaultHandling defaultHandling);
- PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling,
- bool yieldTokenizedAsName);
+ bool checkLabelOrIdentifierReference(HandlePropertyName ident,
+ uint32_t offset,
+ YieldHandling yieldHandling);
+
+ bool checkLocalExportName(HandlePropertyName ident, uint32_t offset) {
+ return checkLabelOrIdentifierReference(ident, offset, YieldIsName);
+ }
+
+ bool checkBindingIdentifier(HandlePropertyName ident,
+ uint32_t offset,
+ YieldHandling yieldHandling);
+
+ PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling);
PropertyName* labelIdentifier(YieldHandling yieldHandling) {
- return labelOrIdentifierReference(yieldHandling, false);
+ return labelOrIdentifierReference(yieldHandling);
}
- PropertyName* identifierReference(YieldHandling yieldHandling,
- bool yieldTokenizedAsName = false)
- {
- return labelOrIdentifierReference(yieldHandling, yieldTokenizedAsName);
+ PropertyName* identifierReference(YieldHandling yieldHandling) {
+ return labelOrIdentifierReference(yieldHandling);
}
PropertyName* importedBinding() {
@@ -1337,23 +1433,27 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
Node newDotGeneratorName();
bool declareDotGeneratorName();
- bool skipLazyInnerFunction(Node pn, uint32_t preludeStart, FunctionSyntaxKind kind,
+ bool skipLazyInnerFunction(Node pn, uint32_t toStringStart, FunctionSyntaxKind kind,
bool tryAnnexB);
- bool innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun, uint32_t preludeStart,
+ bool innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun, uint32_t toStringStart,
InHandling inHandling, YieldHandling yieldHandling,
FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB,
Directives inheritedDirectives, Directives* newDirectives);
- bool trySyntaxParseInnerFunction(Node pn, HandleFunction fun, uint32_t preludeStart,
+ bool trySyntaxParseInnerFunction(Node pn, HandleFunction fun, uint32_t toStringStart,
InHandling inHandling, YieldHandling yieldHandling,
FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
bool tryAnnexB,
Directives inheritedDirectives, Directives* newDirectives);
- bool finishFunctionScopes();
- bool finishFunction();
+ bool finishFunctionScopes(bool isStandaloneFunction);
+ bool finishFunction(bool isStandaloneFunction = false);
bool leaveInnerFunction(ParseContext* outerpc);
+ bool matchOrInsertSemicolonHelper(TokenStream::Modifier modifier);
+ bool matchOrInsertSemicolonAfterExpression();
+ bool matchOrInsertSemicolonAfterNonExpression();
+
public:
enum FunctionCallBehavior {
PermitAssignmentToFunctionCalls,
@@ -1366,21 +1466,22 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
private:
bool checkIncDecOperand(Node operand, uint32_t operandOffset);
bool checkStrictAssignment(Node lhs);
- bool checkStrictBinding(PropertyName* name, TokenPos pos);
bool hasValidSimpleStrictParameterNames();
- bool isValidStrictBinding(PropertyName* name);
+ void reportMissingClosing(unsigned errorNumber, unsigned noteNumber, uint32_t openedPos);
- void reportRedeclaration(HandlePropertyName name, DeclarationKind kind, TokenPos pos);
- bool notePositionalFormalParameter(Node fn, HandlePropertyName name,
+ void reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind, TokenPos pos,
+ uint32_t prevPos);
+ bool notePositionalFormalParameter(Node fn, HandlePropertyName name, uint32_t beginPos,
bool disallowDuplicateParams, bool* duplicatedParam);
bool noteDestructuredPositionalFormalParameter(Node fn, Node destruct);
mozilla::Maybe<DeclarationKind> isVarRedeclaredInEval(HandlePropertyName name,
DeclarationKind kind);
- bool tryDeclareVar(HandlePropertyName name, DeclarationKind kind,
- mozilla::Maybe<DeclarationKind>* redeclaredKind);
- bool tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name, bool* tryAnnexB);
+ bool tryDeclareVar(HandlePropertyName name, DeclarationKind kind, uint32_t beginPos,
+ mozilla::Maybe<DeclarationKind>* redeclaredKind, uint32_t* prevPos);
+ bool tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name, uint32_t beginPos,
+ bool* tryAnnexB);
bool checkLexicalDeclarationDirectlyWithinBlock(ParseContext::Statement& stmt,
DeclarationKind kind, TokenPos pos);
bool noteDeclaredName(HandlePropertyName name, DeclarationKind kind, TokenPos pos);
@@ -1399,25 +1500,27 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
mozilla::Maybe<LexicalScope::Data*> newLexicalScopeData(ParseContext::Scope& scope);
Node finishLexicalScope(ParseContext::Scope& scope, Node body);
- Node propertyName(YieldHandling yieldHandling, Node propList,
+ Node propertyName(YieldHandling yieldHandling,
+ const mozilla::Maybe<DeclarationKind>& maybeDecl, Node propList,
PropertyType* propType, MutableHandleAtom propAtom);
- Node computedPropertyName(YieldHandling yieldHandling, Node literal);
+ Node computedPropertyName(YieldHandling yieldHandling,
+ const mozilla::Maybe<DeclarationKind>& maybeDecl, Node literal);
Node arrayInitializer(YieldHandling yieldHandling, PossibleError* possibleError);
Node newRegExp();
Node objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError);
- // Top-level entrypoint into destructuring pattern checking/name-analyzing.
- bool checkDestructuringPattern(Node pattern, mozilla::Maybe<DeclarationKind> maybeDecl,
- PossibleError* possibleError = nullptr);
+ Node bindingInitializer(Node lhs, DeclarationKind kind, YieldHandling yieldHandling);
+ Node bindingIdentifier(DeclarationKind kind, YieldHandling yieldHandling);
+ Node bindingIdentifierOrPattern(DeclarationKind kind, YieldHandling yieldHandling,
+ TokenKind tt);
+ Node objectBindingPattern(DeclarationKind kind, YieldHandling yieldHandling);
+ Node arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling);
- // Recursive methods for checking/name-analyzing subcomponents of a
- // destructuring pattern. The array/object methods *must* be passed arrays
- // or objects. The name method may be passed anything but will report an
- // error if not passed a name.
- bool checkDestructuringArray(Node arrayPattern, mozilla::Maybe<DeclarationKind> maybeDecl);
- bool checkDestructuringObject(Node objectPattern, mozilla::Maybe<DeclarationKind> maybeDecl);
- bool checkDestructuringName(Node expr, mozilla::Maybe<DeclarationKind> maybeDecl);
+ void checkDestructuringAssignmentTarget(Node expr, TokenPos exprPos,
+ PossibleError* possibleError);
+ void checkDestructuringAssignmentElement(Node expr, TokenPos exprPos,
+ PossibleError* possibleError);
Node newNumber(const Token& tok) {
return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos);
@@ -1427,12 +1530,26 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
JSAtom* prefixAccessorName(PropertyType propType, HandleAtom propAtom);
- TokenPos pos() const { return tokenStream.currentToken().pos; }
-
bool asmJS(Node list);
+};
- bool warnOnceAboutExprClosure();
- bool warnOnceAboutForEach();
+template <typename ParseHandler>
+class MOZ_STACK_CLASS AutoAwaitIsKeyword
+{
+ private:
+ Parser<ParseHandler>* parser_;
+ bool oldAwaitIsKeyword_;
+
+ public:
+ AutoAwaitIsKeyword(Parser<ParseHandler>* parser, bool awaitIsKeyword) {
+ parser_ = parser;
+ oldAwaitIsKeyword_ = parser_->awaitIsKeyword_;
+ parser_->setAwaitIsKeyword(awaitIsKeyword);
+ }
+
+ ~AutoAwaitIsKeyword() {
+ parser_->setAwaitIsKeyword(oldAwaitIsKeyword_);
+ }
};
} /* namespace frontend */
diff --git a/js/src/vm/Keywords.h b/js/src/frontend/ReservedWords.h
index ef37c4419..27f5b11c1 100644
--- a/js/src/vm/Keywords.h
+++ b/js/src/frontend/ReservedWords.h
@@ -4,15 +4,16 @@
* 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/. */
-/* A higher-order macro for enumerating keyword tokens. */
+/* A higher-order macro for enumerating reserved word tokens. */
-#ifndef vm_Keywords_h
-#define vm_Keywords_h
+#ifndef vm_ReservedWords_h
+#define vm_ReservedWords_h
-#define FOR_EACH_JAVASCRIPT_KEYWORD(macro) \
+#define FOR_EACH_JAVASCRIPT_RESERVED_WORD(macro) \
macro(false, false_, TOK_FALSE) \
macro(true, true_, TOK_TRUE) \
macro(null, null, TOK_NULL) \
+ \
/* Keywords. */ \
macro(break, break_, TOK_BREAK) \
macro(case, case_, TOK_CASE) \
@@ -36,31 +37,45 @@
macro(this, this_, TOK_THIS) \
macro(throw, throw_, TOK_THROW) \
macro(try, try_, TOK_TRY) \
- macro(typeof, typeof, TOK_TYPEOF) \
+ macro(typeof, typeof_, TOK_TYPEOF) \
macro(var, var, TOK_VAR) \
macro(void, void_, TOK_VOID) \
macro(while, while_, TOK_WHILE) \
macro(with, with, TOK_WITH) \
macro(import, import, TOK_IMPORT) \
- macro(export, export, TOK_EXPORT) \
+ macro(export, export_, TOK_EXPORT) \
macro(class, class_, TOK_CLASS) \
macro(extends, extends, TOK_EXTENDS) \
macro(super, super, TOK_SUPER) \
- /* Reserved keywords. */ \
- macro(enum, enum_, TOK_RESERVED) \
- /* Future reserved keywords, but only in strict mode. */ \
- macro(implements, implements, TOK_STRICT_RESERVED) \
- macro(interface, interface, TOK_STRICT_RESERVED) \
- macro(package, package, TOK_STRICT_RESERVED) \
- macro(private, private_, TOK_STRICT_RESERVED) \
- macro(protected, protected_, TOK_STRICT_RESERVED) \
- macro(public, public_, TOK_STRICT_RESERVED) \
+ \
+ /* Future reserved words. */ \
+ macro(enum, enum_, TOK_ENUM) \
+ \
+ /* Future reserved words, but only in strict mode. */ \
+ macro(implements, implements, TOK_IMPLEMENTS) \
+ macro(interface, interface, TOK_INTERFACE) \
+ macro(package, package, TOK_PACKAGE) \
+ macro(private, private_, TOK_PRIVATE) \
+ macro(protected, protected_, TOK_PROTECTED) \
+ macro(public, public_, TOK_PUBLIC) \
+ \
+ /* Contextual keywords. */ \
+ macro(as, as, TOK_AS) \
+ macro(async, async, TOK_ASYNC) \
macro(await, await, TOK_AWAIT) \
+ macro(each, each, TOK_EACH) \
+ macro(from, from, TOK_FROM) \
+ macro(get, get, TOK_GET) \
+ macro(let, let, TOK_LET) \
+ macro(of, of, TOK_OF) \
+ macro(set, set, TOK_SET) \
+ macro(static, static_, TOK_STATIC) \
+ macro(target, target, TOK_TARGET) \
/* \
* Yield is a token inside function*. Outside of a function*, it is a \
- * future reserved keyword in strict mode, but a keyword in JS1.7 even \
+ * future reserved word in strict mode, but a keyword in JS1.7 even \
* when strict. Punt logic to parser. \
*/ \
macro(yield, yield, TOK_YIELD)
-#endif /* vm_Keywords_h */
+#endif /* vm_ReservedWords_h */
diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h
index b20417d5d..013444690 100644
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -38,6 +38,7 @@ enum class StatementKind : uint8_t
ForOfLoop,
DoLoop,
WhileLoop,
+ Class,
// Used only by BytecodeEmitter.
Spread
@@ -450,7 +451,8 @@ class FunctionBox : public ObjectBox, public SharedContext
uint32_t bufEnd;
uint32_t startLine;
uint32_t startColumn;
- uint32_t preludeStart;
+ uint32_t toStringStart;
+ uint32_t toStringEnd;
uint16_t length;
uint8_t generatorKindBits_; /* The GeneratorKind of this function. */
@@ -473,11 +475,14 @@ class FunctionBox : public ObjectBox, public SharedContext
bool usesThis:1; /* contains 'this' */
bool usesReturn:1; /* contains a 'return' statement */
bool hasRest_:1; /* has rest parameter */
+ bool isExprBody_:1; /* arrow function with expression
+ * body or expression closure:
+ * function(x) x*x */
FunctionContextFlags funCxFlags;
FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* traceListHead, JSFunction* fun,
- uint32_t preludeStart, Directives directives, bool extraWarnings,
+ uint32_t toStringStart, Directives directives, bool extraWarnings,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind);
MutableHandle<LexicalScope::Data*> namedLambdaBindings() {
@@ -546,6 +551,11 @@ class FunctionBox : public ObjectBox, public SharedContext
hasRest_ = true;
}
+ bool isExprBody() const { return isExprBody_; }
+ void setIsExprBody() {
+ isExprBody_ = true;
+ }
+
void setGeneratorKind(GeneratorKind kind) {
// A generator kind can be set at initialization, or when "yield" is
// first seen. In both cases the transition can only happen from
@@ -595,6 +605,14 @@ class FunctionBox : public ObjectBox, public SharedContext
tokenStream.srcCoords.lineNumAndColumnIndex(bufStart, &startLine, &startColumn);
}
+ void setEnd(uint32_t end) {
+ // For all functions except class constructors, the buffer and
+ // toString ending positions are the same. Class constructors override
+ // the toString ending position with the end of the class definition.
+ bufEnd = end;
+ toStringEnd = end;
+ }
+
void trace(JSTracer* trc) override;
};
diff --git a/js/src/frontend/SourceNotes.h b/js/src/frontend/SourceNotes.h
index dd2a95ad1..6ae184ae4 100644
--- a/js/src/frontend/SourceNotes.h
+++ b/js/src/frontend/SourceNotes.h
@@ -56,13 +56,14 @@ namespace js {
M(SRC_NEXTCASE, "nextcase", 1) /* Distance forward from one CASE in a CONDSWITCH to \
the next. */ \
M(SRC_ASSIGNOP, "assignop", 0) /* += or another assign-op follows. */ \
+ M(SRC_CLASS_SPAN, "class", 2) /* The starting and ending offsets for the class, used \
+ for toString correctness for default ctors. */ \
M(SRC_TRY, "try", 1) /* JSOP_TRY, offset points to goto at the end of the \
try block. */ \
/* All notes above here are "gettable". See SN_IS_GETTABLE below. */ \
M(SRC_COLSPAN, "colspan", 1) /* Number of columns this opcode spans. */ \
M(SRC_NEWLINE, "newline", 0) /* Bytecode follows a source newline. */ \
M(SRC_SETLINE, "setline", 1) /* A file-absolute source line number note. */ \
- M(SRC_UNUSED20, "unused20", 0) /* Unused. */ \
M(SRC_UNUSED21, "unused21", 0) /* Unused. */ \
M(SRC_UNUSED22, "unused22", 0) /* Unused. */ \
M(SRC_UNUSED23, "unused23", 0) /* Unused. */ \
diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h
index 00ea9d35d..a604b599f 100644
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -9,6 +9,8 @@
#include "mozilla/Attributes.h"
+#include <string.h>
+
#include "frontend/ParseNode.h"
#include "frontend/TokenStream.h"
@@ -94,10 +96,13 @@ class SyntaxParseHandler
// Nodes representing unparenthesized names.
NodeUnparenthesizedArgumentsName,
- NodeUnparenthesizedAsyncName,
NodeUnparenthesizedEvalName,
NodeUnparenthesizedName,
+ // Node representing the "async" name, which may actually be a
+ // contextual keyword.
+ NodePotentialAsyncKeyword,
+
// Valuable for recognizing potential destructuring patterns.
NodeUnparenthesizedArray,
NodeUnparenthesizedObject,
@@ -183,8 +188,8 @@ class SyntaxParseHandler
lastAtom = name;
if (name == cx->names().arguments)
return NodeUnparenthesizedArgumentsName;
- if (name == cx->names().async)
- return NodeUnparenthesizedAsyncName;
+ if (pos.begin + strlen("async") == pos.end && name == cx->names().async)
+ return NodePotentialAsyncKeyword;
if (name == cx->names().eval)
return NodeUnparenthesizedEvalName;
return NodeUnparenthesizedName;
@@ -219,6 +224,7 @@ class SyntaxParseHandler
Node newThisLiteral(const TokenPos& pos, Node thisName) { return NodeGeneric; }
Node newNullLiteral(const TokenPos& pos) { return NodeGeneric; }
+ Node newRawUndefinedLiteral(const TokenPos& pos) { return NodeGeneric; }
template <class Boxer>
Node newRegExp(RegExpObject* reobj, const TokenPos& pos, Boxer& boxer) { return NodeGeneric; }
@@ -235,6 +241,10 @@ class SyntaxParseHandler
return NodeUnparenthesizedUnary;
}
+ Node newNullary(ParseNodeKind kind, JSOp op, const TokenPos& pos) {
+ return NodeGeneric;
+ }
+
Node newUnary(ParseNodeKind kind, JSOp op, uint32_t begin, Node kid) {
return NodeUnparenthesizedUnary;
}
@@ -279,7 +289,7 @@ class SyntaxParseHandler
Node newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; }
Node newClassMethodList(uint32_t begin) { return NodeGeneric; }
Node newClassNames(Node outer, Node inner, const TokenPos& pos) { return NodeGeneric; }
- Node newClass(Node name, Node heritage, Node methodBlock) { return NodeGeneric; }
+ Node newClass(Node name, Node heritage, Node methodBlock, const TokenPos& pos) { return NodeGeneric; }
Node newNewTarget(Node newHolder, Node targetHolder) { return NodeGeneric; }
Node newPosHolder(const TokenPos& pos) { return NodeGeneric; }
@@ -288,6 +298,7 @@ class SyntaxParseHandler
MOZ_MUST_USE bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; }
MOZ_MUST_USE bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; }
MOZ_MUST_USE bool addShorthand(Node literal, Node name, Node expr) { return true; }
+ MOZ_MUST_USE bool addSpreadProperty(Node literal, uint32_t begin, Node inner) { return true; }
MOZ_MUST_USE bool addObjectMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; }
MOZ_MUST_USE bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op, bool isStatic) { return true; }
Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
@@ -302,6 +313,16 @@ class SyntaxParseHandler
MOZ_MUST_USE bool prependInitialYield(Node stmtList, Node gen) { return true; }
Node newEmptyStatement(const TokenPos& pos) { return NodeEmptyStatement; }
+ Node newExportDeclaration(Node kid, const TokenPos& pos) {
+ return NodeGeneric;
+ }
+ Node newExportFromDeclaration(uint32_t begin, Node exportSpecSet, Node moduleSpec) {
+ return NodeGeneric;
+ }
+ Node newExportDefaultDeclaration(Node kid, Node maybeBinding, const TokenPos& pos) {
+ return NodeGeneric;
+ }
+
Node newSetThis(Node thisName, Node value) { return value; }
Node newExprStatement(Node expr, uint32_t end) {
@@ -497,7 +518,7 @@ class SyntaxParseHandler
return NodeParenthesizedArgumentsName;
if (node == NodeUnparenthesizedEvalName)
return NodeParenthesizedEvalName;
- if (node == NodeUnparenthesizedName || node == NodeUnparenthesizedAsyncName)
+ if (node == NodeUnparenthesizedName || node == NodePotentialAsyncKeyword)
return NodeParenthesizedName;
if (node == NodeUnparenthesizedArray)
@@ -528,9 +549,9 @@ class SyntaxParseHandler
bool isUnparenthesizedName(Node node) {
return node == NodeUnparenthesizedArgumentsName ||
- node == NodeUnparenthesizedAsyncName ||
node == NodeUnparenthesizedEvalName ||
- node == NodeUnparenthesizedName;
+ node == NodeUnparenthesizedName ||
+ node == NodePotentialAsyncKeyword;
}
bool isNameAnyParentheses(Node node) {
@@ -541,9 +562,11 @@ class SyntaxParseHandler
node == NodeParenthesizedName;
}
- bool nameIsEvalAnyParentheses(Node node, ExclusiveContext* cx) {
- MOZ_ASSERT(isNameAnyParentheses(node),
- "must only call this function on known names");
+ bool isArgumentsAnyParentheses(Node node, ExclusiveContext* cx) {
+ return node == NodeUnparenthesizedArgumentsName || node == NodeParenthesizedArgumentsName;
+ }
+
+ bool isEvalAnyParentheses(Node node, ExclusiveContext* cx) {
return node == NodeUnparenthesizedEvalName || node == NodeParenthesizedEvalName;
}
@@ -551,17 +574,15 @@ class SyntaxParseHandler
MOZ_ASSERT(isNameAnyParentheses(node),
"must only call this method on known names");
- if (nameIsEvalAnyParentheses(node, cx))
+ if (isEvalAnyParentheses(node, cx))
return js_eval_str;
- if (node == NodeUnparenthesizedArgumentsName || node == NodeParenthesizedArgumentsName)
+ if (isArgumentsAnyParentheses(node, cx))
return js_arguments_str;
return nullptr;
}
- bool nameIsUnparenthesizedAsync(Node node, ExclusiveContext* cx) {
- MOZ_ASSERT(isNameAnyParentheses(node),
- "must only call this function on known names");
- return node == NodeUnparenthesizedAsyncName;
+ bool isAsyncKeyword(Node node, ExclusiveContext* cx) {
+ return node == NodePotentialAsyncKeyword;
}
PropertyName* maybeDottedProperty(Node node) {
diff --git a/js/src/frontend/TokenKind.h b/js/src/frontend/TokenKind.h
index 6f22d78e5..98f23fec8 100644
--- a/js/src/frontend/TokenKind.h
+++ b/js/src/frontend/TokenKind.h
@@ -81,9 +81,12 @@
\
macro(REGEXP, "regular expression literal") \
macro(TRUE, "boolean literal 'true'") \
+ range(RESERVED_WORD_LITERAL_FIRST, TRUE) \
macro(FALSE, "boolean literal 'false'") \
macro(NULL, "null literal") \
+ range(RESERVED_WORD_LITERAL_LAST, NULL) \
macro(THIS, "keyword 'this'") \
+ range(KEYWORD_FIRST, THIS) \
macro(FUNCTION, "keyword 'function'") \
macro(IF, "keyword 'if'") \
macro(ELSE, "keyword 'else'") \
@@ -106,16 +109,43 @@
macro(FINALLY, "keyword 'finally'") \
macro(THROW, "keyword 'throw'") \
macro(DEBUGGER, "keyword 'debugger'") \
- macro(YIELD, "keyword 'yield'") \
- macro(AWAIT, "keyword 'await'") \
macro(EXPORT, "keyword 'export'") \
macro(IMPORT, "keyword 'import'") \
macro(CLASS, "keyword 'class'") \
macro(EXTENDS, "keyword 'extends'") \
macro(SUPER, "keyword 'super'") \
- macro(RESERVED, "reserved keyword") \
- /* reserved keywords in strict mode */ \
- macro(STRICT_RESERVED, "reserved keyword") \
+ range(KEYWORD_LAST, SUPER) \
+ \
+ /* contextual keywords */ \
+ macro(AS, "'as'") \
+ range(CONTEXTUAL_KEYWORD_FIRST, AS) \
+ macro(ASYNC, "'async'") \
+ macro(AWAIT, "'await'") \
+ macro(EACH, "'each'") \
+ macro(FROM, "'from'") \
+ macro(GET, "'get'") \
+ macro(LET, "'let'") \
+ macro(OF, "'of'") \
+ macro(SET, "'set'") \
+ macro(STATIC, "'static'") \
+ macro(TARGET, "'target'") \
+ macro(YIELD, "'yield'") \
+ range(CONTEXTUAL_KEYWORD_LAST, YIELD) \
+ \
+ /* future reserved words */ \
+ macro(ENUM, "reserved word 'enum'") \
+ range(FUTURE_RESERVED_KEYWORD_FIRST, ENUM) \
+ range(FUTURE_RESERVED_KEYWORD_LAST, ENUM) \
+ \
+ /* reserved words in strict mode */ \
+ macro(IMPLEMENTS, "reserved word 'implements'") \
+ range(STRICT_RESERVED_KEYWORD_FIRST, IMPLEMENTS) \
+ macro(INTERFACE, "reserved word 'interface'") \
+ macro(PACKAGE, "reserved word 'package'") \
+ macro(PRIVATE, "reserved word 'private'") \
+ macro(PROTECTED, "reserved word 'protected'") \
+ macro(PUBLIC, "reserved word 'public'") \
+ range(STRICT_RESERVED_KEYWORD_LAST, PUBLIC) \
\
/* \
* The following token types occupy contiguous ranges to enable easy \
@@ -149,7 +179,9 @@
range(RELOP_LAST, GE) \
\
macro(INSTANCEOF, "keyword 'instanceof'") \
+ range(KEYWORD_BINOP_FIRST, INSTANCEOF) \
macro(IN, "keyword 'in'") \
+ range(KEYWORD_BINOP_LAST, IN) \
\
/* Shift ops, per TokenKindIsShift. */ \
macro(LSH, "'<<'") \
@@ -168,7 +200,9 @@
\
/* Unary operation tokens. */ \
macro(TYPEOF, "keyword 'typeof'") \
+ range(KEYWORD_UNOP_FIRST, TYPEOF) \
macro(VOID, "keyword 'void'") \
+ range(KEYWORD_UNOP_LAST, VOID) \
macro(NOT, "'!'") \
macro(BITNOT, "'~'") \
\
@@ -239,6 +273,61 @@ TokenKindIsAssignment(TokenKind tt)
return TOK_ASSIGNMENT_START <= tt && tt <= TOK_ASSIGNMENT_LAST;
}
+inline MOZ_MUST_USE bool
+TokenKindIsKeyword(TokenKind tt)
+{
+ return (TOK_KEYWORD_FIRST <= tt && tt <= TOK_KEYWORD_LAST) ||
+ (TOK_KEYWORD_BINOP_FIRST <= tt && tt <= TOK_KEYWORD_BINOP_LAST) ||
+ (TOK_KEYWORD_UNOP_FIRST <= tt && tt <= TOK_KEYWORD_UNOP_LAST);
+}
+
+inline MOZ_MUST_USE bool
+TokenKindIsContextualKeyword(TokenKind tt)
+{
+ return TOK_CONTEXTUAL_KEYWORD_FIRST <= tt && tt <= TOK_CONTEXTUAL_KEYWORD_LAST;
+}
+
+inline MOZ_MUST_USE bool
+TokenKindIsFutureReservedWord(TokenKind tt)
+{
+ return TOK_FUTURE_RESERVED_KEYWORD_FIRST <= tt && tt <= TOK_FUTURE_RESERVED_KEYWORD_LAST;
+}
+
+inline MOZ_MUST_USE bool
+TokenKindIsStrictReservedWord(TokenKind tt)
+{
+ return TOK_STRICT_RESERVED_KEYWORD_FIRST <= tt && tt <= TOK_STRICT_RESERVED_KEYWORD_LAST;
+}
+
+inline MOZ_MUST_USE bool
+TokenKindIsReservedWordLiteral(TokenKind tt)
+{
+ return TOK_RESERVED_WORD_LITERAL_FIRST <= tt && tt <= TOK_RESERVED_WORD_LITERAL_LAST;
+}
+
+inline MOZ_MUST_USE bool
+TokenKindIsReservedWord(TokenKind tt)
+{
+ return TokenKindIsKeyword(tt) ||
+ TokenKindIsFutureReservedWord(tt) ||
+ TokenKindIsReservedWordLiteral(tt);
+}
+
+inline MOZ_MUST_USE bool
+TokenKindIsPossibleIdentifier(TokenKind tt)
+{
+ return tt == TOK_NAME ||
+ TokenKindIsContextualKeyword(tt) ||
+ TokenKindIsStrictReservedWord(tt);
+}
+
+inline MOZ_MUST_USE bool
+TokenKindIsPossibleIdentifierName(TokenKind tt)
+{
+ return TokenKindIsPossibleIdentifier(tt) ||
+ TokenKindIsReservedWord(tt);
+}
+
} // namespace frontend
} // namespace js
diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp
index 8438ff7c5..b8623d545 100644
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -8,6 +8,7 @@
#include "frontend/TokenStream.h"
+#include "mozilla/ArrayUtils.h"
#include "mozilla/IntegerTypeTraits.h"
#include "mozilla/PodOperations.h"
@@ -23,80 +24,81 @@
#include "jsnum.h"
#include "frontend/BytecodeCompiler.h"
+#include "frontend/ReservedWords.h"
#include "js/CharacterEncoding.h"
#include "js/UniquePtr.h"
#include "vm/HelperThreads.h"
-#include "vm/Keywords.h"
#include "vm/StringBuffer.h"
#include "vm/Unicode.h"
using namespace js;
using namespace js::frontend;
+using mozilla::ArrayLength;
using mozilla::Maybe;
using mozilla::PodAssign;
using mozilla::PodCopy;
using mozilla::PodZero;
-struct KeywordInfo {
- const char* chars; // C string with keyword text
+struct ReservedWordInfo {
+ const char* chars; // C string with reserved word text
TokenKind tokentype;
};
-static const KeywordInfo keywords[] = {
-#define KEYWORD_INFO(keyword, name, type) \
- {js_##keyword##_str, type},
- FOR_EACH_JAVASCRIPT_KEYWORD(KEYWORD_INFO)
-#undef KEYWORD_INFO
+static const ReservedWordInfo reservedWords[] = {
+#define RESERVED_WORD_INFO(word, name, type) \
+ {js_##word##_str, type},
+ FOR_EACH_JAVASCRIPT_RESERVED_WORD(RESERVED_WORD_INFO)
+#undef RESERVED_WORD_INFO
};
-// Returns a KeywordInfo for the specified characters, or nullptr if the string
-// is not a keyword.
+// Returns a ReservedWordInfo for the specified characters, or nullptr if the
+// string is not a reserved word.
template <typename CharT>
-static const KeywordInfo*
-FindKeyword(const CharT* s, size_t length)
+static const ReservedWordInfo*
+FindReservedWord(const CharT* s, size_t length)
{
MOZ_ASSERT(length != 0);
size_t i;
- const KeywordInfo* kw;
+ const ReservedWordInfo* rw;
const char* chars;
-#define JSKW_LENGTH() length
-#define JSKW_AT(column) s[column]
-#define JSKW_GOT_MATCH(index) i = (index); goto got_match;
-#define JSKW_TEST_GUESS(index) i = (index); goto test_guess;
-#define JSKW_NO_MATCH() goto no_match;
-#include "jsautokw.h"
-#undef JSKW_NO_MATCH
-#undef JSKW_TEST_GUESS
-#undef JSKW_GOT_MATCH
-#undef JSKW_AT
-#undef JSKW_LENGTH
+#define JSRW_LENGTH() length
+#define JSRW_AT(column) s[column]
+#define JSRW_GOT_MATCH(index) i = (index); goto got_match;
+#define JSRW_TEST_GUESS(index) i = (index); goto test_guess;
+#define JSRW_NO_MATCH() goto no_match;
+#include "frontend/ReservedWordsGenerated.h"
+#undef JSRW_NO_MATCH
+#undef JSRW_TEST_GUESS
+#undef JSRW_GOT_MATCH
+#undef JSRW_AT
+#undef JSRW_LENGTH
got_match:
- return &keywords[i];
+ return &reservedWords[i];
test_guess:
- kw = &keywords[i];
- chars = kw->chars;
+ rw = &reservedWords[i];
+ chars = rw->chars;
do {
if (*s++ != (unsigned char)(*chars++))
goto no_match;
} while (--length != 0);
- return kw;
+ return rw;
no_match:
return nullptr;
}
-static const KeywordInfo*
-FindKeyword(JSLinearString* str)
+static const ReservedWordInfo*
+FindReservedWord(JSLinearString* str)
{
JS::AutoCheckCannotGC nogc;
return str->hasLatin1Chars()
- ? FindKeyword(str->latin1Chars(nogc), str->length())
- : FindKeyword(str->twoByteChars(nogc), str->length());
+ ? FindReservedWord(str->latin1Chars(nogc), str->length())
+ : FindReservedWord(str->twoByteChars(nogc), str->length());
}
template <typename CharT>
@@ -186,7 +188,68 @@ frontend::IsIdentifier(const char16_t* chars, size_t length)
bool
frontend::IsKeyword(JSLinearString* str)
{
- return FindKeyword(str) != nullptr;
+ if (const ReservedWordInfo* rw = FindReservedWord(str))
+ return TokenKindIsKeyword(rw->tokentype);
+
+ return false;
+}
+
+bool
+frontend::IsFutureReservedWord(JSLinearString* str)
+{
+ if (const ReservedWordInfo* rw = FindReservedWord(str))
+ return TokenKindIsFutureReservedWord(rw->tokentype);
+
+ return false;
+}
+
+bool
+frontend::IsStrictReservedWord(JSLinearString* str)
+{
+ if (const ReservedWordInfo* rw = FindReservedWord(str))
+ return TokenKindIsStrictReservedWord(rw->tokentype);
+
+ return false;
+}
+
+bool
+frontend::IsReservedWordLiteral(JSLinearString* str)
+{
+ if (const ReservedWordInfo* rw = FindReservedWord(str))
+ return TokenKindIsReservedWordLiteral(rw->tokentype);
+
+ return false;
+}
+
+const char*
+frontend::ReservedWordToCharZ(PropertyName* str)
+{
+ const ReservedWordInfo* rw = FindReservedWord(str);
+ if (rw == nullptr)
+ return nullptr;
+
+ switch (rw->tokentype) {
+#define EMIT_CASE(word, name, type) case type: return js_##word##_str;
+ FOR_EACH_JAVASCRIPT_RESERVED_WORD(EMIT_CASE)
+#undef EMIT_CASE
+ default:
+ MOZ_ASSERT_UNREACHABLE("Not a reserved word PropertyName.");
+ }
+ return nullptr;
+}
+
+PropertyName*
+TokenStream::reservedWordToPropertyName(TokenKind tt) const
+{
+ MOZ_ASSERT(tt != TOK_NAME);
+ switch (tt) {
+#define EMIT_CASE(word, name, type) case type: return cx->names().name;
+ FOR_EACH_JAVASCRIPT_RESERVED_WORD(EMIT_CASE)
+#undef EMIT_CASE
+ default:
+ MOZ_ASSERT_UNREACHABLE("Not a reserved word TokenKind.");
+ }
+ return nullptr;
}
TokenStream::SourceCoords::SourceCoords(ExclusiveContext* cx, uint32_t ln)
@@ -223,8 +286,13 @@ TokenStream::SourceCoords::add(uint32_t lineNum, uint32_t lineStartOffset)
// only if lineStartOffsets_.append succeeds, to keep sentinel.
// Otherwise return false to tell TokenStream about OOM.
uint32_t maxPtr = MAX_PTR;
- if (!lineStartOffsets_.append(maxPtr))
+ if (!lineStartOffsets_.append(maxPtr)) {
+ static_assert(mozilla::IsSame<decltype(lineStartOffsets_.allocPolicy()),
+ TempAllocPolicy&>::value,
+ "this function's caller depends on it reporting an "
+ "error on failure, as TempAllocPolicy ensures");
return false;
+ }
lineStartOffsets_[lineIndex] = lineStartOffset;
} else {
@@ -554,8 +622,9 @@ TokenStream::advance(size_t position)
MOZ_MAKE_MEM_UNDEFINED(&cur->type, sizeof(cur->type));
lookahead = 0;
- if (flags.hitOOM)
- return reportError(JSMSG_OUT_OF_MEMORY);
+ if (flags.hitOOM) {
+ return false;
+ }
return true;
}
@@ -599,8 +668,8 @@ TokenStream::seek(const Position& pos, const TokenStream& other)
}
bool
-TokenStream::reportStrictModeErrorNumberVA(uint32_t offset, bool strictMode, unsigned errorNumber,
- va_list args)
+TokenStream::reportStrictModeErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset,
+ bool strictMode, unsigned errorNumber, va_list args)
{
// In strict mode code, this is an error, not merely a warning.
unsigned flags;
@@ -611,7 +680,7 @@ TokenStream::reportStrictModeErrorNumberVA(uint32_t offset, bool strictMode, uns
else
return true;
- return reportCompileErrorNumberVA(offset, flags, errorNumber, args);
+ return reportCompileErrorNumberVA(Move(notes), offset, flags, errorNumber, args);
}
void
@@ -637,8 +706,8 @@ CompileError::throwError(JSContext* cx)
}
bool
-TokenStream::reportCompileErrorNumberVA(uint32_t offset, unsigned flags, unsigned errorNumber,
- va_list args)
+TokenStream::reportCompileErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset,
+ unsigned flags, unsigned errorNumber, va_list args)
{
bool warning = JSREPORT_IS_WARNING(flags);
@@ -655,6 +724,7 @@ TokenStream::reportCompileErrorNumberVA(uint32_t offset, unsigned flags, unsigne
return false;
CompileError& err = *tempErrPtr;
+ err.notes = Move(notes);
err.flags = flags;
err.errorNumber = errorNumber;
err.filename = filename;
@@ -746,7 +816,7 @@ TokenStream::reportStrictModeError(unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
- bool result = reportStrictModeErrorNumberVA(currentToken().pos.begin, strictMode(),
+ bool result = reportStrictModeErrorNumberVA(nullptr, currentToken().pos.begin, strictMode(),
errorNumber, args);
va_end(args);
return result;
@@ -757,8 +827,8 @@ TokenStream::reportError(unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
- bool result = reportCompileErrorNumberVA(currentToken().pos.begin, JSREPORT_ERROR, errorNumber,
- args);
+ bool result = reportCompileErrorNumberVA(nullptr, currentToken().pos.begin, JSREPORT_ERROR,
+ errorNumber, args);
va_end(args);
return result;
}
@@ -768,30 +838,32 @@ TokenStream::reportErrorNoOffset(unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
- bool result = reportCompileErrorNumberVA(NoOffset, JSREPORT_ERROR, errorNumber,
- args);
+ bool result = reportCompileErrorNumberVA(nullptr, NoOffset, JSREPORT_ERROR,
+ errorNumber, args);
va_end(args);
return result;
}
bool
-TokenStream::reportWarning(unsigned errorNumber, ...)
+TokenStream::warning(unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
- bool result = reportCompileErrorNumberVA(currentToken().pos.begin, JSREPORT_WARNING,
+ bool result = reportCompileErrorNumberVA(nullptr, currentToken().pos.begin, JSREPORT_WARNING,
errorNumber, args);
va_end(args);
return result;
}
bool
-TokenStream::reportExtraWarningErrorNumberVA(uint32_t offset, unsigned errorNumber, va_list args)
+TokenStream::reportExtraWarningErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset,
+ unsigned errorNumber, va_list args)
{
if (!options().extraWarningsOption)
return true;
- return reportCompileErrorNumberVA(offset, JSREPORT_STRICT|JSREPORT_WARNING, errorNumber, args);
+ return reportCompileErrorNumberVA(Move(notes), offset, JSREPORT_STRICT|JSREPORT_WARNING,
+ errorNumber, args);
}
void
@@ -802,7 +874,34 @@ TokenStream::reportAsmJSError(uint32_t offset, unsigned errorNumber, ...)
unsigned flags = options().throwOnAsmJSValidationFailureOption
? JSREPORT_ERROR
: JSREPORT_WARNING;
- reportCompileErrorNumberVA(offset, flags, errorNumber, args);
+ reportCompileErrorNumberVA(nullptr, offset, flags, errorNumber, args);
+ va_end(args);
+}
+
+void
+TokenStream::error(unsigned errorNumber, ...)
+{
+ va_list args;
+ va_start(args, errorNumber);
+#ifdef DEBUG
+ bool result =
+#endif
+ reportCompileErrorNumberVA(nullptr, currentToken().pos.begin, JSREPORT_ERROR,
+ errorNumber, args);
+ MOZ_ASSERT(!result, "reporting an error returned true?");
+ va_end(args);
+}
+
+void
+TokenStream::errorAt(uint32_t offset, unsigned errorNumber, ...)
+{
+ va_list args;
+ va_start(args, errorNumber);
+#ifdef DEBUG
+ bool result =
+#endif
+ reportCompileErrorNumberVA(nullptr, offset, JSREPORT_ERROR, errorNumber, args);
+ MOZ_ASSERT(!result, "reporting an error returned true?");
va_end(args);
}
@@ -934,34 +1033,49 @@ TokenStream::getDirectives(bool isMultiline, bool shouldWarnDeprecated)
bool
TokenStream::getDirective(bool isMultiline, bool shouldWarnDeprecated,
- const char* directive, int directiveLength,
+ const char* directive, uint8_t directiveLength,
const char* errorMsgPragma,
UniqueTwoByteChars* destination)
{
MOZ_ASSERT(directiveLength <= 18);
char16_t peeked[18];
- int32_t c;
if (peekChars(directiveLength, peeked) && CharsMatch(peeked, directive)) {
- if (shouldWarnDeprecated &&
- !reportWarning(JSMSG_DEPRECATED_PRAGMA, errorMsgPragma))
- return false;
+ if (shouldWarnDeprecated) {
+ if (!warning(JSMSG_DEPRECATED_PRAGMA, errorMsgPragma))
+ return false;
+ }
skipChars(directiveLength);
tokenbuf.clear();
- while ((c = peekChar()) && c != EOF && !unicode::IsSpaceOrBOM2(c)) {
- getChar();
+ do {
+ int32_t c;
+ if (!peekChar(&c))
+ return false;
+
+ if (c == EOF || unicode::IsSpaceOrBOM2(c))
+ break;
+
+ consumeKnownChar(c);
+
// Debugging directives can occur in both single- and multi-line
// comments. If we're currently inside a multi-line comment, we also
// need to recognize multi-line comment terminators.
- if (isMultiline && c == '*' && peekChar() == '/') {
- ungetChar('*');
- break;
+ if (isMultiline && c == '*') {
+ int32_t c2;
+ if (!peekChar(&c2))
+ return false;
+
+ if (c2 == '/') {
+ ungetChar('*');
+ break;
+ }
}
+
if (!tokenbuf.append(c))
return false;
- }
+ } while (true);
if (tokenbuf.empty()) {
// The directive's URL was missing, but this is not quite an
@@ -993,7 +1107,10 @@ TokenStream::getDisplayURL(bool isMultiline, bool shouldWarnDeprecated)
// developer would like to refer to the source as from the source's actual
// URL.
- return getDirective(isMultiline, shouldWarnDeprecated, " sourceURL=", 11,
+ static const char sourceURLDirective[] = " sourceURL=";
+ constexpr uint8_t sourceURLDirectiveLength = ArrayLength(sourceURLDirective) - 1;
+ return getDirective(isMultiline, shouldWarnDeprecated,
+ sourceURLDirective, sourceURLDirectiveLength,
"sourceURL", &displayURL_);
}
@@ -1003,7 +1120,10 @@ TokenStream::getSourceMappingURL(bool isMultiline, bool shouldWarnDeprecated)
// Match comments of the form "//# sourceMappingURL=<url>" or
// "/\* //# sourceMappingURL=<url> *\/"
- return getDirective(isMultiline, shouldWarnDeprecated, " sourceMappingURL=", 18,
+ static const char sourceMappingURLDirective[] = " sourceMappingURL=";
+ constexpr uint8_t sourceMappingURLDirectiveLength = ArrayLength(sourceMappingURLDirective) - 1;
+ return getDirective(isMultiline, shouldWarnDeprecated,
+ sourceMappingURLDirective, sourceMappingURLDirectiveLength,
"sourceMappingURL", &sourceMapURL_);
}
@@ -1110,36 +1230,6 @@ TokenStream::putIdentInTokenbuf(const char16_t* identStart)
return true;
}
-bool
-TokenStream::checkForKeyword(const KeywordInfo* kw, TokenKind* ttp)
-{
- if (!awaitIsKeyword && kw->tokentype == TOK_AWAIT) {
- if (ttp)
- *ttp = TOK_NAME;
- return true;
- }
-
- if (kw->tokentype == TOK_RESERVED)
- return reportError(JSMSG_RESERVED_ID, kw->chars);
-
- if (kw->tokentype == TOK_STRICT_RESERVED)
- return reportStrictModeError(JSMSG_RESERVED_ID, kw->chars);
-
- // Working keyword.
- *ttp = kw->tokentype;
- return true;
-}
-
-bool
-TokenStream::checkForKeyword(JSAtom* atom, TokenKind* ttp)
-{
- const KeywordInfo* kw = FindKeyword(atom);
- if (!kw)
- return true;
-
- return checkForKeyword(kw, ttp);
-}
-
enum FirstCharKind {
// A char16_t has the 'OneChar' kind if it, by itself, constitutes a valid
// token that cannot also be a prefix of a longer token. E.g. ';' has the
@@ -1363,36 +1453,18 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
length = userbuf.addressOfNextRawChar() - identStart;
}
- // Represent keywords as keyword tokens unless told otherwise.
- if (modifier != KeywordIsName) {
- if (const KeywordInfo* kw = FindKeyword(chars, length)) {
- // That said, keywords can't contain escapes. (Contexts where
- // keywords are treated as names, that also sometimes treat
- // keywords as keywords, must manually check this requirement.)
- // There are two exceptions
- // 1) StrictReservedWords: These keywords need to be treated as
- // names in non-strict mode.
- // 2) yield is also treated as a name if it contains an escape
- // sequence. The parser must handle this case separately.
- if (hadUnicodeEscape && !(
- (kw->tokentype == TOK_STRICT_RESERVED && !strictMode()) ||
- kw->tokentype == TOK_YIELD))
- {
- reportError(JSMSG_ESCAPED_KEYWORD);
- goto error;
- }
-
- tp->type = TOK_NAME;
- if (!checkForKeyword(kw, &tp->type))
- goto error;
- if (tp->type != TOK_NAME && !hadUnicodeEscape)
- goto out;
+ // Represent reserved words as reserved word tokens.
+ if (!hadUnicodeEscape) {
+ if (const ReservedWordInfo* rw = FindReservedWord(chars, length)) {
+ tp->type = rw->tokentype;
+ goto out;
}
}
JSAtom* atom = AtomizeChars(cx, chars, length);
- if (!atom)
+ if (!atom) {
goto error;
+ }
tp->type = TOK_NAME;
tp->setName(atom->asPropertyName());
goto out;
@@ -1538,10 +1610,11 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
// grammar. We might not always be so permissive, so we warn
// about it.
if (c >= '8') {
- if (!reportWarning(JSMSG_BAD_OCTAL, c == '8' ? "08" : "09")) {
+ if (!warning(JSMSG_BAD_OCTAL, c == '8' ? "08" : "09"))
goto error;
- }
- goto decimal; // use the decimal scanner for the rest of the number
+
+ // Use the decimal scanner for the rest of the number.
+ goto decimal;
}
c = getCharIgnoreEOL();
}
@@ -1690,7 +1763,8 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
case '/':
// Look for a single-line comment.
if (matchChar('/')) {
- c = peekChar();
+ if (!peekChar(&c))
+ goto error;
if (c == '@' || c == '#') {
bool shouldWarn = getChar() == '@';
if (!getDirectives(false, shouldWarn))
@@ -1757,7 +1831,8 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
RegExpFlag reflags = NoFlags;
unsigned length = tokenbuf.length() + 1;
while (true) {
- c = peekChar();
+ if (!peekChar(&c))
+ goto error;
if (c == 'g' && !(reflags & GlobalFlag))
reflags = RegExpFlag(reflags | GlobalFlag);
else if (c == 'i' && !(reflags & IgnoreCaseFlag))
@@ -1774,7 +1849,8 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
length++;
}
- c = peekChar();
+ if (!peekChar(&c))
+ goto error;
if (JS7_ISLET(c)) {
char buf[2] = { '\0', '\0' };
tp->pos.begin += length + 1;
@@ -1797,8 +1873,13 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
case '-':
if (matchChar('-')) {
- if (peekChar() == '>' && !flags.isDirtyLine)
+ int32_t c2;
+ if (!peekChar(&c2))
+ goto error;
+
+ if (c2 == '>' && !flags.isDirtyLine)
goto skipline;
+
tp->type = TOK_DEC;
} else {
tp->type = matchChar('=') ? TOK_SUBASSIGN : TOK_SUB;
@@ -1814,8 +1895,9 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
MOZ_CRASH("should have jumped to |out| or |error|");
out:
- if (flags.hitOOM)
- return reportError(JSMSG_OUT_OF_MEMORY);
+ if (flags.hitOOM) {
+ return false;
+ }
flags.isDirtyLine = true;
tp->pos.end = userbuf.offset();
@@ -1831,8 +1913,9 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
return true;
error:
- if (flags.hitOOM)
- return reportError(JSMSG_OUT_OF_MEMORY);
+ if (flags.hitOOM) {
+ return false;
+ }
flags.isDirtyLine = true;
tp->pos.end = userbuf.offset();
@@ -1850,37 +1933,6 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
}
bool
-TokenStream::getBracedUnicode(uint32_t* cp)
-{
- consumeKnownChar('{');
-
- bool first = true;
- int32_t c;
- uint32_t code = 0;
- while (true) {
- c = getCharIgnoreEOL();
- if (c == EOF)
- return false;
- if (c == '}') {
- if (first)
- return false;
- break;
- }
-
- if (!JS7_ISHEX(c))
- return false;
-
- code = (code << 4) | JS7_UNHEX(c);
- if (code > unicode::NonBMPMax)
- return false;
- first = false;
- }
-
- *cp = code;
- return true;
-}
-
-bool
TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
{
int c;
@@ -1897,11 +1949,15 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
while ((c = getCharIgnoreEOL()) != untilChar) {
if (c == EOF) {
ungetCharIgnoreEOL(c);
- reportError(JSMSG_UNTERMINATED_STRING);
+ error(JSMSG_UNTERMINATED_STRING);
return false;
}
if (c == '\\') {
+ // When parsing templates, we don't immediately report errors for
+ // invalid escapes; these are handled by the parser.
+ // In those cases we don't append to tokenbuf, since it won't be
+ // read.
switch (c = getChar()) {
case 'b': c = '\b'; break;
case 'f': c = '\f'; break;
@@ -1917,12 +1973,73 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
// Unicode character specification.
case 'u': {
- if (peekChar() == '{') {
- uint32_t code;
- if (!getBracedUnicode(&code)) {
- reportError(JSMSG_MALFORMED_ESCAPE, "Unicode");
- return false;
- }
+ uint32_t code = 0;
+
+ int32_t c2;
+ if (!peekChar(&c2))
+ return false;
+
+ uint32_t start = userbuf.offset() - 2;
+
+ if (c2 == '{') {
+ consumeKnownChar('{');
+
+ bool first = true;
+ bool valid = true;
+ do {
+ int32_t c = getCharIgnoreEOL();
+ if (c == EOF) {
+ if (parsingTemplate) {
+ setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
+ valid = false;
+ break;
+ }
+ reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
+ return false;
+ }
+ if (c == '}') {
+ if (first) {
+ if (parsingTemplate) {
+ setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
+ valid = false;
+ break;
+ }
+ reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
+ return false;
+ }
+ break;
+ }
+
+ if (!JS7_ISHEX(c)) {
+ if (parsingTemplate) {
+ // We put the character back so that we read
+ // it on the next pass, which matters if it
+ // was '`' or '\'.
+ ungetCharIgnoreEOL(c);
+ setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
+ valid = false;
+ break;
+ }
+ reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
+ return false;
+ }
+
+ code = (code << 4) | JS7_UNHEX(c);
+ if (code > unicode::NonBMPMax) {
+ if (parsingTemplate) {
+ setInvalidTemplateEscape(start + 3, InvalidEscapeType::UnicodeOverflow);
+ valid = false;
+ break;
+ }
+ reportInvalidEscapeError(start + 3, InvalidEscapeType::UnicodeOverflow);
+ return false;
+ }
+
+ first = false;
+ } while (true);
+
+ if (!valid)
+ continue;
MOZ_ASSERT(code <= unicode::NonBMPMax);
if (code < unicode::NonBMPMin) {
@@ -1945,7 +2062,11 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
c = (c << 4) + JS7_UNHEX(cp[3]);
skipChars(4);
} else {
- reportError(JSMSG_MALFORMED_ESCAPE, "Unicode");
+ if (parsingTemplate) {
+ setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
+ continue;
+ }
+ reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
return false;
}
break;
@@ -1958,7 +2079,12 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]);
skipChars(2);
} else {
- reportError(JSMSG_MALFORMED_ESCAPE, "hexadecimal");
+ uint32_t start = userbuf.offset() - 2;
+ if (parsingTemplate) {
+ setInvalidTemplateEscape(start, InvalidEscapeType::Hexadecimal);
+ continue;
+ }
+ reportInvalidEscapeError(start, InvalidEscapeType::Hexadecimal);
return false;
}
break;
@@ -1969,13 +2095,14 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
if (JS7_ISOCT(c)) {
int32_t val = JS7_UNOCT(c);
- c = peekChar();
+ if (!peekChar(&c))
+ return false;
// Strict mode code allows only \0, then a non-digit.
if (val != 0 || JS7_ISDEC(c)) {
if (parsingTemplate) {
- reportError(JSMSG_DEPRECATED_OCTAL);
- return false;
+ setInvalidTemplateEscape(userbuf.offset() - 2, InvalidEscapeType::Octal);
+ continue;
}
if (!reportStrictModeError(JSMSG_DEPRECATED_OCTAL))
return false;
@@ -1985,7 +2112,8 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
if (JS7_ISOCT(c)) {
val = 8 * val + JS7_UNOCT(c);
getChar();
- c = peekChar();
+ if (!peekChar(&c))
+ return false;
if (JS7_ISOCT(c)) {
int32_t save = val;
val = 8 * val + JS7_UNOCT(c);
@@ -2003,7 +2131,7 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
} else if (TokenBuf::isRawEOLChar(c)) {
if (!parsingTemplate) {
ungetCharIgnoreEOL(c);
- reportError(JSMSG_UNTERMINATED_STRING);
+ error(JSMSG_UNTERMINATED_STRING);
return false;
}
if (c == '\r') {
diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h
index 6ba9fba5a..2744fd144 100644
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -26,14 +26,13 @@
#include "js/UniquePtr.h"
#include "js/Vector.h"
#include "vm/RegExpObject.h"
+#include "vm/String.h"
struct KeywordInfo;
namespace js {
namespace frontend {
-class AutoAwaitIsKeyword;
-
struct TokenPos {
uint32_t begin; // Offset of the token's first char.
uint32_t end; // Offset of 1 past the token's last char.
@@ -80,6 +79,20 @@ struct TokenPos {
enum DecimalPoint { NoDecimal = false, HasDecimal = true };
+enum class InvalidEscapeType {
+ // No invalid character escapes.
+ None,
+ // A malformed \x escape.
+ Hexadecimal,
+ // A malformed \u escape.
+ Unicode,
+ // An otherwise well-formed \u escape which represents a
+ // codepoint > 10FFFF.
+ UnicodeOverflow,
+ // An octal escape in a template token.
+ Octal
+};
+
class TokenStream;
struct Token
@@ -106,9 +119,6 @@ struct Token
// TOK_DIV.
Operand,
- // Treat keywords as names by returning TOK_NAME.
- KeywordIsName,
-
// Treat subsequent characters as the tail of a template literal, after
// a template substitution, beginning with a "}", continuing with zero
// or more template literal characters, and ending with either "${" or
@@ -150,10 +160,6 @@ struct Token
// If a semicolon is inserted automatically, the next token is already
// gotten with None, but we expect Operand.
OperandIsNone,
-
- // If name of method definition is `get` or `set`, the next token is
- // already gotten with KeywordIsName, but we expect None.
- NoneIsKeywordIsName,
};
friend class TokenStream;
@@ -210,11 +216,6 @@ struct Token
return u.name->JSAtom::asPropertyName(); // poor-man's type verification
}
- bool nameContainsEscape() const {
- PropertyName* n = name();
- return pos.begin + n->length() != pos.end;
- }
-
JSAtom* atom() const {
MOZ_ASSERT(type == TOK_STRING ||
type == TOK_TEMPLATE_HEAD ||
@@ -240,10 +241,22 @@ struct Token
};
class CompileError : public JSErrorReport {
-public:
+ public:
void throwError(JSContext* cx);
};
+extern const char*
+ReservedWordToCharZ(PropertyName* str);
+
+extern MOZ_MUST_USE bool
+IsFutureReservedWord(JSLinearString* str);
+
+extern MOZ_MUST_USE bool
+IsReservedWordLiteral(JSLinearString* str);
+
+extern MOZ_MUST_USE bool
+IsStrictReservedWord(JSLinearString* str);
+
// Ideally, tokenizing would be entirely independent of context. But the
// strict mode flag, which is in SharedContext, affects tokenizing, and
// TokenStream needs to see it.
@@ -330,25 +343,26 @@ class MOZ_STACK_CLASS TokenStream
JSVersion versionNumber() const { return VersionNumber(options().version); }
JSVersion versionWithFlags() const { return options().version; }
+ private:
+ PropertyName* reservedWordToPropertyName(TokenKind tt) const;
+
+ public:
PropertyName* currentName() const {
- if (isCurrentTokenType(TOK_YIELD))
- return cx->names().yield;
- MOZ_ASSERT(isCurrentTokenType(TOK_NAME));
- return currentToken().name();
+ if (isCurrentTokenType(TOK_NAME)) {
+ return currentToken().name();
+ }
+
+ MOZ_ASSERT(TokenKindIsPossibleIdentifierName(currentToken().type));
+ return reservedWordToPropertyName(currentToken().type);
}
PropertyName* nextName() const {
- if (nextToken().type == TOK_YIELD)
- return cx->names().yield;
- MOZ_ASSERT(nextToken().type == TOK_NAME);
- return nextToken().name();
- }
+ if (nextToken().type != TOK_NAME) {
+ return nextToken().name();
+ }
- bool nextNameContainsEscape() const {
- if (nextToken().type == TOK_YIELD)
- return false;
- MOZ_ASSERT(nextToken().type == TOK_NAME);
- return nextToken().nameContainsEscape();
+ MOZ_ASSERT(TokenKindIsPossibleIdentifierName(nextToken().type));
+ return reservedWordToPropertyName(nextToken().type);
}
bool isCurrentTokenAssignment() const {
@@ -361,21 +375,47 @@ class MOZ_STACK_CLASS TokenStream
bool hadError() const { return flags.hadError; }
void clearSawOctalEscape() { flags.sawOctalEscape = false; }
+ bool hasInvalidTemplateEscape() const {
+ return invalidTemplateEscapeType != InvalidEscapeType::None;
+ }
+ void clearInvalidTemplateEscape() {
+ invalidTemplateEscapeType = InvalidEscapeType::None;
+ }
+
+ // If there is an invalid escape in a template, report it and return false,
+ // otherwise return true.
+ bool checkForInvalidTemplateEscapeError() {
+ if (invalidTemplateEscapeType == InvalidEscapeType::None)
+ return true;
+
+ reportInvalidEscapeError(invalidTemplateEscapeOffset, invalidTemplateEscapeType);
+ return false;
+ }
+
// TokenStream-specific error reporters.
bool reportError(unsigned errorNumber, ...);
bool reportErrorNoOffset(unsigned errorNumber, ...);
- bool reportWarning(unsigned errorNumber, ...);
+
+ // Report the given error at the current offset.
+ void error(unsigned errorNumber, ...);
+
+ // Report the given error at the given offset.
+ void errorAt(uint32_t offset, unsigned errorNumber, ...);
+
+ // Warn at the current offset.
+ MOZ_MUST_USE bool warning(unsigned errorNumber, ...);
static const uint32_t NoOffset = UINT32_MAX;
// General-purpose error reporters. You should avoid calling these
- // directly, and instead use the more succinct alternatives (e.g.
- // reportError()) in TokenStream, Parser, and BytecodeEmitter.
- bool reportCompileErrorNumberVA(uint32_t offset, unsigned flags, unsigned errorNumber,
- va_list args);
- bool reportStrictModeErrorNumberVA(uint32_t offset, bool strictMode, unsigned errorNumber,
- va_list args);
- bool reportExtraWarningErrorNumberVA(uint32_t offset, unsigned errorNumber, va_list args);
+ // directly, and instead use the more succinct alternatives (error(),
+ // warning(), &c.) in TokenStream, Parser, and BytecodeEmitter.
+ bool reportCompileErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset, unsigned flags,
+ unsigned errorNumber, va_list args);
+ bool reportStrictModeErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset,
+ bool strictMode, unsigned errorNumber, va_list args);
+ bool reportExtraWarningErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset,
+ unsigned errorNumber, va_list args);
// asm.js reporter
void reportAsmJSError(uint32_t offset, unsigned errorNumber, ...);
@@ -414,6 +454,33 @@ class MOZ_STACK_CLASS TokenStream
bool reportStrictModeError(unsigned errorNumber, ...);
bool strictMode() const { return strictModeGetter && strictModeGetter->strictMode(); }
+ void setInvalidTemplateEscape(uint32_t offset, InvalidEscapeType type) {
+ MOZ_ASSERT(type != InvalidEscapeType::None);
+ if (invalidTemplateEscapeType != InvalidEscapeType::None)
+ return;
+ invalidTemplateEscapeOffset = offset;
+ invalidTemplateEscapeType = type;
+ }
+ void reportInvalidEscapeError(uint32_t offset, InvalidEscapeType type) {
+ switch (type) {
+ case InvalidEscapeType::None:
+ MOZ_ASSERT_UNREACHABLE("unexpected InvalidEscapeType");
+ return;
+ case InvalidEscapeType::Hexadecimal:
+ errorAt(offset, JSMSG_MALFORMED_ESCAPE, "hexadecimal");
+ return;
+ case InvalidEscapeType::Unicode:
+ errorAt(offset, JSMSG_MALFORMED_ESCAPE, "Unicode");
+ return;
+ case InvalidEscapeType::UnicodeOverflow:
+ errorAt(offset, JSMSG_UNICODE_OVERFLOW, "escape sequence");
+ return;
+ case InvalidEscapeType::Octal:
+ errorAt(offset, JSMSG_DEPRECATED_OCTAL);
+ return;
+ }
+ }
+
static JSAtom* atomize(ExclusiveContext* cx, CharBuffer& cb);
MOZ_MUST_USE bool putIdentInTokenbuf(const char16_t* identStart);
@@ -431,21 +498,19 @@ class MOZ_STACK_CLASS TokenStream
{}
};
- bool awaitIsKeyword = false;
- friend class AutoAwaitIsKeyword;
+ uint32_t invalidTemplateEscapeOffset = 0;
+ InvalidEscapeType invalidTemplateEscapeType = InvalidEscapeType::None;
public:
typedef Token::Modifier Modifier;
static constexpr Modifier None = Token::None;
static constexpr Modifier Operand = Token::Operand;
- static constexpr Modifier KeywordIsName = Token::KeywordIsName;
static constexpr Modifier TemplateTail = Token::TemplateTail;
typedef Token::ModifierException ModifierException;
static constexpr ModifierException NoException = Token::NoException;
static constexpr ModifierException NoneIsOperand = Token::NoneIsOperand;
static constexpr ModifierException OperandIsNone = Token::OperandIsNone;
- static constexpr ModifierException NoneIsKeywordIsName = Token::NoneIsKeywordIsName;
void addModifierException(ModifierException modifierException) {
#ifdef DEBUG
@@ -474,10 +539,6 @@ class MOZ_STACK_CLASS TokenStream
MOZ_ASSERT(next.type != TOK_DIV && next.type != TOK_REGEXP,
"next token requires contextual specifier to be parsed unambiguously");
break;
- case NoneIsKeywordIsName:
- MOZ_ASSERT(next.modifier == KeywordIsName);
- MOZ_ASSERT(next.type != TOK_NAME);
- break;
default:
MOZ_CRASH("unexpected modifier exception");
}
@@ -504,18 +565,17 @@ class MOZ_STACK_CLASS TokenStream
return;
}
- if (lookaheadToken.modifierException == NoneIsKeywordIsName) {
- // getToken() permissibly following getToken(KeywordIsName).
- if (modifier == None && lookaheadToken.modifier == KeywordIsName)
- return;
- }
-
MOZ_ASSERT_UNREACHABLE("this token was previously looked up with a "
"different modifier, potentially making "
"tokenization non-deterministic");
#endif
}
+ const Token& nextToken() const {
+ MOZ_ASSERT(hasLookahead());
+ return tokens[(cursor + 1) & ntokensMask];
+ }
+
// Advance to the next token. If the token stream encountered an error,
// return false. Otherwise return true and store the token kind in |*ttp|.
MOZ_MUST_USE bool getToken(TokenKind* ttp, Modifier modifier = None) {
@@ -644,36 +704,6 @@ class MOZ_STACK_CLASS TokenStream
MOZ_ALWAYS_TRUE(matched);
}
- // Like matchToken(..., TOK_NAME) but further matching the name token only
- // if it has the given characters, without containing escape sequences.
- // If the name token has the given characters yet *does* contain an escape,
- // a syntax error will be reported.
- //
- // This latter behavior makes this method unsuitable for use in any context
- // where ASI might occur. In such places, an escaped "contextual keyword"
- // on a new line is the start of an ExpressionStatement, not a continuation
- // of a StatementListItem (or ImportDeclaration or ExportDeclaration, in
- // modules).
- MOZ_MUST_USE bool matchContextualKeyword(bool* matchedp, Handle<PropertyName*> keyword,
- Modifier modifier = None)
- {
- TokenKind token;
- if (!getToken(&token, modifier))
- return false;
- if (token == TOK_NAME && currentToken().name() == keyword) {
- if (currentToken().nameContainsEscape()) {
- reportError(JSMSG_ESCAPED_KEYWORD);
- return false;
- }
-
- *matchedp = true;
- } else {
- *matchedp = false;
- ungetToken();
- }
- return true;
- }
-
MOZ_MUST_USE bool nextTokenEndsExpr(bool* endsExpr) {
TokenKind tt;
if (!peekToken(&tt))
@@ -739,19 +769,6 @@ class MOZ_STACK_CLASS TokenStream
return sourceMapURL_.get();
}
- // If |atom| is not a keyword in this version, return true with *ttp
- // unchanged.
- //
- // If it is a reserved word in this version and strictness mode, and thus
- // can't be present in correct code, report a SyntaxError and return false.
- //
- // If it is a keyword, like "if", return true with the keyword's TokenKind
- // in *ttp.
- MOZ_MUST_USE bool checkForKeyword(JSAtom* atom, TokenKind* ttp);
-
- // Same semantics as above, but for the provided keyword.
- MOZ_MUST_USE bool checkForKeyword(const KeywordInfo* kw, TokenKind* ttp);
-
// This class maps a userbuf offset (which is 0-indexed) to a line number
// (which is 1-indexed) and a column index (which is 0-indexed).
class SourceCoords
@@ -947,7 +964,6 @@ class MOZ_STACK_CLASS TokenStream
MOZ_MUST_USE bool getTokenInternal(TokenKind* ttp, Modifier modifier);
- MOZ_MUST_USE bool getBracedUnicode(uint32_t* code);
MOZ_MUST_USE bool getStringOrTemplateToken(int untilChar, Token** tp);
int32_t getChar();
@@ -964,7 +980,7 @@ class MOZ_STACK_CLASS TokenStream
MOZ_MUST_USE bool getDirectives(bool isMultiline, bool shouldWarnDeprecated);
MOZ_MUST_USE bool getDirective(bool isMultiline, bool shouldWarnDeprecated,
- const char* directive, int directiveLength,
+ const char* directive, uint8_t directiveLength,
const char* errorMsgPragma,
UniquePtr<char16_t[], JS::FreePolicy>* destination);
MOZ_MUST_USE bool getDisplayURL(bool isMultiline, bool shouldWarnDeprecated);
@@ -982,29 +998,30 @@ class MOZ_STACK_CLASS TokenStream
MOZ_ASSERT(c == expect);
}
- int32_t peekChar() {
- int32_t c = getChar();
- ungetChar(c);
- return c;
+ MOZ_MUST_USE bool peekChar(int32_t* c) {
+ *c = getChar();
+ ungetChar(*c);
+ return true;
}
- void skipChars(int n) {
- while (--n >= 0)
- getChar();
+ void skipChars(uint8_t n) {
+ while (n-- > 0) {
+ MOZ_ASSERT(userbuf.hasRawChars());
+ mozilla::DebugOnly<int32_t> c = getCharIgnoreEOL();
+ MOZ_ASSERT(c != '\n');
+ }
}
- void skipCharsIgnoreEOL(int n) {
- while (--n >= 0)
+ void skipCharsIgnoreEOL(uint8_t n) {
+ while (n-- > 0) {
+ MOZ_ASSERT(userbuf.hasRawChars());
getCharIgnoreEOL();
+ }
}
void updateLineInfoForEOL();
void updateFlagsForEOL();
- const Token& nextToken() const {
- MOZ_ASSERT(hasLookahead());
- return tokens[(cursor + 1) & ntokensMask];
- }
bool hasLookahead() const { return lookahead > 0; }
@@ -1029,25 +1046,6 @@ class MOZ_STACK_CLASS TokenStream
StrictModeGetter* strictModeGetter; // used to test for strict mode
};
-class MOZ_STACK_CLASS AutoAwaitIsKeyword
-{
-private:
- TokenStream* ts_;
- bool oldAwaitIsKeyword_;
-
-public:
- AutoAwaitIsKeyword(TokenStream* ts, bool awaitIsKeyword) {
- ts_ = ts;
- oldAwaitIsKeyword_ = ts_->awaitIsKeyword;
- ts_->awaitIsKeyword = awaitIsKeyword;
- }
-
- ~AutoAwaitIsKeyword() {
- ts_->awaitIsKeyword = oldAwaitIsKeyword_;
- ts_ = nullptr;
- }
-};
-
extern const char*
TokenKindToDesc(TokenKind tt);
diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp
index 47d2314c1..da3ef7d0d 100644
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -2504,8 +2504,6 @@ js::TenuringTracer::moveObjectToTenured(JSObject* dst, JSObject* src, AllocKind
InlineTypedObject::objectMovedDuringMinorGC(this, dst, src);
} else if (src->is<TypedArrayObject>()) {
tenuredSize += TypedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
- } else if (src->is<UnboxedArrayObject>()) {
- tenuredSize += UnboxedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
} else if (src->is<ArgumentsObject>()) {
tenuredSize += ArgumentsObject::objectMovedDuringMinorGC(this, dst, src);
} else if (src->is<ProxyObject>()) {
diff --git a/js/src/irregexp/RegExpParser.cpp b/js/src/irregexp/RegExpParser.cpp
index ccc6ae3eb..8bd88047a 100644
--- a/js/src/irregexp/RegExpParser.cpp
+++ b/js/src/irregexp/RegExpParser.cpp
@@ -243,10 +243,10 @@ RegExpParser<CharT>::RegExpParser(frontend::TokenStream& ts, LifoAlloc* alloc,
template <typename CharT>
RegExpTree*
-RegExpParser<CharT>::ReportError(unsigned errorNumber)
+RegExpParser<CharT>::ReportError(unsigned errorNumber, const char* param /* = nullptr */)
{
gc::AutoSuppressGC suppressGC(ts.context());
- ts.reportError(errorNumber);
+ ts.reportError(errorNumber, param);
return nullptr;
}
@@ -350,7 +350,7 @@ RegExpParser<CharT>::ParseBracedHexEscape(widechar* value)
}
code = (code << 4) | d;
if (code > unicode::NonBMPMax) {
- ReportError(JSMSG_UNICODE_OVERFLOW);
+ ReportError(JSMSG_UNICODE_OVERFLOW, "regular expression");
return false;
}
Advance();
diff --git a/js/src/irregexp/RegExpParser.h b/js/src/irregexp/RegExpParser.h
index b5228a86f..0a7e61858 100644
--- a/js/src/irregexp/RegExpParser.h
+++ b/js/src/irregexp/RegExpParser.h
@@ -211,7 +211,7 @@ class RegExpParser
bool ParseBackReferenceIndex(int* index_out);
bool ParseClassAtom(char16_t* char_class, widechar *value);
- RegExpTree* ReportError(unsigned errorNumber);
+ RegExpTree* ReportError(unsigned errorNumber, const char* param = nullptr);
void Advance();
void Advance(int dist) {
next_pos_ += dist - 1;
diff --git a/js/src/jit-test/modules/export-default-async-asi.js b/js/src/jit-test/modules/export-default-async-asi.js
new file mode 100644
index 000000000..a69a7aa3d
--- /dev/null
+++ b/js/src/jit-test/modules/export-default-async-asi.js
@@ -0,0 +1,2 @@
+export default async // ASI occurs here due to the [no LineTerminator here] restriction on default-exporting an async function
+function async() { return 17; }
diff --git a/js/src/jit-test/tests/asm.js/import-function-toPrimitive.js b/js/src/jit-test/tests/asm.js/import-function-toPrimitive.js
new file mode 100644
index 000000000..aa529b465
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/import-function-toPrimitive.js
@@ -0,0 +1,26 @@
+var counter = 0;
+
+function f(stdlib, foreign)
+{
+ "use asm";
+ var a = +foreign.a;
+ var b = +foreign.b;
+ function g() {}
+ return g;
+}
+
+var foreign =
+ {
+ a: function() {},
+ b: /value that doesn't coerce purely/,
+ };
+
+foreign.a[Symbol.toPrimitive] =
+ function()
+ {
+ counter++;
+ return 0;
+ };
+
+f(null, foreign);
+assertEq(counter, 1);
diff --git a/js/src/jit-test/tests/baseline/bug1344334.js b/js/src/jit-test/tests/baseline/bug1344334.js
new file mode 100644
index 000000000..66994338a
--- /dev/null
+++ b/js/src/jit-test/tests/baseline/bug1344334.js
@@ -0,0 +1,14 @@
+if (!('oomTest' in this))
+ quit();
+
+function f(s) {
+ s + "x";
+ s.indexOf("y") === 0;
+ oomTest(new Function(s));
+}
+var s = `
+ class TestClass { constructor() {} }
+ for (var fun of hasPrototype) {}
+`;
+if (s.length)
+ f(s);
diff --git a/js/src/jit-test/tests/basic/bug713226.js b/js/src/jit-test/tests/basic/bug713226.js
index 3ae991f43..36858b86c 100644
--- a/js/src/jit-test/tests/basic/bug713226.js
+++ b/js/src/jit-test/tests/basic/bug713226.js
@@ -8,7 +8,7 @@ function addDebug(g, id) {\
var debuggerGlobal = newGlobal();\
debuggerGlobal.debuggee = g;\
debuggerGlobal.id = id;\
- debuggerGlobal.print = function (s) { (g) += s; };\
+ debuggerGlobal.print = function (s) { print(s); };\
debuggerGlobal.eval('var dbg = new Debugger(debuggee);dbg.onDebuggerStatement = function () { print(id); debugger; };');\
return debuggerGlobal;\
}\
diff --git a/js/src/jit-test/tests/basic/hasnativemethodpure-optimization.js b/js/src/jit-test/tests/basic/hasnativemethodpure-optimization.js
new file mode 100644
index 000000000..2f5e99186
--- /dev/null
+++ b/js/src/jit-test/tests/basic/hasnativemethodpure-optimization.js
@@ -0,0 +1,21 @@
+load(libdir + "asserts.js");
+
+let string = Object.defineProperty(new String("123"), "valueOf", {
+ get: function() { throw "get-valueOf"; }
+});
+assertThrowsValue(() => "" + string, "get-valueOf");
+
+string = Object.defineProperty(new String("123"), "toString", {
+ get: function() { throw "get-toString"; }
+});
+assertThrowsValue(() => string.toLowerCase(), "get-toString");
+
+string = Object.defineProperty(new String("123"), Symbol.toPrimitive, {
+ get: function() { throw "get-toPrimitive"; }
+});
+assertThrowsValue(() => string.toLowerCase(), "get-toPrimitive");
+
+let number = Object.defineProperty(new Number(123), "valueOf", {
+ get: function() { throw "get-valueOf"; }
+});
+assertThrowsValue(() => +number, "get-valueOf"); \ No newline at end of file
diff --git a/js/src/jit-test/tests/basic/testFunctionStatementAliasLocals.js b/js/src/jit-test/tests/basic/testFunctionStatementAliasLocals.js
index 7be49b7f3..be7f528b9 100644
--- a/js/src/jit-test/tests/basic/testFunctionStatementAliasLocals.js
+++ b/js/src/jit-test/tests/basic/testFunctionStatementAliasLocals.js
@@ -8,9 +8,11 @@ assertEq(typeof f1(true), "function");
assertEq(f1(false), 3);
function f2(b, w) {
+ // Annex B doesn't apply to functions in blocks with the same name as a
+ // parameter.
if (b)
function w() {}
return w;
}
-assertEq(typeof f2(true, 3), "function");
+assertEq(typeof f2(true, 3), "number");
assertEq(f2(false, 3), 3);
diff --git a/js/src/jit-test/tests/basic/unboxed-object-clear-new-script.js b/js/src/jit-test/tests/basic/unboxed-object-clear-new-script.js
deleted file mode 100644
index f55456222..000000000
--- a/js/src/jit-test/tests/basic/unboxed-object-clear-new-script.js
+++ /dev/null
@@ -1,49 +0,0 @@
-
-function Foo(a, b) {
- this.a = a;
- this.b = b;
-}
-
-function invalidate_foo() {
- var a = [];
- var counter = 0;
- for (var i = 0; i < 50; i++)
- a.push(new Foo(i, i + 1));
- Object.defineProperty(Foo.prototype, "a", {configurable: true, set: function() { counter++; }});
- for (var i = 0; i < 50; i++)
- a.push(new Foo(i, i + 1));
- delete Foo.prototype.a;
- var total = 0;
- for (var i = 0; i < a.length; i++) {
- assertEq('a' in a[i], i < 50);
- total += a[i].b;
- }
- assertEq(total, 2550);
- assertEq(counter, 50);
-}
-invalidate_foo();
-
-function Bar(a, b, fn) {
- this.a = a;
- if (b == 30)
- Object.defineProperty(Bar.prototype, "b", {configurable: true, set: fn});
- this.b = b;
-}
-
-function invalidate_bar() {
- var a = [];
- var counter = 0;
- function fn() { counter++; }
- for (var i = 0; i < 50; i++)
- a.push(new Bar(i, i + 1, fn));
- delete Bar.prototype.b;
- var total = 0;
- for (var i = 0; i < a.length; i++) {
- assertEq('a' in a[i], true);
- assertEq('b' in a[i], i < 29);
- total += a[i].a;
- }
- assertEq(total, 1225);
- assertEq(counter, 21);
-}
-invalidate_bar();
diff --git a/js/src/jit-test/tests/basic/unboxed-object-convert-to-native.js b/js/src/jit-test/tests/basic/unboxed-object-convert-to-native.js
deleted file mode 100644
index 691fe166c..000000000
--- a/js/src/jit-test/tests/basic/unboxed-object-convert-to-native.js
+++ /dev/null
@@ -1,47 +0,0 @@
-
-// Test various ways of converting an unboxed object to native.
-
-function Foo(a, b) {
- this.a = a;
- this.b = b;
-}
-
-var proxyObj = {
- get: function(recipient, name) {
- return recipient[name] + 2;
- }
-};
-
-function f() {
- var a = [];
- for (var i = 0; i < 50; i++)
- a.push(new Foo(i, i + 1));
-
- var prop = "a";
-
- i = 0;
- for (; i < 5; i++)
- a[i].c = i;
- for (; i < 10; i++)
- Object.defineProperty(a[i], 'c', {value: i});
- for (; i < 15; i++)
- a[i] = new Proxy(a[i], proxyObj);
- for (; i < 20; i++)
- a[i].a = 3.5;
- for (; i < 25; i++)
- delete a[i].b;
- for (; i < 30; i++)
- a[prop] = 4;
-
- var total = 0;
- for (i = 0; i < a.length; i++) {
- if ('a' in a[i])
- total += a[i].a;
- if ('b' in a[i])
- total += a[i].b;
- if ('c' in a[i])
- total += a[i].c;
- }
- assertEq(total, 2382.5);
-}
-f();
diff --git a/js/src/jit-test/tests/basic/unboxed-object-getelem.js b/js/src/jit-test/tests/basic/unboxed-object-getelem.js
deleted file mode 100644
index b30b8325a..000000000
--- a/js/src/jit-test/tests/basic/unboxed-object-getelem.js
+++ /dev/null
@@ -1,20 +0,0 @@
-
-function f() {
- var propNames = ["a","b","c","d","e","f","g","h","i","j","x","y"];
- var arr = [];
- for (var i=0; i<64; i++)
- arr.push({x:1, y:2});
- for (var i=0; i<64; i++) {
- // Make sure there are expandos with dynamic slots for each object.
- for (var j = 0; j < propNames.length; j++)
- arr[i][propNames[j]] = j;
- }
- var res = 0;
- for (var i=0; i<100000; i++) {
- var o = arr[i % 64];
- var p = propNames[i % propNames.length];
- res += o[p];
- }
- assertEq(res, 549984);
-}
-f();
diff --git a/js/src/jit-test/tests/basic/unboxed-object-set-property.js b/js/src/jit-test/tests/basic/unboxed-object-set-property.js
deleted file mode 100644
index fdcfcf6b2..000000000
--- a/js/src/jit-test/tests/basic/unboxed-object-set-property.js
+++ /dev/null
@@ -1,31 +0,0 @@
-
-// Use the correct receiver when non-native objects are prototypes of other objects.
-
-function Thing(a, b) {
- this.a = a;
- this.b = b;
-}
-
-function foo() {
- var array = [];
- for (var i = 0; i < 10000; i++)
- array.push(new Thing(i, i + 1));
-
- var proto = new Thing(1, 2);
- var obj = Object.create(proto);
-
- Object.defineProperty(Thing.prototype, "c", {set:function() { this.d = 0; }});
- obj.c = 3;
- assertEq(obj.c, undefined);
- assertEq(obj.d, 0);
- assertEq(obj.hasOwnProperty("d"), true);
- assertEq(proto.d, undefined);
- assertEq(proto.hasOwnProperty("d"), false);
-
- obj.a = 3;
- assertEq(obj.a, 3);
- assertEq(proto.a, 1);
- assertEq(obj.hasOwnProperty("a"), true);
-}
-
-foo();
diff --git a/js/src/jit-test/tests/basic/unboxed-property-enumeration.js b/js/src/jit-test/tests/basic/unboxed-property-enumeration.js
deleted file mode 100644
index 142d932dd..000000000
--- a/js/src/jit-test/tests/basic/unboxed-property-enumeration.js
+++ /dev/null
@@ -1,24 +0,0 @@
-function O() {
- this.x = 1;
- this.y = 2;
-}
-function testUnboxed() {
- var arr = [];
- for (var i=0; i<100; i++)
- arr.push(new O);
-
- var o = arr[arr.length-1];
- o[0] = 0;
- o[2] = 2;
- var sym = Symbol();
- o[sym] = 1;
- o.z = 3;
- Object.defineProperty(o, '3', {value:1,enumerable:false,configurable:false,writable:false});
- o[4] = 4;
-
- var props = Reflect.ownKeys(o);
- assertEq(props[props.length-1], sym);
-
- assertEq(Object.getOwnPropertyNames(o).join(""), "0234xyz");
-}
-testUnboxed();
diff --git a/js/src/jit-test/tests/class/bug1357506.js b/js/src/jit-test/tests/class/bug1357506.js
new file mode 100644
index 000000000..52a5643e6
--- /dev/null
+++ b/js/src/jit-test/tests/class/bug1357506.js
@@ -0,0 +1,8 @@
+// Test that constructors that abort due to asm.js do not assert due to the
+// parser keeping track of the FunctionBox corresponding to the constructor.
+
+class a {
+ constructor() {
+ "use asm";
+ }
+}
diff --git a/js/src/jit-test/tests/class/bug1359622.js b/js/src/jit-test/tests/class/bug1359622.js
new file mode 100644
index 000000000..b4a0df749
--- /dev/null
+++ b/js/src/jit-test/tests/class/bug1359622.js
@@ -0,0 +1,4 @@
+setDiscardSource(true)
+evaluate(`
+ unescape(class get { static staticMethod() {} });
+`);
diff --git a/js/src/jit-test/tests/debug/wasm-12.js b/js/src/jit-test/tests/debug/wasm-12.js
new file mode 100644
index 000000000..18d3b574d
--- /dev/null
+++ b/js/src/jit-test/tests/debug/wasm-12.js
@@ -0,0 +1,26 @@
+// Tests that wasm module scripts have special URLs.
+
+if (!wasmIsSupported())
+ quit();
+
+var g = newGlobal();
+g.eval(`
+function initWasm(s) { return new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(s))); }
+o = initWasm('(module (func) (export "" 0))');
+o2 = initWasm('(module (func) (func) (export "" 1))');
+`);
+
+function isWasm(script) { return script.format === "wasm"; }
+
+function isValidWasmURL(url) {
+ // The URLs will have the following format:
+ // wasm: [<uri-econded-filename-of-host> ":"] <64-bit-hash>
+ return /^wasm:(?:[^:]*:)*?[0-9a-f]{16}$/.test(url);
+}
+
+var dbg = new Debugger(g);
+var foundScripts = dbg.findScripts().filter(isWasm);
+assertEq(foundScripts.length, 2);
+assertEq(isValidWasmURL(foundScripts[0].source.url), true);
+assertEq(isValidWasmURL(foundScripts[1].source.url), true);
+assertEq(foundScripts[0].source.url != foundScripts[1].source.url, true);
diff --git a/js/src/jit-test/tests/ion/unboxed-objects-invalidate.js b/js/src/jit-test/tests/ion/unboxed-objects-invalidate.js
deleted file mode 100644
index 02e27614f..000000000
--- a/js/src/jit-test/tests/ion/unboxed-objects-invalidate.js
+++ /dev/null
@@ -1,16 +0,0 @@
-
-var a = [];
-for (var i = 0; i < 2000; i++)
- a.push({f:i});
-
-function f() {
- var total = 0;
- for (var i = 0; i < a.length; i++)
- total += a[i].f;
- return total;
-}
-assertEq(f(), 1999000);
-
-var sub = Object.create(a[0]);
-
-assertEq(f(), 1999000);
diff --git a/js/src/jit-test/tests/modules/export-declaration.js b/js/src/jit-test/tests/modules/export-declaration.js
index 3c4a9b735..9925f2c68 100644
--- a/js/src/jit-test/tests/modules/export-declaration.js
+++ b/js/src/jit-test/tests/modules/export-declaration.js
@@ -403,12 +403,52 @@ assertThrowsInstanceOf(function() {
parseAsModule("export {,} from 'a'");
}, SyntaxError);
+program([
+ exportDeclaration(
+ null,
+ [
+ exportSpecifier(
+ ident("true"),
+ ident("true")
+ ),
+ ],
+ lit("b"),
+ false
+ )
+]).assert(parseAsModule("export { true } from 'b'"));
+
+program([
+ exportDeclaration(
+ null,
+ [
+ exportSpecifier(
+ ident("true"),
+ ident("name")
+ ),
+ ],
+ lit("b"),
+ false
+ )
+]).assert(parseAsModule("export { true as name } from 'b'"));
+
+assertThrowsInstanceOf(function() {
+ parseAsModule("export { true }");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function() {
+ parseAsModule("export { true as name }");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function() {
+ parseAsModule("export { static }");
+}, SyntaxError);
+
assertThrowsInstanceOf(function() {
- parseAsModule("export { true as a } from 'b'");
+ parseAsModule("export { static as name }");
}, SyntaxError);
assertThrowsInstanceOf(function () {
- parseAsModule("export { a } from 'b' f();");
+ parseAsModule("export { name } from 'b' f();");
}, SyntaxError);
assertThrowsInstanceOf(function () {
diff --git a/js/src/jit-test/tests/modules/function-redeclaration.js b/js/src/jit-test/tests/modules/function-redeclaration.js
new file mode 100644
index 000000000..b84704641
--- /dev/null
+++ b/js/src/jit-test/tests/modules/function-redeclaration.js
@@ -0,0 +1,94 @@
+load(libdir + "asserts.js");
+
+var functionDeclarations = [
+ "function f(){}",
+ "function* f(){}",
+ "async function f(){}",
+];
+
+var varDeclarations = [
+ "var f",
+ "{ var f; }",
+ "for (var f in null);",
+ "for (var f of null);",
+ "for (var f; ;);",
+];
+
+var lexicalDeclarations = [
+ "let f;",
+ "const f = 0;",
+ "class f {};",
+];
+
+var imports = [
+ "import f from '';",
+ "import f, {} from '';",
+ "import d, {f} from '';",
+ "import d, {f as f} from '';",
+ "import d, {foo as f} from '';",
+ "import f, * as d from '';",
+ "import d, * as f from '';",
+ "import {f} from '';",
+ "import {f as f} from '';",
+ "import {foo as f} from '';",
+ "import* as f from '';",
+];
+
+var exports = [
+ "export var f;",
+ ...functionDeclarations.map(fn => `export ${fn};`),
+ ...lexicalDeclarations.map(ld => `export ${ld};`),
+ ...functionDeclarations.map(fn => `export default ${fn};`),
+ "export default class f {};",
+];
+
+var redeclarations = [
+ ...functionDeclarations,
+ ...varDeclarations,
+ ...lexicalDeclarations,
+ ...imports,
+ ...exports,
+];
+
+var noredeclarations = [
+ ...functionDeclarations.map(fn => `{ ${fn} }`),
+ ...lexicalDeclarations.map(ld => `{ ${ld} }`),
+ ...["let", "const"].map(ld => `for (${ld} f in null);`),
+ ...["let", "const"].map(ld => `for (${ld} f of null);`),
+ ...["let", "const"].map(ld => `for (${ld} f = 0; ;);`),
+ "export {f};",
+ "export {f as f};",
+ "export {foo as f}; var foo;",
+ "export {f} from '';",
+ "export {f as f} from '';",
+ "export {foo as f} from '';",
+];
+
+for (var decl of functionDeclarations) {
+ for (var redecl of redeclarations) {
+ assertThrowsInstanceOf(() => {
+ parseModule(`
+ ${decl}
+ ${redecl}
+ `);
+ }, SyntaxError);
+
+ assertThrowsInstanceOf(() => {
+ parseModule(`
+ ${redecl}
+ ${decl}
+ `);
+ }, SyntaxError);
+ }
+
+ for (var redecl of noredeclarations) {
+ parseModule(`
+ ${decl}
+ ${redecl}
+ `);
+ parseModule(`
+ ${redecl}
+ ${decl}
+ `);
+ }
+}
diff --git a/js/src/jit-test/tests/parser/arrow-rest.js b/js/src/jit-test/tests/parser/arrow-rest.js
index 53750f25b..b1429066e 100644
--- a/js/src/jit-test/tests/parser/arrow-rest.js
+++ b/js/src/jit-test/tests/parser/arrow-rest.js
@@ -39,7 +39,7 @@ testThrow(`
testThrow(`
({...a)=>
-`, 2);
+`, 6);
testThrow(`
function f([... ...a)=>
@@ -47,7 +47,7 @@ function f([... ...a)=>
testThrow(`
function f({...a)=>
-`, 12);
+`, 16);
// arrow
@@ -67,7 +67,7 @@ var [... ...a)=>
testThrow(`
var {...a)=>
-`, 5);
+`, 9);
// initializer
diff --git a/js/src/jit-test/tests/parser/missing-closing-brace.js b/js/src/jit-test/tests/parser/missing-closing-brace.js
new file mode 100644
index 000000000..6820954ae
--- /dev/null
+++ b/js/src/jit-test/tests/parser/missing-closing-brace.js
@@ -0,0 +1,90 @@
+function test(source, [lineNumber, columnNumber], openType = "{", closeType = "}") {
+ let caught = false;
+ try {
+ Reflect.parse(source, { source: "foo.js" });
+ } catch (e) {
+ assertEq(e.message.includes("missing " + closeType + " "), true);
+ let notes = getErrorNotes(e);
+ assertEq(notes.length, 1);
+ let note = notes[0];
+ assertEq(note.message, openType + " opened at line " + lineNumber + ", column " + columnNumber);
+ assertEq(note.fileName, "foo.js");
+ assertEq(note.lineNumber, lineNumber);
+ assertEq(note.columnNumber, columnNumber);
+ caught = true;
+ }
+ assertEq(caught, true);
+}
+
+// Function
+
+test(`
+function test1() {
+}
+function test2() {
+ if (true) {
+ //}
+}
+function test3() {
+}
+`, [4, 17]);
+
+// Block statement.
+test(`
+{
+ if (true) {
+}
+`, [2, 0]);
+test(`
+if (true) {
+ if (true) {
+}
+`, [2, 10]);
+test(`
+for (;;) {
+ if (true) {
+}
+`, [2, 9]);
+test(`
+while (true) {
+ if (true) {
+}
+`, [2, 13]);
+test(`
+do {
+ do {
+} while(true);
+`, [2, 3]);
+
+// try-catch-finally.
+test(`
+try {
+ if (true) {
+}
+`, [2, 4]);
+test(`
+try {
+} catch (e) {
+ if (true) {
+}
+`, [3, 12]);
+test(`
+try {
+} finally {
+ if (true) {
+}
+`, [3, 10]);
+
+// Object literal.
+test(`
+var x = {
+ foo: {
+};
+`, [2, 8]);
+
+// Array literal.
+test(`
+var x = [
+ [
+];
+`, [2, 8], "[", "]");
diff --git a/js/src/jit-test/tests/parser/redeclaration.js b/js/src/jit-test/tests/parser/redeclaration.js
new file mode 100644
index 000000000..f719021ac
--- /dev/null
+++ b/js/src/jit-test/tests/parser/redeclaration.js
@@ -0,0 +1,230 @@
+// Error message for redeclaration should show the position where the variable
+// was declared.
+
+const npos = -1;
+
+function test_one(fun, filename, name,
+ [prevLineNumber, prevColumnNumber],
+ [lineNumber, columnNumber]) {
+ let caught = false;
+ try {
+ fun();
+ } catch (e) {
+ assertEq(e.message.includes("redeclaration"), true);
+ assertEq(e.lineNumber, lineNumber);
+ assertEq(e.columnNumber, columnNumber);
+ let notes = getErrorNotes(e);
+ if (prevLineNumber == npos) {
+ assertEq(notes.length, 0);
+ } else {
+ assertEq(notes.length, 1);
+ let note = notes[0];
+ assertEq(note.message,
+ `Previously declared at line ${prevLineNumber}, column ${prevColumnNumber}`);
+ assertEq(note.lineNumber, prevLineNumber);
+ assertEq(note.columnNumber, prevColumnNumber);
+ if (filename)
+ assertEq(note.fileName, filename);
+ }
+ caught = true;
+ }
+ assertEq(caught, true);
+}
+
+function test_parse(source, ...args) {
+ test_one(() => {
+ Reflect.parse(source, { source: "foo.js" });
+ }, "foo.js", ...args);
+}
+
+function test_eval(source, ...args) {
+ test_one(() => {
+ eval(source);
+ }, undefined, ...args);
+}
+
+function test(...args) {
+ test_parse(...args);
+ test_eval(...args);
+}
+
+// let
+
+test(`
+let a, a;
+`, "a", [2, 4], [2, 7]);
+
+test(`
+let a;
+let a;
+`, "a", [2, 4], [3, 4]);
+
+test(`
+let a;
+const a = 1;
+`, "a", [2, 4], [3, 6]);
+
+test(`
+let a;
+var a;
+`, "a", [2, 4], [3, 4]);
+
+test(`
+let a;
+function a() {
+}
+`, "a", [2, 4], [3, 9]);
+
+test(`
+{
+ let a;
+ function a() {
+ }
+}
+`, "a", [3, 6], [4, 11]);
+
+// const
+
+test(`
+const a = 1, a = 2;
+`, "a", [2, 6], [2, 13]);
+
+test(`
+const a = 1;
+const a = 2;
+`, "a", [2, 6], [3, 6]);
+
+test(`
+const a = 1;
+let a;
+`, "a", [2, 6], [3, 4]);
+
+test(`
+const a = 1;
+var a;
+`, "a", [2, 6], [3, 4]);
+
+test(`
+const a = 1;
+function a() {
+}
+`, "a", [2, 6], [3, 9]);
+
+test(`
+{
+ const a = 1;
+ function a() {
+ }
+}
+`, "a", [3, 8], [4, 11]);
+
+// var
+
+test(`
+var a;
+let a;
+`, "a", [2, 4], [3, 4]);
+
+test(`
+var a;
+const a = 1;
+`, "a", [2, 4], [3, 6]);
+
+// function
+
+test(`
+function a() {};
+let a;
+`, "a", [2, 9], [3, 4]);
+
+test(`
+function a() {};
+const a = 1;
+`, "a", [2, 9], [3, 6]);
+
+// Annex B lexical function
+
+test(`
+{
+ function a() {};
+ let a;
+}
+`, "a", [3, 11], [4, 6]);
+
+test(`
+{
+ function a() {};
+ const a = 1;
+}
+`, "a", [3, 11], [4, 8]);
+
+// catch parameter
+
+test(`
+try {
+} catch (a) {
+ let a;
+}
+`, "a", [3, 9], [4, 6]);
+
+test(`
+try {
+} catch (a) {
+ const a = 1;
+}
+`, "a", [3, 9], [4, 8]);
+
+test(`
+try {
+} catch (a) {
+ function a() {
+ }
+}
+`, "a", [3, 9], [4, 11]);
+
+// parameter
+
+test(`
+function f(a) {
+ let a;
+}
+`, "a", [2, 11], [3, 6]);
+
+test(`
+function f(a) {
+ const a = 1;
+}
+`, "a", [2, 11], [3, 8]);
+
+test(`
+function f([a]) {
+ let a;
+}
+`, "a", [2, 12], [3, 6]);
+
+test(`
+function f({a}) {
+ let a;
+}
+`, "a", [2, 12], [3, 6]);
+
+test(`
+function f(...a) {
+ let a;
+}
+`, "a", [2, 14], [3, 6]);
+
+test(`
+function f(a=1) {
+ let a;
+}
+`, "a", [2, 11], [3, 6]);
+
+// eval
+// We don't have position information at runtime.
+// No note should be shown.
+
+test_eval(`
+let a;
+eval("var a");
+`, "a", [npos, npos], [1, 4]);
diff --git a/js/src/jit-test/tests/profiler/AutoEntryMonitor-01.js b/js/src/jit-test/tests/profiler/AutoEntryMonitor-01.js
index d66548680..e9dd5d526 100644
--- a/js/src/jit-test/tests/profiler/AutoEntryMonitor-01.js
+++ b/js/src/jit-test/tests/profiler/AutoEntryMonitor-01.js
@@ -31,7 +31,7 @@ cold_and_warm(Object.prototype.toString, { ToString: {} }, []);
var toS = { toString: function myToString() { return "string"; } };
cold_and_warm(toS.toString, { ToString: toS }, [ "myToString" ]);
-cold_and_warm(undefined, { ToNumber: {} }, []);
+cold_and_warm(undefined, { ToNumber: 5 }, []);
var vOf = { valueOf: function myValueOf() { return 42; } };
cold_and_warm(vOf.valueOf, { ToNumber: vOf }, [ "myValueOf" ]);
diff --git a/js/src/jit/AliasAnalysisShared.cpp b/js/src/jit/AliasAnalysisShared.cpp
index 3b83df74e..0f0d4a66a 100644
--- a/js/src/jit/AliasAnalysisShared.cpp
+++ b/js/src/jit/AliasAnalysisShared.cpp
@@ -93,10 +93,6 @@ GetObject(const MDefinition* ins)
case MDefinition::Op_Elements:
case MDefinition::Op_MaybeCopyElementsForWrite:
case MDefinition::Op_MaybeToDoubleElement:
- case MDefinition::Op_UnboxedArrayLength:
- case MDefinition::Op_UnboxedArrayInitializedLength:
- case MDefinition::Op_IncrementUnboxedArrayInitializedLength:
- case MDefinition::Op_SetUnboxedArrayInitializedLength:
case MDefinition::Op_TypedArrayLength:
case MDefinition::Op_SetTypedObjectOffset:
case MDefinition::Op_SetDisjointTypedElements:
diff --git a/js/src/jit/BaselineCacheIR.cpp b/js/src/jit/BaselineCacheIR.cpp
index bf96932d1..67c80473b 100644
--- a/js/src/jit/BaselineCacheIR.cpp
+++ b/js/src/jit/BaselineCacheIR.cpp
@@ -16,7 +16,7 @@ using namespace js;
using namespace js::jit;
// OperandLocation represents the location of an OperandId. The operand is
-// either in a register or on the stack, and is either boxed or unboxed.
+// either in a register or on the stack.
class OperandLocation
{
public:
@@ -787,9 +787,6 @@ BaselineCacheIRCompiler::emitGuardClass()
case GuardClassKind::Array:
clasp = &ArrayObject::class_;
break;
- case GuardClassKind::UnboxedArray:
- clasp = &UnboxedArrayObject::class_;
- break;
case GuardClassKind::MappedArguments:
clasp = &MappedArgumentsObject::class_;
break;
@@ -818,36 +815,6 @@ BaselineCacheIRCompiler::emitGuardSpecificObject()
}
bool
-BaselineCacheIRCompiler::emitGuardNoUnboxedExpando()
-{
- Register obj = allocator.useRegister(masm, reader.objOperandId());
-
- FailurePath* failure;
- if (!addFailurePath(&failure))
- return false;
-
- Address expandoAddr(obj, UnboxedPlainObject::offsetOfExpando());
- masm.branchPtr(Assembler::NotEqual, expandoAddr, ImmWord(0), failure->label());
- return true;
-}
-
-bool
-BaselineCacheIRCompiler::emitGuardAndLoadUnboxedExpando()
-{
- Register obj = allocator.useRegister(masm, reader.objOperandId());
- Register output = allocator.defineRegister(masm, reader.objOperandId());
-
- FailurePath* failure;
- if (!addFailurePath(&failure))
- return false;
-
- Address expandoAddr(obj, UnboxedPlainObject::offsetOfExpando());
- masm.loadPtr(expandoAddr, output);
- masm.branchTestPtr(Assembler::Zero, output, output, failure->label());
- return true;
-}
-
-bool
BaselineCacheIRCompiler::emitLoadFixedSlotResult()
{
Register obj = allocator.useRegister(masm, reader.objOperandId());
@@ -874,26 +841,6 @@ BaselineCacheIRCompiler::emitLoadDynamicSlotResult()
}
bool
-BaselineCacheIRCompiler::emitLoadUnboxedPropertyResult()
-{
- Register obj = allocator.useRegister(masm, reader.objOperandId());
- AutoScratchRegister scratch(allocator, masm);
-
- JSValueType fieldType = reader.valueType();
-
- Address fieldOffset(stubAddress(reader.stubOffset()));
- masm.load32(fieldOffset, scratch);
- masm.loadUnboxedProperty(BaseIndex(obj, scratch, TimesOne), fieldType, R0);
-
- if (fieldType == JSVAL_TYPE_OBJECT)
- emitEnterTypeMonitorIC();
- else
- emitReturnFromIC();
-
- return true;
-}
-
-bool
BaselineCacheIRCompiler::emitGuardNoDetachedTypedObjects()
{
FailurePath* failure;
@@ -1004,19 +951,6 @@ BaselineCacheIRCompiler::emitLoadInt32ArrayLengthResult()
}
bool
-BaselineCacheIRCompiler::emitLoadUnboxedArrayLengthResult()
-{
- Register obj = allocator.useRegister(masm, reader.objOperandId());
- masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), R0.scratchReg());
- masm.tagValue(JSVAL_TYPE_INT32, R0.scratchReg(), R0);
-
- // The int32 type was monitored when attaching the stub, so we can
- // just return.
- emitReturnFromIC();
- return true;
-}
-
-bool
BaselineCacheIRCompiler::emitLoadArgumentsObjectLengthResult()
{
Register obj = allocator.useRegister(masm, reader.objOperandId());
diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp
index 3fa5a80ed..b2f9d3b23 100644
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -2049,13 +2049,7 @@ BaselineCompiler::emit_JSOP_NEWARRAY()
return true;
}
-bool
-BaselineCompiler::emit_JSOP_SPREADCALLARRAY()
-{
- return emit_JSOP_NEWARRAY();
-}
-
-typedef JSObject* (*NewArrayCopyOnWriteFn)(JSContext*, HandleArrayObject, gc::InitialHeap);
+typedef ArrayObject* (*NewArrayCopyOnWriteFn)(JSContext*, HandleArrayObject, gc::InitialHeap);
const VMFunction jit::NewArrayCopyOnWriteInfo =
FunctionInfo<NewArrayCopyOnWriteFn>(js::NewDenseCopyOnWriteArray, "NewDenseCopyOnWriteArray");
@@ -3298,6 +3292,12 @@ BaselineCompiler::emit_JSOP_CALL()
}
bool
+BaselineCompiler::emit_JSOP_CALL_IGNORES_RV()
+{
+ return emitCall();
+}
+
+bool
BaselineCompiler::emit_JSOP_CALLITER()
{
return emitCall();
@@ -4136,14 +4136,14 @@ BaselineCompiler::emit_JSOP_REST()
{
frame.syncStack(0);
- JSObject* templateObject =
+ ArrayObject* templateObject =
ObjectGroup::newArrayObject(cx, nullptr, 0, TenuredObject,
ObjectGroup::NewArrayKind::UnknownIndex);
if (!templateObject)
return false;
// Call IC.
- ICRest_Fallback::Compiler compiler(cx, &templateObject->as<ArrayObject>());
+ ICRest_Fallback::Compiler compiler(cx, templateObject);
if (!emitOpIC(compiler.getStub(&stubSpace_)))
return false;
diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h
index 6b5bf009e..95e0c77ad 100644
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -100,7 +100,6 @@ namespace jit {
_(JSOP_BITNOT) \
_(JSOP_NEG) \
_(JSOP_NEWARRAY) \
- _(JSOP_SPREADCALLARRAY) \
_(JSOP_NEWARRAY_COPYONWRITE) \
_(JSOP_INITELEM_ARRAY) \
_(JSOP_NEWOBJECT) \
@@ -160,6 +159,7 @@ namespace jit {
_(JSOP_INITALIASEDLEXICAL) \
_(JSOP_UNINITIALIZED) \
_(JSOP_CALL) \
+ _(JSOP_CALL_IGNORES_RV) \
_(JSOP_CALLITER) \
_(JSOP_FUNCALL) \
_(JSOP_FUNAPPLY) \
diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp
index 863c61161..a001357f8 100644
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -10,6 +10,8 @@
#include "mozilla/SizePrintfMacros.h"
#include "mozilla/TemplateLib.h"
+#include "jsfriendapi.h"
+#include "jsfun.h"
#include "jslibmath.h"
#include "jstypes.h"
@@ -42,8 +44,8 @@
#include "jit/shared/Lowering-shared-inl.h"
#include "vm/EnvironmentObject-inl.h"
#include "vm/Interpreter-inl.h"
+#include "vm/NativeObject-inl.h"
#include "vm/StringObject-inl.h"
-#include "vm/UnboxedObject-inl.h"
using mozilla::DebugOnly;
@@ -289,7 +291,7 @@ DoTypeUpdateFallback(JSContext* cx, BaselineFrame* frame, ICUpdatedStub* stub, H
case ICStub::SetProp_Native:
case ICStub::SetProp_NativeAdd:
case ICStub::SetProp_Unboxed: {
- MOZ_ASSERT(obj->isNative() || obj->is<UnboxedPlainObject>());
+ MOZ_ASSERT(obj->isNative());
jsbytecode* pc = stub->getChainFallback()->icEntry()->pc(script);
if (*pc == JSOP_SETALIASEDVAR || *pc == JSOP_INITALIASEDLEXICAL)
id = NameToId(EnvironmentCoordinateName(cx->caches.envCoordinateNameCache, script, pc));
@@ -321,7 +323,14 @@ DoTypeUpdateFallback(JSContext* cx, BaselineFrame* frame, ICUpdatedStub* stub, H
MOZ_CRASH("Invalid stub");
}
- return stub->addUpdateStubForValue(cx, script /* = outerScript */, obj, id, value);
+ if (!stub->addUpdateStubForValue(cx, script /* = outerScript */, obj, id, value)) {
+ // The calling JIT code assumes this function is infallible (for
+ // instance we may reallocate dynamic slots before calling this),
+ // so ignore OOMs if we failed to attach a stub.
+ cx->recoverFromOutOfMemory();
+ }
+
+ return true;
}
typedef bool (*DoTypeUpdateFallbackFn)(JSContext*, BaselineFrame*, ICUpdatedStub*, HandleValue,
@@ -732,11 +741,6 @@ LastPropertyForSetProp(JSObject* obj)
if (obj->isNative())
return obj->as<NativeObject>().lastProperty();
- if (obj->is<UnboxedPlainObject>()) {
- UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
- return expando ? expando->lastProperty() : nullptr;
- }
-
return nullptr;
}
@@ -1153,56 +1157,6 @@ TryAttachNativeOrUnboxedGetValueElemStub(JSContext* cx, HandleScript script, jsb
ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
- if (obj->is<UnboxedPlainObject>() && holder == obj) {
- const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
-
- // Once unboxed objects support symbol-keys, we need to change the following accordingly
- MOZ_ASSERT_IF(!keyVal.isString(), !property);
-
- if (property) {
- if (!cx->runtime()->jitSupportsFloatingPoint)
- return true;
-
- RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
- ICGetElemNativeCompiler<PropertyName*> compiler(cx, ICStub::GetElem_UnboxedPropertyName,
- monitorStub, obj, holder,
- name,
- ICGetElemNativeStub::UnboxedProperty,
- needsAtomize, property->offset +
- UnboxedPlainObject::offsetOfData(),
- property->type);
- ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
- if (!newStub)
- return false;
-
- stub->addNewStub(newStub);
- *attached = true;
- return true;
- }
-
- Shape* shape = obj->as<UnboxedPlainObject>().maybeExpando()->lookup(cx, id);
- if (!shape->hasDefaultGetter() || !shape->hasSlot())
- return true;
-
- bool isFixedSlot;
- uint32_t offset;
- GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
-
- ICGetElemNativeStub::AccessType acctype =
- isFixedSlot ? ICGetElemNativeStub::FixedSlot
- : ICGetElemNativeStub::DynamicSlot;
- ICGetElemNativeCompiler<T> compiler(cx, getGetElemStubKind<T>(ICStub::GetElem_NativeSlotName),
- monitorStub, obj, holder, key,
- acctype, needsAtomize, offset);
- ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
- if (!newStub)
- return false;
-
- stub->addNewStub(newStub);
- *attached = true;
- return true;
- }
-
if (!holder->isNative())
return true;
@@ -1366,7 +1320,7 @@ IsNativeDenseElementAccess(HandleObject obj, HandleValue key)
static bool
IsNativeOrUnboxedDenseElementAccess(HandleObject obj, HandleValue key)
{
- if (!obj->isNative() && !obj->is<UnboxedArrayObject>())
+ if (!obj->isNative())
return false;
if (key.isInt32() && key.toInt32() >= 0 && !obj->is<TypedArrayObject>())
return true;
@@ -1450,7 +1404,7 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_
}
// Check for NativeObject[id] and UnboxedPlainObject[id] shape-optimizable accesses.
- if (obj->isNative() || obj->is<UnboxedPlainObject>()) {
+ if (obj->isNative()) {
RootedScript rootedScript(cx, script);
if (rhs.isString()) {
if (!TryAttachNativeOrUnboxedGetValueElemStub<PropertyName*>(cx, rootedScript, pc, stub,
@@ -1470,20 +1424,6 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_
script = rootedScript;
}
- // Check for UnboxedArray[int] accesses.
- if (obj->is<UnboxedArrayObject>() && rhs.isInt32() && rhs.toInt32() >= 0) {
- JitSpew(JitSpew_BaselineIC, " Generating GetElem(UnboxedArray[Int32]) stub");
- ICGetElem_UnboxedArray::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
- obj->group());
- ICStub* unboxedStub = compiler.getStub(compiler.getStubSpace(script));
- if (!unboxedStub)
- return false;
-
- stub->addNewStub(unboxedStub);
- *attached = true;
- return true;
- }
-
// Check for TypedArray[int] => Number and TypedObject[int] => Number accesses.
if ((obj->is<TypedArrayObject>() || IsPrimitiveArrayTypedObject(obj)) &&
rhs.isNumber() &&
@@ -1876,14 +1816,6 @@ ICGetElemNativeCompiler<T>::generateStubCode(MacroAssembler& masm)
Register holderReg;
if (obj_ == holder_) {
holderReg = objReg;
-
- if (obj_->is<UnboxedPlainObject>() && acctype_ != ICGetElemNativeStub::UnboxedProperty) {
- // The property will be loaded off the unboxed expando.
- masm.push(R1.scratchReg());
- popR1 = true;
- holderReg = R1.scratchReg();
- masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg);
- }
} else {
// Shape guard holder.
if (regs.empty()) {
@@ -1934,13 +1866,6 @@ ICGetElemNativeCompiler<T>::generateStubCode(MacroAssembler& masm)
if (popR1)
masm.addToStackPtr(ImmWord(sizeof(size_t)));
- } else if (acctype_ == ICGetElemNativeStub::UnboxedProperty) {
- masm.load32(Address(ICStubReg, ICGetElemNativeSlotStub<T>::offsetOfOffset()),
- scratchReg);
- masm.loadUnboxedProperty(BaseIndex(objReg, scratchReg, TimesOne), unboxedType_,
- TypedOrValueRegister(R0));
- if (popR1)
- masm.addToStackPtr(ImmWord(sizeof(size_t)));
} else {
MOZ_ASSERT(acctype_ == ICGetElemNativeStub::NativeGetter ||
acctype_ == ICGetElemNativeStub::ScriptedGetter);
@@ -2090,56 +2015,6 @@ ICGetElem_Dense::Compiler::generateStubCode(MacroAssembler& masm)
}
//
-// GetElem_UnboxedArray
-//
-
-bool
-ICGetElem_UnboxedArray::Compiler::generateStubCode(MacroAssembler& masm)
-{
- MOZ_ASSERT(engine_ == Engine::Baseline);
-
- Label failure;
- masm.branchTestObject(Assembler::NotEqual, R0, &failure);
- masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
-
- AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
- Register scratchReg = regs.takeAny();
-
- // Unbox R0 and group guard.
- Register obj = masm.extractObject(R0, ExtractTemp0);
- masm.loadPtr(Address(ICStubReg, ICGetElem_UnboxedArray::offsetOfGroup()), scratchReg);
- masm.branchTestObjGroup(Assembler::NotEqual, obj, scratchReg, &failure);
-
- // Unbox key.
- Register key = masm.extractInt32(R1, ExtractTemp1);
-
- // Bounds check.
- masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()),
- scratchReg);
- masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg);
- masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure);
-
- // Load obj->elements.
- masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg);
-
- // Load value.
- size_t width = UnboxedTypeSize(elementType_);
- BaseIndex addr(scratchReg, key, ScaleFromElemWidth(width));
- masm.loadUnboxedProperty(addr, elementType_, R0);
-
- // Only monitor the result if its type might change.
- if (elementType_ == JSVAL_TYPE_OBJECT)
- EmitEnterTypeMonitorIC(masm);
- else
- EmitReturnFromIC(masm);
-
- // Failure case - jump to next stub
- masm.bind(&failure);
- EmitStubGuardFailure(masm);
- return true;
-}
-
-//
// GetElem_TypedArray
//
@@ -2387,14 +2262,20 @@ DenseOrUnboxedArraySetElemStubExists(JSContext* cx, ICStub::Kind kind,
for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
if (kind == ICStub::SetElem_DenseOrUnboxedArray && iter->isSetElem_DenseOrUnboxedArray()) {
ICSetElem_DenseOrUnboxedArray* nstub = iter->toSetElem_DenseOrUnboxedArray();
- if (obj->maybeShape() == nstub->shape() && obj->getGroup(cx) == nstub->group())
+ if (obj->maybeShape() == nstub->shape() &&
+ JSObject::getGroup(cx, obj) == nstub->group())
+ {
return true;
+ }
}
if (kind == ICStub::SetElem_DenseOrUnboxedArrayAdd && iter->isSetElem_DenseOrUnboxedArrayAdd()) {
ICSetElem_DenseOrUnboxedArrayAdd* nstub = iter->toSetElem_DenseOrUnboxedArrayAdd();
- if (obj->getGroup(cx) == nstub->group() && SetElemAddHasSameShapes(nstub, obj))
+ if (JSObject::getGroup(cx, obj) == nstub->group() &&
+ SetElemAddHasSameShapes(nstub, obj))
+ {
return true;
+ }
}
}
return false;
@@ -2437,8 +2318,8 @@ CanOptimizeDenseOrUnboxedArraySetElem(JSObject* obj, uint32_t index,
Shape* oldShape, uint32_t oldCapacity, uint32_t oldInitLength,
bool* isAddingCaseOut, size_t* protoDepthOut)
{
- uint32_t initLength = GetAnyBoxedOrUnboxedInitializedLength(obj);
- uint32_t capacity = GetAnyBoxedOrUnboxedCapacity(obj);
+ uint32_t initLength = obj->as<NativeObject>().getDenseInitializedLength();
+ uint32_t capacity = obj->as<NativeObject>().getDenseCapacity();
*isAddingCaseOut = false;
*protoDepthOut = 0;
@@ -2447,10 +2328,6 @@ CanOptimizeDenseOrUnboxedArraySetElem(JSObject* obj, uint32_t index,
if (initLength < oldInitLength || capacity < oldCapacity)
return false;
- // Unboxed arrays need to be able to emit floating point code.
- if (obj->is<UnboxedArrayObject>() && !obj->runtimeFromMainThread()->jitSupportsFloatingPoint)
- return false;
-
Shape* shape = obj->maybeShape();
// Cannot optimize if the shape changed.
@@ -2532,8 +2409,8 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_
uint32_t oldCapacity = 0;
uint32_t oldInitLength = 0;
if (index.isInt32() && index.toInt32() >= 0) {
- oldCapacity = GetAnyBoxedOrUnboxedCapacity(obj);
- oldInitLength = GetAnyBoxedOrUnboxedInitializedLength(obj);
+ oldCapacity = obj->as<NativeObject>().getDenseCapacity();
+ oldInitLength = obj->as<NativeObject>().getDenseInitializedLength();
}
if (op == JSOP_INITELEM || op == JSOP_INITHIDDENELEM) {
@@ -2584,7 +2461,7 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_
&addingCase, &protoDepth))
{
RootedShape shape(cx, obj->maybeShape());
- RootedObjectGroup group(cx, obj->getGroup(cx));
+ RootedObjectGroup group(cx, JSObject::getGroup(cx, obj));
if (!group)
return false;
@@ -2741,18 +2618,6 @@ BaselineScript::noteArrayWriteHole(uint32_t pcOffset)
// SetElem_DenseOrUnboxedArray
//
-template <typename T>
-void
-EmitUnboxedPreBarrierForBaseline(MacroAssembler &masm, T address, JSValueType type)
-{
- if (type == JSVAL_TYPE_OBJECT)
- EmitPreBarrier(masm, address, MIRType::Object);
- else if (type == JSVAL_TYPE_STRING)
- EmitPreBarrier(masm, address, MIRType::String);
- else
- MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(type));
-}
-
bool
ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm)
{
@@ -2871,29 +2736,6 @@ ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm)
masm.loadValue(valueAddr, tmpVal);
EmitPreBarrier(masm, element, MIRType::Value);
masm.storeValue(tmpVal, element);
- } else {
- // Set element on an unboxed array.
-
- // Bounds check.
- Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
- masm.load32(initLength, scratchReg);
- masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg);
- masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure);
-
- // Load obj->elements.
- masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg);
-
- // Compute the address being written to.
- BaseIndex address(scratchReg, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_)));
-
- EmitUnboxedPreBarrierForBaseline(masm, address, unboxedType_);
-
- Address valueAddr(masm.getStackPointer(), ICStackValueOffset + sizeof(Value));
- masm.Push(R0);
- masm.loadValue(valueAddr, R0);
- masm.storeUnboxedProperty(address, unboxedType_,
- ConstantOrRegister(TypedOrValueRegister(R0)), &failurePopR0);
- masm.Pop(R0);
}
EmitReturnFromIC(masm);
@@ -3087,40 +2929,6 @@ ICSetElemDenseOrUnboxedArrayAddCompiler::generateStubCode(MacroAssembler& masm)
BaseIndex element(scratchReg, key, TimesEight);
masm.loadValue(valueAddr, tmpVal);
masm.storeValue(tmpVal, element);
- } else {
- // Adding element to an unboxed array.
-
- // Bounds check (key == initLength)
- Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
- masm.load32(initLengthAddr, scratchReg);
- masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg);
- masm.branch32(Assembler::NotEqual, scratchReg, key, &failure);
-
- // Capacity check.
- masm.checkUnboxedArrayCapacity(obj, RegisterOrInt32Constant(key), scratchReg, &failure);
-
- // Load obj->elements.
- masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg);
-
- // Write the value first, since this can fail. No need for pre-barrier
- // since we're not overwriting an old value.
- masm.Push(R0);
- Address valueAddr(masm.getStackPointer(), ICStackValueOffset + sizeof(Value));
- masm.loadValue(valueAddr, R0);
- BaseIndex address(scratchReg, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_)));
- masm.storeUnboxedProperty(address, unboxedType_,
- ConstantOrRegister(TypedOrValueRegister(R0)), &failurePopR0);
- masm.Pop(R0);
-
- // Increment initialized length.
- masm.add32(Imm32(1), initLengthAddr);
-
- // If length is now <= key, increment length.
- Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength());
- Label skipIncrementLength;
- masm.branch32(Assembler::Above, lengthAddr, key, &skipIncrementLength);
- masm.add32(Imm32(1), lengthAddr);
- masm.bind(&skipIncrementLength);
}
EmitReturnFromIC(masm);
@@ -4256,18 +4064,7 @@ TryAttachSetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, IC
return true;
if (!obj->isNative()) {
- if (obj->is<UnboxedPlainObject>()) {
- UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
- if (expando) {
- shape = expando->lookup(cx, name);
- if (!shape)
- return true;
- } else {
- return true;
- }
- } else {
- return true;
- }
+ return true;
}
size_t chainDepth;
@@ -4418,40 +4215,6 @@ TryAttachSetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecode* pc,
}
static bool
-TryAttachUnboxedSetPropStub(JSContext* cx, HandleScript script,
- ICSetProp_Fallback* stub, HandleId id,
- HandleObject obj, HandleValue rhs, bool* attached)
-{
- MOZ_ASSERT(!*attached);
-
- if (!cx->runtime()->jitSupportsFloatingPoint)
- return true;
-
- if (!obj->is<UnboxedPlainObject>())
- return true;
-
- const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
- if (!property)
- return true;
-
- ICSetProp_Unboxed::Compiler compiler(cx, obj->group(),
- property->offset + UnboxedPlainObject::offsetOfData(),
- property->type);
- ICUpdatedStub* newStub = compiler.getStub(compiler.getStubSpace(script));
- if (!newStub)
- return false;
- if (compiler.needsUpdateStubs() && !newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
- return false;
-
- stub->addNewStub(newStub);
-
- StripPreliminaryObjectStubs(cx, stub);
-
- *attached = true;
- return true;
-}
-
-static bool
TryAttachTypedObjectSetPropStub(JSContext* cx, HandleScript script,
ICSetProp_Fallback* stub, HandleId id,
HandleObject obj, HandleValue rhs, bool* attached)
@@ -4529,17 +4292,11 @@ DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_
if (!obj)
return false;
RootedShape oldShape(cx, obj->maybeShape());
- RootedObjectGroup oldGroup(cx, obj->getGroup(cx));
+ RootedObjectGroup oldGroup(cx, JSObject::getGroup(cx, obj));
if (!oldGroup)
return false;
RootedReceiverGuard oldGuard(cx, ReceiverGuard(obj));
- if (obj->is<UnboxedPlainObject>()) {
- MOZ_ASSERT(!oldShape);
- if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
- oldShape = expando->lastProperty();
- }
-
bool attached = false;
// There are some reasons we can fail to attach a stub that are temporary.
// We want to avoid calling noteUnoptimizableAccess() if the reason we
@@ -4612,15 +4369,6 @@ DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_
if (!attached &&
lhs.isObject() &&
- !TryAttachUnboxedSetPropStub(cx, script, stub, id, obj, rhs, &attached))
- {
- return false;
- }
- if (attached)
- return true;
-
- if (!attached &&
- lhs.isObject() &&
!TryAttachTypedObjectSetPropStub(cx, script, stub, id, obj, rhs, &attached))
{
return false;
@@ -4703,20 +4451,7 @@ GuardGroupAndShapeMaybeUnboxedExpando(MacroAssembler& masm, JSObject* obj,
// Guard against shape or expando shape.
masm.loadPtr(Address(ICStubReg, offsetOfShape), scratch);
- if (obj->is<UnboxedPlainObject>()) {
- Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
- masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure);
- Label done;
- masm.push(object);
- masm.loadPtr(expandoAddress, object);
- masm.branchTestObjShape(Assembler::Equal, object, scratch, &done);
- masm.pop(object);
- masm.jump(failure);
- masm.bind(&done);
- masm.pop(object);
- } else {
- masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure);
- }
+ masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure);
}
bool
@@ -4755,13 +4490,7 @@ ICSetProp_Native::Compiler::generateStubCode(MacroAssembler& masm)
regs.takeUnchecked(objReg);
Register holderReg;
- if (obj_->is<UnboxedPlainObject>()) {
- // We are loading off the expando object, so use that for the holder.
- holderReg = regs.takeAny();
- masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg);
- if (!isFixedSlot_)
- masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg);
- } else if (isFixedSlot_) {
+ if (isFixedSlot_) {
holderReg = objReg;
} else {
holderReg = regs.takeAny();
@@ -4898,31 +4627,17 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler& masm)
regs.add(R0);
regs.takeUnchecked(objReg);
- if (obj_->is<UnboxedPlainObject>()) {
- holderReg = regs.takeAny();
- masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg);
-
- // Write the expando object's new shape.
- Address shapeAddr(holderReg, ShapedObject::offsetOfShape());
- EmitPreBarrier(masm, shapeAddr, MIRType::Shape);
- masm.loadPtr(Address(ICStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch);
- masm.storePtr(scratch, shapeAddr);
+ // Write the object's new shape.
+ Address shapeAddr(objReg, ShapedObject::offsetOfShape());
+ EmitPreBarrier(masm, shapeAddr, MIRType::Shape);
+ masm.loadPtr(Address(ICStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch);
+ masm.storePtr(scratch, shapeAddr);
- if (!isFixedSlot_)
- masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg);
+ if (isFixedSlot_) {
+ holderReg = objReg;
} else {
- // Write the object's new shape.
- Address shapeAddr(objReg, ShapedObject::offsetOfShape());
- EmitPreBarrier(masm, shapeAddr, MIRType::Shape);
- masm.loadPtr(Address(ICStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch);
- masm.storePtr(scratch, shapeAddr);
-
- if (isFixedSlot_) {
- holderReg = objReg;
- } else {
- holderReg = regs.takeAny();
- masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg);
- }
+ holderReg = regs.takeAny();
+ masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg);
}
// Perform the store. No write barrier required since this is a new
@@ -4954,70 +4669,6 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler& masm)
}
bool
-ICSetProp_Unboxed::Compiler::generateStubCode(MacroAssembler& masm)
-{
- MOZ_ASSERT(engine_ == Engine::Baseline);
-
- Label failure;
-
- // Guard input is an object.
- masm.branchTestObject(Assembler::NotEqual, R0, &failure);
-
- AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
- Register scratch = regs.takeAny();
-
- // Unbox and group guard.
- Register object = masm.extractObject(R0, ExtractTemp0);
- masm.loadPtr(Address(ICStubReg, ICSetProp_Unboxed::offsetOfGroup()), scratch);
- masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), scratch,
- &failure);
-
- if (needsUpdateStubs()) {
- // Stow both R0 and R1 (object and value).
- EmitStowICValues(masm, 2);
-
- // Move RHS into R0 for TypeUpdate check.
- masm.moveValue(R1, R0);
-
- // Call the type update stub.
- if (!callTypeUpdateIC(masm, sizeof(Value)))
- return false;
-
- // Unstow R0 and R1 (object and key)
- EmitUnstowICValues(masm, 2);
-
- // The TypeUpdate IC may have smashed object. Rederive it.
- masm.unboxObject(R0, object);
-
- // Trigger post barriers here on the values being written. Fields which
- // objects can be written to also need update stubs.
- LiveGeneralRegisterSet saveRegs;
- saveRegs.add(R0);
- saveRegs.add(R1);
- saveRegs.addUnchecked(object);
- saveRegs.add(ICStubReg);
- emitPostWriteBarrierSlot(masm, object, R1, scratch, saveRegs);
- }
-
- // Compute the address being written to.
- masm.load32(Address(ICStubReg, ICSetProp_Unboxed::offsetOfFieldOffset()), scratch);
- BaseIndex address(object, scratch, TimesOne);
-
- EmitUnboxedPreBarrierForBaseline(masm, address, fieldType_);
- masm.storeUnboxedProperty(address, fieldType_,
- ConstantOrRegister(TypedOrValueRegister(R1)), &failure);
-
- // The RHS has to be in R0.
- masm.moveValue(R1, R0);
-
- EmitReturnFromIC(masm);
-
- masm.bind(&failure);
- EmitStubGuardFailure(masm);
- return true;
-}
-
-bool
ICSetProp_TypedObject::Compiler::generateStubCode(MacroAssembler& masm)
{
MOZ_ASSERT(engine_ == Engine::Baseline);
@@ -5490,13 +5141,6 @@ GetTemplateObjectForSimd(JSContext* cx, JSFunction* target, MutableHandleObject
return true;
}
-static void
-EnsureArrayGroupAnalyzed(JSContext* cx, JSObject* obj)
-{
- if (PreliminaryObjectArrayWithTemplate* objects = obj->group()->maybePreliminaryObjects())
- objects->maybeAnalyze(cx, obj->group(), /* forceAnalyze = */ true);
-}
-
static bool
GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs& args,
MutableHandleObject res, bool* skipAttach)
@@ -5528,10 +5172,7 @@ GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs&
// With this and other array templates, analyze the group so that
// we don't end up with a template whose structure might change later.
res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, count, TenuredObject));
- if (!res)
- return false;
- EnsureArrayGroupAnalyzed(cx, res);
- return true;
+ return !!res;
}
}
@@ -5549,18 +5190,14 @@ GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs&
if (native == js::array_slice) {
if (args.thisv().isObject()) {
- JSObject* obj = &args.thisv().toObject();
+ RootedObject obj(cx, &args.thisv().toObject());
if (!obj->isSingleton()) {
if (obj->group()->maybePreliminaryObjects()) {
*skipAttach = true;
return true;
}
- res.set(NewFullyAllocatedArrayTryReuseGroup(cx, &args.thisv().toObject(), 0,
- TenuredObject));
- if (!res)
- return false;
- EnsureArrayGroupAnalyzed(cx, res);
- return true;
+ res.set(NewFullyAllocatedArrayTryReuseGroup(cx, obj, 0, TenuredObject));
+ return !!res;
}
}
}
@@ -5577,10 +5214,7 @@ GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs&
}
res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, 0, TenuredObject));
- if (!res)
- return false;
- EnsureArrayGroupAnalyzed(cx, res);
- return true;
+ return !!res;
}
if (native == StringConstructor) {
@@ -5793,7 +5427,7 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
if (!thisObject)
return false;
- if (thisObject->is<PlainObject>() || thisObject->is<UnboxedPlainObject>())
+ if (thisObject->is<PlainObject>())
templateObject = thisObject;
}
@@ -5869,11 +5503,17 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
MOZ_ASSERT_IF(templateObject, !templateObject->group()->maybePreliminaryObjects());
}
+ bool ignoresReturnValue = false;
+ if (op == JSOP_CALL_IGNORES_RV && fun->isNative()) {
+ const JSJitInfo* jitInfo = fun->jitInfo();
+ ignoresReturnValue = jitInfo && jitInfo->type() == JSJitInfo::IgnoresReturnValueNative;
+ }
+
JitSpew(JitSpew_BaselineIC, " Generating Call_Native stub (fun=%p, cons=%s, spread=%s)",
fun.get(), constructing ? "yes" : "no", isSpread ? "yes" : "no");
ICCall_Native::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
- fun, templateObject, constructing, isSpread,
- script->pcToOffset(pc));
+ fun, templateObject, constructing, ignoresReturnValue,
+ isSpread, script->pcToOffset(pc));
ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
if (!newStub)
return false;
@@ -5887,15 +5527,24 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
}
static bool
-CopyArray(JSContext* cx, HandleObject obj, MutableHandleValue result)
+CopyArray(JSContext* cx, HandleArrayObject arr, MutableHandleValue result)
{
- uint32_t length = GetAnyBoxedOrUnboxedArrayLength(obj);
- JSObject* nobj = NewFullyAllocatedArrayTryReuseGroup(cx, obj, length, TenuredObject);
+ uint32_t length = arr->length();
+ ArrayObject* nobj = NewFullyAllocatedArrayTryReuseGroup(cx, arr, length, TenuredObject);
if (!nobj)
return false;
- EnsureArrayGroupAnalyzed(cx, nobj);
- CopyAnyBoxedOrUnboxedDenseElements(cx, nobj, obj, 0, 0, length);
-
+
+ MOZ_ASSERT(arr->isNative());
+ MOZ_ASSERT(nobj->isNative());
+ MOZ_ASSERT(nobj->as<NativeObject>().getDenseInitializedLength() == 0);
+ MOZ_ASSERT(arr->as<NativeObject>().getDenseInitializedLength() >= length);
+ MOZ_ASSERT(nobj->as<NativeObject>().getDenseCapacity() >= length);
+
+ nobj->as<NativeObject>().setDenseInitializedLength(length);
+
+ const Value* vp = arr->as<NativeObject>().getDenseElements();
+ nobj->as<NativeObject>().initDenseElements(0, vp, length);
+
result.setObject(*nobj);
return true;
}
@@ -5926,26 +5575,22 @@ TryAttachStringSplit(JSContext* cx, ICCall_Fallback* stub, HandleScript script,
RootedValue arr(cx);
// Copy the array before storing in stub.
- if (!CopyArray(cx, obj, &arr))
+ if (!CopyArray(cx, obj.as<ArrayObject>(), &arr))
return false;
// Atomize all elements of the array.
- RootedObject arrObj(cx, &arr.toObject());
- uint32_t initLength = GetAnyBoxedOrUnboxedArrayLength(arrObj);
+ RootedArrayObject arrObj(cx, &arr.toObject().as<ArrayObject>());
+ uint32_t initLength = arrObj->length();
for (uint32_t i = 0; i < initLength; i++) {
- JSAtom* str = js::AtomizeString(cx, GetAnyBoxedOrUnboxedDenseElement(arrObj, i).toString());
+ JSAtom* str = js::AtomizeString(cx, arrObj->getDenseElement(i).toString());
if (!str)
return false;
- if (!SetAnyBoxedOrUnboxedDenseElement(cx, arrObj, i, StringValue(str))) {
- // The value could not be stored to an unboxed dense element.
- return true;
- }
+ arrObj->setDenseElementWithType(cx, i, StringValue(str));
}
ICCall_StringSplit::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
- script->pcToOffset(pc), str, sep,
- arr);
+ script->pcToOffset(pc), str, sep, arrObj);
ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
if (!newStub)
return false;
@@ -5971,12 +5616,14 @@ DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint
MOZ_ASSERT(argc == GET_ARGC(pc));
bool constructing = (op == JSOP_NEW);
+ bool ignoresReturnValue = (op == JSOP_CALL_IGNORES_RV);
// Ensure vp array is rooted - we may GC in here.
size_t numValues = argc + 2 + constructing;
AutoArrayRooter vpRoot(cx, numValues, vp);
- CallArgs callArgs = CallArgsFromSp(argc + constructing, vp + numValues, constructing);
+ CallArgs callArgs = CallArgsFromSp(argc + constructing, vp + numValues, constructing,
+ ignoresReturnValue);
RootedValue callee(cx, vp[0]);
// Handle funapply with JSOP_ARGUMENTS
@@ -6006,6 +5653,7 @@ DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint
return false;
} else {
MOZ_ASSERT(op == JSOP_CALL ||
+ op == JSOP_CALL_IGNORES_RV ||
op == JSOP_CALLITER ||
op == JSOP_FUNCALL ||
op == JSOP_FUNAPPLY ||
@@ -6830,7 +6478,7 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm)
return true;
}
-typedef bool (*CopyArrayFn)(JSContext*, HandleObject, MutableHandleValue);
+typedef bool (*CopyArrayFn)(JSContext*, HandleArrayObject, MutableHandleValue);
static const VMFunction CopyArrayInfo = FunctionInfo<CopyArrayFn>(CopyArray, "CopyArray");
bool
@@ -7056,7 +6704,12 @@ ICCall_Native::Compiler::generateStubCode(MacroAssembler& masm)
// stub and use that instead of the original one.
masm.callWithABI(Address(ICStubReg, ICCall_Native::offsetOfNative()));
#else
- masm.callWithABI(Address(callee, JSFunction::offsetOfNativeOrScript()));
+ if (ignoresReturnValue_) {
+ masm.loadPtr(Address(callee, JSFunction::offsetOfJitInfo()), callee);
+ masm.callWithABI(Address(callee, JSJitInfo::offsetOfIgnoresReturnValueNative()));
+ } else {
+ masm.callWithABI(Address(callee, JSFunction::offsetOfNativeOrScript()));
+ }
#endif
// Test for failure.
@@ -8301,19 +7954,6 @@ ICGetElem_Dense::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorSt
return New<ICGetElem_Dense>(cx, space, other.jitCode(), firstMonitorStub, other.shape_);
}
-ICGetElem_UnboxedArray::ICGetElem_UnboxedArray(JitCode* stubCode, ICStub* firstMonitorStub,
- ObjectGroup *group)
- : ICMonitoredStub(GetElem_UnboxedArray, stubCode, firstMonitorStub),
- group_(group)
-{ }
-
-/* static */ ICGetElem_UnboxedArray*
-ICGetElem_UnboxedArray::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
- ICGetElem_UnboxedArray& other)
-{
- return New<ICGetElem_UnboxedArray>(cx, space, other.jitCode(), firstMonitorStub, other.group_);
-}
-
ICGetElem_TypedArray::ICGetElem_TypedArray(JitCode* stubCode, Shape* shape, Scalar::Type type)
: ICStub(GetElem_TypedArray, stubCode),
shape_(shape)
@@ -8349,7 +7989,7 @@ ICUpdatedStub*
ICSetElemDenseOrUnboxedArrayAddCompiler::getStubSpecific(ICStubSpace* space,
Handle<ShapeVector> shapes)
{
- RootedObjectGroup group(cx, obj_->getGroup(cx));
+ RootedObjectGroup group(cx, JSObject::getGroup(cx, obj_));
if (!group)
return nullptr;
Rooted<JitCode*> stubCode(cx, getStubCode());
@@ -8486,7 +8126,7 @@ ICSetProp_Native::ICSetProp_Native(JitCode* stubCode, ObjectGroup* group, Shape*
ICSetProp_Native*
ICSetProp_Native::Compiler::getStub(ICStubSpace* space)
{
- RootedObjectGroup group(cx, obj_->getGroup(cx));
+ RootedObjectGroup group(cx, JSObject::getGroup(cx, obj_));
if (!group)
return nullptr;
@@ -8689,8 +8329,8 @@ static bool DoRestFallback(JSContext* cx, BaselineFrame* frame, ICRest_Fallback*
unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0;
Value* rest = frame->argv() + numFormals;
- JSObject* obj = ObjectGroup::newArrayObject(cx, rest, numRest, GenericObject,
- ObjectGroup::NewArrayKind::UnknownIndex);
+ ArrayObject* obj = ObjectGroup::newArrayObject(cx, rest, numRest, GenericObject,
+ ObjectGroup::NewArrayKind::UnknownIndex);
if (!obj)
return false;
res.setObject(*obj);
diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h
index a57556d99..e1ad12559 100644
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -22,7 +22,6 @@
#include "jit/SharedICRegisters.h"
#include "js/GCVector.h"
#include "vm/ArrayObject.h"
-#include "vm/UnboxedObject.h"
namespace js {
namespace jit {
@@ -892,54 +891,6 @@ class ICGetElem_Dense : public ICMonitoredStub
};
};
-class ICGetElem_UnboxedArray : public ICMonitoredStub
-{
- friend class ICStubSpace;
-
- GCPtrObjectGroup group_;
-
- ICGetElem_UnboxedArray(JitCode* stubCode, ICStub* firstMonitorStub, ObjectGroup* group);
-
- public:
- static ICGetElem_UnboxedArray* Clone(JSContext* cx, ICStubSpace* space,
- ICStub* firstMonitorStub, ICGetElem_UnboxedArray& other);
-
- static size_t offsetOfGroup() {
- return offsetof(ICGetElem_UnboxedArray, group_);
- }
-
- GCPtrObjectGroup& group() {
- return group_;
- }
-
- class Compiler : public ICStubCompiler {
- ICStub* firstMonitorStub_;
- RootedObjectGroup group_;
- JSValueType elementType_;
-
- protected:
- MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
-
- virtual int32_t getKey() const {
- return static_cast<int32_t>(engine_) |
- (static_cast<int32_t>(kind) << 1) |
- (static_cast<int32_t>(elementType_) << 17);
- }
-
- public:
- Compiler(JSContext* cx, ICStub* firstMonitorStub, ObjectGroup* group)
- : ICStubCompiler(cx, ICStub::GetElem_UnboxedArray, Engine::Baseline),
- firstMonitorStub_(firstMonitorStub),
- group_(cx, group),
- elementType_(group->unboxedLayoutDontCheckGeneration().elementType())
- {}
-
- ICStub* getStub(ICStubSpace* space) {
- return newStub<ICGetElem_UnboxedArray>(space, getStubCode(), firstMonitorStub_, group_);
- }
- };
-};
-
// Accesses scalar elements of a typed array or typed object.
class ICGetElem_TypedArray : public ICStub
{
@@ -1115,9 +1066,7 @@ class ICSetElem_DenseOrUnboxedArray : public ICUpdatedStub
: ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArray, Engine::Baseline),
shape_(cx, shape),
group_(cx, group),
- unboxedType_(shape
- ? JSVAL_TYPE_MAGIC
- : group->unboxedLayoutDontCheckGeneration().elementType())
+ unboxedType_(JSVAL_TYPE_MAGIC)
{}
ICUpdatedStub* getStub(ICStubSpace* space) {
@@ -1225,9 +1174,7 @@ class ICSetElemDenseOrUnboxedArrayAddCompiler : public ICStubCompiler {
: ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArrayAdd, Engine::Baseline),
obj_(cx, obj),
protoChainDepth_(protoChainDepth),
- unboxedType_(obj->is<UnboxedArrayObject>()
- ? obj->as<UnboxedArrayObject>().elementType()
- : JSVAL_TYPE_MAGIC)
+ unboxedType_(JSVAL_TYPE_MAGIC)
{}
template <size_t ProtoChainDepth>
@@ -1875,8 +1822,7 @@ class ICSetProp_Native : public ICUpdatedStub
virtual int32_t getKey() const {
return static_cast<int32_t>(engine_) |
(static_cast<int32_t>(kind) << 1) |
- (static_cast<int32_t>(isFixedSlot_) << 17) |
- (static_cast<int32_t>(obj_->is<UnboxedPlainObject>()) << 18);
+ (static_cast<int32_t>(isFixedSlot_) << 17);
}
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
@@ -1981,7 +1927,6 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler
return static_cast<int32_t>(engine_) |
(static_cast<int32_t>(kind) << 1) |
(static_cast<int32_t>(isFixedSlot_) << 17) |
- (static_cast<int32_t>(obj_->is<UnboxedPlainObject>()) << 18) |
(static_cast<int32_t>(protoChainDepth_) << 19);
}
@@ -1995,7 +1940,7 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler
template <size_t ProtoChainDepth>
ICUpdatedStub* getStubSpecific(ICStubSpace* space, Handle<ShapeVector> shapes)
{
- RootedObjectGroup newGroup(cx, obj_->getGroup(cx));
+ RootedObjectGroup newGroup(cx, JSObject::getGroup(cx, obj_));
if (!newGroup)
return nullptr;
@@ -2006,10 +1951,7 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler
newGroup = nullptr;
RootedShape newShape(cx);
- if (obj_->isNative())
- newShape = obj_->as<NativeObject>().lastProperty();
- else
- newShape = obj_->as<UnboxedPlainObject>().maybeExpando()->lastProperty();
+ newShape = obj_->as<NativeObject>().lastProperty();
return newStub<ICSetProp_NativeAddImpl<ProtoChainDepth>>(
space, getStubCode(), oldGroup_, shapes, newShape, newGroup, offset_);
@@ -2335,6 +2277,7 @@ class ICSetProp_CallNative : public ICSetPropCallSetter
// Call
// JSOP_CALL
+// JSOP_CALL_IGNORES_RV
// JSOP_FUNAPPLY
// JSOP_FUNCALL
// JSOP_NEW
@@ -2605,6 +2548,7 @@ class ICCall_Native : public ICMonitoredStub
protected:
ICStub* firstMonitorStub_;
bool isConstructing_;
+ bool ignoresReturnValue_;
bool isSpread_;
RootedFunction callee_;
RootedObject templateObject_;
@@ -2614,17 +2558,19 @@ class ICCall_Native : public ICMonitoredStub
virtual int32_t getKey() const {
return static_cast<int32_t>(engine_) |
(static_cast<int32_t>(kind) << 1) |
- (static_cast<int32_t>(isConstructing_) << 17) |
- (static_cast<int32_t>(isSpread_) << 18);
+ (static_cast<int32_t>(isSpread_) << 17) |
+ (static_cast<int32_t>(isConstructing_) << 18) |
+ (static_cast<int32_t>(ignoresReturnValue_) << 19);
}
public:
Compiler(JSContext* cx, ICStub* firstMonitorStub,
HandleFunction callee, HandleObject templateObject,
- bool isConstructing, bool isSpread, uint32_t pcOffset)
+ bool isConstructing, bool ignoresReturnValue, bool isSpread, uint32_t pcOffset)
: ICCallStubCompiler(cx, ICStub::Call_Native),
firstMonitorStub_(firstMonitorStub),
isConstructing_(isConstructing),
+ ignoresReturnValue_(ignoresReturnValue),
isSpread_(isSpread),
callee_(cx, callee),
templateObject_(cx, templateObject),
@@ -2870,10 +2816,10 @@ class ICCall_StringSplit : public ICMonitoredStub
uint32_t pcOffset_;
GCPtrString expectedStr_;
GCPtrString expectedSep_;
- GCPtrObject templateObject_;
+ GCPtrArrayObject templateObject_;
ICCall_StringSplit(JitCode* stubCode, ICStub* firstMonitorStub, uint32_t pcOffset, JSString* str,
- JSString* sep, JSObject* templateObject)
+ JSString* sep, ArrayObject* templateObject)
: ICMonitoredStub(ICStub::Call_StringSplit, stubCode, firstMonitorStub),
pcOffset_(pcOffset), expectedStr_(str), expectedSep_(sep),
templateObject_(templateObject)
@@ -2900,7 +2846,7 @@ class ICCall_StringSplit : public ICMonitoredStub
return expectedSep_;
}
- GCPtrObject& templateObject() {
+ GCPtrArrayObject& templateObject() {
return templateObject_;
}
@@ -2910,7 +2856,7 @@ class ICCall_StringSplit : public ICMonitoredStub
uint32_t pcOffset_;
RootedString expectedStr_;
RootedString expectedSep_;
- RootedObject templateObject_;
+ RootedArrayObject templateObject_;
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
@@ -2921,13 +2867,13 @@ class ICCall_StringSplit : public ICMonitoredStub
public:
Compiler(JSContext* cx, ICStub* firstMonitorStub, uint32_t pcOffset, HandleString str,
- HandleString sep, HandleValue templateObject)
+ HandleString sep, HandleArrayObject templateObject)
: ICCallStubCompiler(cx, ICStub::Call_StringSplit),
firstMonitorStub_(firstMonitorStub),
pcOffset_(pcOffset),
expectedStr_(cx, str),
expectedSep_(cx, sep),
- templateObject_(cx, &templateObject.toObject())
+ templateObject_(cx, templateObject)
{ }
ICStub* getStub(ICStubSpace* space) {
diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp
index 9c7b88fb2..3b852debf 100644
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -104,19 +104,11 @@ AddReceiver(const ReceiverGuard& receiver,
static bool
GetCacheIRReceiverForNativeReadSlot(ICCacheIR_Monitored* stub, ReceiverGuard* receiver)
{
- // We match either:
+ // We match:
//
// GuardIsObject 0
// GuardShape 0
// LoadFixedSlotResult 0 or LoadDynamicSlotResult 0
- //
- // or
- //
- // GuardIsObject 0
- // GuardGroup 0
- // 1: GuardAndLoadUnboxedExpando 0
- // GuardShape 1
- // LoadFixedSlotResult 1 or LoadDynamicSlotResult 1
*receiver = ReceiverGuard();
CacheIRReader reader(stub->stubInfo());
@@ -125,14 +117,6 @@ GetCacheIRReceiverForNativeReadSlot(ICCacheIR_Monitored* stub, ReceiverGuard* re
if (!reader.matchOp(CacheOp::GuardIsObject, objId))
return false;
- if (reader.matchOp(CacheOp::GuardGroup, objId)) {
- receiver->group = stub->stubInfo()->getStubField<ObjectGroup*>(stub, reader.stubOffset());
-
- if (!reader.matchOp(CacheOp::GuardAndLoadUnboxedExpando, objId))
- return false;
- objId = reader.objOperandId();
- }
-
if (reader.matchOp(CacheOp::GuardShape, objId)) {
receiver->shape = stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
return reader.matchOpEither(CacheOp::LoadFixedSlotResult, CacheOp::LoadDynamicSlotResult);
@@ -141,29 +125,6 @@ GetCacheIRReceiverForNativeReadSlot(ICCacheIR_Monitored* stub, ReceiverGuard* re
return false;
}
-static bool
-GetCacheIRReceiverForUnboxedProperty(ICCacheIR_Monitored* stub, ReceiverGuard* receiver)
-{
- // We match:
- //
- // GuardIsObject 0
- // GuardGroup 0
- // LoadUnboxedPropertyResult 0 ..
-
- *receiver = ReceiverGuard();
- CacheIRReader reader(stub->stubInfo());
-
- ObjOperandId objId = ObjOperandId(0);
- if (!reader.matchOp(CacheOp::GuardIsObject, objId))
- return false;
-
- if (!reader.matchOp(CacheOp::GuardGroup, objId))
- return false;
- receiver->group = stub->stubInfo()->getStubField<ObjectGroup*>(stub, reader.stubOffset());
-
- return reader.matchOp(CacheOp::LoadUnboxedPropertyResult, objId);
-}
-
bool
BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers)
{
@@ -182,8 +143,7 @@ BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receiv
while (stub->next()) {
ReceiverGuard receiver;
if (stub->isCacheIR_Monitored()) {
- if (!GetCacheIRReceiverForNativeReadSlot(stub->toCacheIR_Monitored(), &receiver) &&
- !GetCacheIRReceiverForUnboxedProperty(stub->toCacheIR_Monitored(), &receiver))
+ if (!GetCacheIRReceiverForNativeReadSlot(stub->toCacheIR_Monitored(), &receiver))
{
receivers.clear();
return true;
@@ -191,8 +151,6 @@ BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receiv
} else if (stub->isSetProp_Native()) {
receiver = ReceiverGuard(stub->toSetProp_Native()->group(),
stub->toSetProp_Native()->shape());
- } else if (stub->isSetProp_Unboxed()) {
- receiver = ReceiverGuard(stub->toSetProp_Unboxed()->group(), nullptr);
} else {
receivers.clear();
return true;
@@ -580,7 +538,7 @@ BaselineInspector::getTemplateObjectForNative(jsbytecode* pc, Native native)
bool
BaselineInspector::isOptimizableCallStringSplit(jsbytecode* pc, JSString** strOut, JSString** sepOut,
- JSObject** objOut)
+ ArrayObject** objOut)
{
if (!hasBaselineScript())
return false;
diff --git a/js/src/jit/BaselineInspector.h b/js/src/jit/BaselineInspector.h
index 4a1791798..1ed4b5547 100644
--- a/js/src/jit/BaselineInspector.h
+++ b/js/src/jit/BaselineInspector.h
@@ -113,7 +113,7 @@ class BaselineInspector
bool hasSeenNonStringIterMore(jsbytecode* pc);
MOZ_MUST_USE bool isOptimizableCallStringSplit(jsbytecode* pc, JSString** strOut,
- JSString** sepOut, JSObject** objOut);
+ JSString** sepOut, ArrayObject** objOut);
JSObject* getTemplateObject(jsbytecode* pc);
JSObject* getTemplateObjectForNative(jsbytecode* pc, Native native);
JSObject* getTemplateObjectForClassHook(jsbytecode* pc, const Class* clasp);
diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp
index d0e297c2d..5c21926b5 100644
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -273,7 +273,7 @@ jit::BaselineCompile(JSContext* cx, JSScript* script, bool forceDebugInstrumenta
MOZ_ASSERT(script->canBaselineCompile());
MOZ_ASSERT(IsBaselineEnabled(cx));
- script->ensureNonLazyCanonicalFunction(cx);
+ script->ensureNonLazyCanonicalFunction();
LifoAlloc alloc(TempAllocator::PreferredLifoChunkSize);
TempAllocator* temp = alloc.new_<TempAllocator>(&alloc);
diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp
index f1061af70..d184ea40c 100644
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -10,8 +10,7 @@
#include "jit/IonCaches.h"
#include "jsobjinlines.h"
-
-#include "vm/UnboxedObject-inl.h"
+#include "vm/NativeObject-inl.h"
using namespace js;
using namespace js::jit;
@@ -60,10 +59,6 @@ GetPropIRGenerator::tryAttachStub(Maybe<CacheIRWriter>& writer)
return false;
if (!emitted_ && !tryAttachNative(*writer, obj, objId))
return false;
- if (!emitted_ && !tryAttachUnboxed(*writer, obj, objId))
- return false;
- if (!emitted_ && !tryAttachUnboxedExpando(*writer, obj, objId))
- return false;
if (!emitted_ && !tryAttachTypedObject(*writer, obj, objId))
return false;
if (!emitted_ && !tryAttachModuleNamespace(*writer, obj, objId))
@@ -163,19 +158,9 @@ GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
}
static void
-TestMatchingReceiver(CacheIRWriter& writer, JSObject* obj, Shape* shape, ObjOperandId objId,
- Maybe<ObjOperandId>* expandoId)
+TestMatchingReceiver(CacheIRWriter& writer, JSObject* obj, Shape* shape, ObjOperandId objId)
{
- if (obj->is<UnboxedPlainObject>()) {
- writer.guardGroup(objId, obj->group());
-
- if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
- expandoId->emplace(writer.guardAndLoadUnboxedExpando(objId));
- writer.guardShape(expandoId->ref(), expando->lastProperty());
- } else {
- writer.guardNoUnboxedExpando(objId);
- }
- } else if (obj->is<UnboxedArrayObject>() || obj->is<TypedObject>()) {
+ if (obj->is<TypedObject>()) {
writer.guardGroup(objId, obj->group());
} else {
Shape* shape = obj->maybeShape();
@@ -188,8 +173,7 @@ static void
EmitReadSlotResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
Shape* shape, ObjOperandId objId)
{
- Maybe<ObjOperandId> expandoId;
- TestMatchingReceiver(writer, obj, shape, objId, &expandoId);
+ TestMatchingReceiver(writer, obj, shape, objId);
ObjOperandId holderId;
if (obj != holder) {
@@ -212,9 +196,6 @@ EmitReadSlotResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
lastObjId = protoId;
}
}
- } else if (obj->is<UnboxedPlainObject>()) {
- holder = obj->as<UnboxedPlainObject>().maybeExpando();
- holderId = *expandoId;
} else {
holderId = objId;
}
@@ -266,51 +247,6 @@ GetPropIRGenerator::tryAttachNative(CacheIRWriter& writer, HandleObject obj, Obj
}
bool
-GetPropIRGenerator::tryAttachUnboxed(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId)
-{
- MOZ_ASSERT(!emitted_);
-
- if (!obj->is<UnboxedPlainObject>())
- return true;
-
- const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(name_);
- if (!property)
- return true;
-
- if (!cx_->runtime()->jitSupportsFloatingPoint)
- return true;
-
- writer.guardGroup(objId, obj->group());
- writer.loadUnboxedPropertyResult(objId, property->type,
- UnboxedPlainObject::offsetOfData() + property->offset);
- emitted_ = true;
- preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
- return true;
-}
-
-bool
-GetPropIRGenerator::tryAttachUnboxedExpando(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId)
-{
- MOZ_ASSERT(!emitted_);
-
- if (!obj->is<UnboxedPlainObject>())
- return true;
-
- UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
- if (!expando)
- return true;
-
- Shape* shape = expando->lookup(cx_, NameToId(name_));
- if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
- return true;
-
- emitted_ = true;
-
- EmitReadSlotResult(writer, obj, obj, shape, objId);
- return true;
-}
-
-bool
GetPropIRGenerator::tryAttachTypedObject(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId)
{
MOZ_ASSERT(!emitted_);
@@ -368,13 +304,6 @@ GetPropIRGenerator::tryAttachObjectLength(CacheIRWriter& writer, HandleObject ob
return true;
}
- if (obj->is<UnboxedArrayObject>()) {
- writer.guardClass(objId, GuardClassKind::UnboxedArray);
- writer.loadUnboxedArrayLengthResult(objId);
- emitted_ = true;
- return true;
- }
-
if (obj->is<ArgumentsObject>() && !obj->as<ArgumentsObject>().hasOverriddenLength()) {
if (obj->is<MappedArgumentsObject>()) {
writer.guardClass(objId, GuardClassKind::MappedArguments);
diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h
index 51e55f48b..ae55cfebb 100644
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -87,16 +87,12 @@ class ObjOperandId : public OperandId
_(GuardClass) \
_(GuardSpecificObject) \
_(GuardNoDetachedTypedObjects) \
- _(GuardNoUnboxedExpando) \
- _(GuardAndLoadUnboxedExpando) \
_(LoadObject) \
_(LoadProto) \
_(LoadFixedSlotResult) \
_(LoadDynamicSlotResult) \
- _(LoadUnboxedPropertyResult) \
_(LoadTypedObjectResult) \
_(LoadInt32ArrayLengthResult) \
- _(LoadUnboxedArrayLengthResult) \
_(LoadArgumentsObjectLengthResult) \
_(LoadUndefinedResult)
@@ -128,7 +124,6 @@ struct StubField {
enum class GuardClassKind
{
Array,
- UnboxedArray,
MappedArguments,
UnmappedArguments,
};
@@ -276,15 +271,6 @@ class MOZ_RAII CacheIRWriter
void guardNoDetachedTypedObjects() {
writeOp(CacheOp::GuardNoDetachedTypedObjects);
}
- void guardNoUnboxedExpando(ObjOperandId obj) {
- writeOpWithOperandId(CacheOp::GuardNoUnboxedExpando, obj);
- }
- ObjOperandId guardAndLoadUnboxedExpando(ObjOperandId obj) {
- ObjOperandId res(nextOperandId_++);
- writeOpWithOperandId(CacheOp::GuardAndLoadUnboxedExpando, obj);
- writeOperandId(res);
- return res;
- }
ObjOperandId loadObject(JSObject* obj) {
ObjOperandId res(nextOperandId_++);
@@ -310,11 +296,6 @@ class MOZ_RAII CacheIRWriter
writeOpWithOperandId(CacheOp::LoadDynamicSlotResult, obj);
addStubWord(offset, StubField::GCType::NoGCThing);
}
- void loadUnboxedPropertyResult(ObjOperandId obj, JSValueType type, size_t offset) {
- writeOpWithOperandId(CacheOp::LoadUnboxedPropertyResult, obj);
- buffer_.writeByte(uint32_t(type));
- addStubWord(offset, StubField::GCType::NoGCThing);
- }
void loadTypedObjectResult(ObjOperandId obj, uint32_t offset, TypedThingLayout layout,
uint32_t typeDescr) {
MOZ_ASSERT(uint32_t(layout) <= UINT8_MAX);
@@ -327,9 +308,6 @@ class MOZ_RAII CacheIRWriter
void loadInt32ArrayLengthResult(ObjOperandId obj) {
writeOpWithOperandId(CacheOp::LoadInt32ArrayLengthResult, obj);
}
- void loadUnboxedArrayLengthResult(ObjOperandId obj) {
- writeOpWithOperandId(CacheOp::LoadUnboxedArrayLengthResult, obj);
- }
void loadArgumentsObjectLengthResult(ObjOperandId obj) {
writeOpWithOperandId(CacheOp::LoadArgumentsObjectLengthResult, obj);
}
@@ -411,9 +389,6 @@ class MOZ_RAII GetPropIRGenerator
PreliminaryObjectAction preliminaryObjectAction_;
MOZ_MUST_USE bool tryAttachNative(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId);
- MOZ_MUST_USE bool tryAttachUnboxed(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId);
- MOZ_MUST_USE bool tryAttachUnboxedExpando(CacheIRWriter& writer, HandleObject obj,
- ObjOperandId objId);
MOZ_MUST_USE bool tryAttachTypedObject(CacheIRWriter& writer, HandleObject obj,
ObjOperandId objId);
MOZ_MUST_USE bool tryAttachObjectLength(CacheIRWriter& writer, HandleObject obj,
diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
index 6d1fb6b9b..205812942 100644
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -25,6 +25,7 @@
#include "builtin/Eval.h"
#include "builtin/TypedObject.h"
#include "gc/Nursery.h"
+#include "gc/StoreBuffer-inl.h"
#include "irregexp/NativeRegExpMacroAssembler.h"
#include "jit/AtomicOperations.h"
#include "jit/BaselineCompiler.h"
@@ -1052,7 +1053,7 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re
Address pairsVectorAddress(masm.getStackPointer(), pairsVectorStartOffset);
- RegExpStatics* res = cx->global()->getRegExpStatics(cx);
+ RegExpStatics* res = GlobalObject::getRegExpStatics(cx, cx->global());
if (!res)
return false;
#ifdef JS_USE_LINK_REGISTER
@@ -3027,7 +3028,7 @@ CodeGenerator::visitStoreSlotV(LStoreSlotV* lir)
static void
GuardReceiver(MacroAssembler& masm, const ReceiverGuard& guard,
- Register obj, Register scratch, Label* miss, bool checkNullExpando)
+ Register obj, Register scratch, Label* miss)
{
if (guard.group) {
masm.branchTestObjGroup(Assembler::NotEqual, obj, guard.group, miss);
@@ -3049,13 +3050,11 @@ CodeGenerator::emitGetPropertyPolymorphic(LInstruction* ins, Register obj, Regis
Label next;
masm.comment("GuardReceiver");
- GuardReceiver(masm, receiver, obj, scratch, &next, /* checkNullExpando = */ false);
+ GuardReceiver(masm, receiver, obj, scratch, &next);
if (receiver.shape) {
masm.comment("loadTypedOrValue");
- // If this is an unboxed expando access, GuardReceiver loaded the
- // expando object into scratch.
- Register target = receiver.group ? scratch : obj;
+ Register target = obj;
Shape* shape = mir->shape(i);
if (shape->slot() < shape->numFixedSlots()) {
@@ -3121,12 +3120,10 @@ CodeGenerator::emitSetPropertyPolymorphic(LInstruction* ins, Register obj, Regis
ReceiverGuard receiver = mir->receiver(i);
Label next;
- GuardReceiver(masm, receiver, obj, scratch, &next, /* checkNullExpando = */ false);
+ GuardReceiver(masm, receiver, obj, scratch, &next);
if (receiver.shape) {
- // If this is an unboxed expando access, GuardReceiver loaded the
- // expando object into scratch.
- Register target = receiver.group ? scratch : obj;
+ Register target = obj;
Shape* shape = mir->shape(i);
if (shape->slot() < shape->numFixedSlots()) {
@@ -3183,9 +3180,7 @@ CodeGenerator::visitSetPropertyPolymorphicT(LSetPropertyPolymorphicT* ins)
void
CodeGenerator::visitElements(LElements* lir)
{
- Address elements(ToRegister(lir->object()),
- lir->mir()->unboxed() ? UnboxedArrayObject::offsetOfElements()
- : NativeObject::offsetOfElements());
+ Address elements(ToRegister(lir->object()), NativeObject::offsetOfElements());
masm.loadPtr(elements, ToRegister(lir->output()));
}
@@ -3294,7 +3289,7 @@ CodeGenerator::visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir)
const ReceiverGuard& receiver = mir->receiver(i);
Label next;
- GuardReceiver(masm, receiver, obj, temp, &next, /* checkNullExpando = */ true);
+ GuardReceiver(masm, receiver, obj, temp, &next);
if (i == mir->numReceivers() - 1) {
bailoutFrom(&next, lir->snapshot());
@@ -3693,7 +3688,13 @@ CodeGenerator::visitCallNative(LCallNative* call)
masm.passABIArg(argContextReg);
masm.passABIArg(argUintNReg);
masm.passABIArg(argVpReg);
- masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()));
+ JSNative native = target->native();
+ if (call->ignoresReturnValue()) {
+ const JSJitInfo* jitInfo = target->jitInfo();
+ if (jitInfo && jitInfo->type() == JSJitInfo::IgnoresReturnValueNative)
+ native = jitInfo->ignoresReturnValueMethod;
+ }
+ masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, native));
emitTracelogStopEvent(TraceLogger_Call);
@@ -3850,13 +3851,15 @@ CodeGenerator::visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir)
callVM(GetIntrinsicValueInfo, lir);
}
-typedef bool (*InvokeFunctionFn)(JSContext*, HandleObject, bool, uint32_t, Value*, MutableHandleValue);
+typedef bool (*InvokeFunctionFn)(JSContext*, HandleObject, bool, bool, uint32_t, Value*,
+ MutableHandleValue);
static const VMFunction InvokeFunctionInfo =
FunctionInfo<InvokeFunctionFn>(InvokeFunction, "InvokeFunction");
void
CodeGenerator::emitCallInvokeFunction(LInstruction* call, Register calleereg,
- bool constructing, uint32_t argc, uint32_t unusedStack)
+ bool constructing, bool ignoresReturnValue,
+ uint32_t argc, uint32_t unusedStack)
{
// Nestle %esp up to the argument vector.
// Each path must account for framePushed_ separately, for callVM to be valid.
@@ -3864,6 +3867,7 @@ CodeGenerator::emitCallInvokeFunction(LInstruction* call, Register calleereg,
pushArg(masm.getStackPointer()); // argv.
pushArg(Imm32(argc)); // argc.
+ pushArg(Imm32(ignoresReturnValue));
pushArg(Imm32(constructing)); // constructing.
pushArg(calleereg); // JSFunction*.
@@ -3950,8 +3954,8 @@ CodeGenerator::visitCallGeneric(LCallGeneric* call)
// Handle uncompiled or native functions.
masm.bind(&invoke);
- emitCallInvokeFunction(call, calleereg, call->isConstructing(), call->numActualArgs(),
- unusedStack);
+ emitCallInvokeFunction(call, calleereg, call->isConstructing(), call->ignoresReturnValue(),
+ call->numActualArgs(), unusedStack);
masm.bind(&end);
@@ -4006,7 +4010,8 @@ CodeGenerator::visitCallKnown(LCallKnown* call)
masm.checkStackAlignment();
if (target->isClassConstructor() && !call->isConstructing()) {
- emitCallInvokeFunction(call, calleereg, call->isConstructing(), call->numActualArgs(), unusedStack);
+ emitCallInvokeFunction(call, calleereg, call->isConstructing(), call->ignoresReturnValue(),
+ call->numActualArgs(), unusedStack);
return;
}
@@ -4050,7 +4055,8 @@ CodeGenerator::visitCallKnown(LCallKnown* call)
if (call->isConstructing() && target->nargs() > call->numActualArgs())
emitCallInvokeFunctionShuffleNewTarget(call, calleereg, target->nargs(), unusedStack);
else
- emitCallInvokeFunction(call, calleereg, call->isConstructing(), call->numActualArgs(), unusedStack);
+ emitCallInvokeFunction(call, calleereg, call->isConstructing(), call->ignoresReturnValue(),
+ call->numActualArgs(), unusedStack);
masm.bind(&end);
@@ -4077,6 +4083,7 @@ CodeGenerator::emitCallInvokeFunction(T* apply, Register extraStackSize)
pushArg(objreg); // argv.
pushArg(ToRegister(apply->getArgc())); // argc.
+ pushArg(Imm32(false)); // ignoresReturnValue.
pushArg(Imm32(false)); // isConstrucing.
pushArg(ToRegister(apply->getFunction())); // JSFunction*.
@@ -4433,19 +4440,6 @@ CodeGenerator::visitApplyArrayGeneric(LApplyArrayGeneric* apply)
emitApplyGeneric(apply);
}
-typedef bool (*ArraySpliceDenseFn)(JSContext*, HandleObject, uint32_t, uint32_t);
-static const VMFunction ArraySpliceDenseInfo =
- FunctionInfo<ArraySpliceDenseFn>(ArraySpliceDense, "ArraySpliceDense");
-
-void
-CodeGenerator::visitArraySplice(LArraySplice* lir)
-{
- pushArg(ToRegister(lir->getDeleteCount()));
- pushArg(ToRegister(lir->getStart()));
- pushArg(ToRegister(lir->getObject()));
- callVM(ArraySpliceDenseInfo, lir);
-}
-
void
CodeGenerator::visitBail(LBail* lir)
{
@@ -5171,11 +5165,11 @@ static JSObject*
NewArrayWithGroup(JSContext* cx, uint32_t length, HandleObjectGroup group,
bool convertDoubleElements)
{
- JSObject* res = NewFullyAllocatedArrayTryUseGroup(cx, group, length);
+ ArrayObject* res = NewFullyAllocatedArrayTryUseGroup(cx, group, length);
if (!res)
return nullptr;
if (convertDoubleElements)
- res->as<ArrayObject>().setShouldConvertDoubleElements();
+ res->setShouldConvertDoubleElements();
return res;
}
@@ -5321,7 +5315,7 @@ CodeGenerator::visitNewArrayCopyOnWrite(LNewArrayCopyOnWrite* lir)
masm.bind(ool->rejoin());
}
-typedef JSObject* (*ArrayConstructorOneArgFn)(JSContext*, HandleObjectGroup, int32_t length);
+typedef ArrayObject* (*ArrayConstructorOneArgFn)(JSContext*, HandleObjectGroup, int32_t length);
static const VMFunction ArrayConstructorOneArgInfo =
FunctionInfo<ArrayConstructorOneArgFn>(ArrayConstructorOneArg, "ArrayConstructorOneArg");
@@ -5341,21 +5335,11 @@ CodeGenerator::visitNewArrayDynamicLength(LNewArrayDynamicLength* lir)
bool canInline = true;
size_t inlineLength = 0;
- if (templateObject->is<ArrayObject>()) {
- if (templateObject->as<ArrayObject>().hasFixedElements()) {
- size_t numSlots = gc::GetGCKindSlots(templateObject->asTenured().getAllocKind());
- inlineLength = numSlots - ObjectElements::VALUES_PER_HEADER;
- } else {
- canInline = false;
- }
+ if (templateObject->as<ArrayObject>().hasFixedElements()) {
+ size_t numSlots = gc::GetGCKindSlots(templateObject->asTenured().getAllocKind());
+ inlineLength = numSlots - ObjectElements::VALUES_PER_HEADER;
} else {
- if (templateObject->as<UnboxedArrayObject>().hasInlineElements()) {
- size_t nbytes =
- templateObject->tenuredSizeOfThis() - UnboxedArrayObject::offsetOfInlineElements();
- inlineLength = nbytes / templateObject->as<UnboxedArrayObject>().elementSize();
- } else {
- canInline = false;
- }
+ canInline = false;
}
if (canInline) {
@@ -7777,7 +7761,7 @@ CodeGenerator::visitSinCos(LSinCos *lir)
masm.freeStack(sizeof(double) * 2);
}
-typedef JSObject* (*StringSplitFn)(JSContext*, HandleObjectGroup, HandleString, HandleString, uint32_t);
+typedef ArrayObject* (*StringSplitFn)(JSContext*, HandleObjectGroup, HandleString, HandleString, uint32_t);
static const VMFunction StringSplitInfo =
FunctionInfo<StringSplitFn>(js::str_split_string, "str_split_string");
@@ -7812,49 +7796,6 @@ CodeGenerator::visitSetInitializedLength(LSetInitializedLength* lir)
}
void
-CodeGenerator::visitUnboxedArrayLength(LUnboxedArrayLength* lir)
-{
- Register obj = ToRegister(lir->object());
- Register result = ToRegister(lir->output());
- masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), result);
-}
-
-void
-CodeGenerator::visitUnboxedArrayInitializedLength(LUnboxedArrayInitializedLength* lir)
-{
- Register obj = ToRegister(lir->object());
- Register result = ToRegister(lir->output());
- masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), result);
- masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), result);
-}
-
-void
-CodeGenerator::visitIncrementUnboxedArrayInitializedLength(LIncrementUnboxedArrayInitializedLength* lir)
-{
- Register obj = ToRegister(lir->object());
- masm.add32(Imm32(1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
-}
-
-void
-CodeGenerator::visitSetUnboxedArrayInitializedLength(LSetUnboxedArrayInitializedLength* lir)
-{
- Register obj = ToRegister(lir->object());
- RegisterOrInt32Constant key = ToRegisterOrInt32Constant(lir->length());
- Register temp = ToRegister(lir->temp());
-
- Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
- masm.load32(initLengthAddr, temp);
- masm.and32(Imm32(UnboxedArrayObject::CapacityMask), temp);
-
- if (key.isRegister())
- masm.or32(key.reg(), temp);
- else
- masm.or32(Imm32(key.constant()), temp);
-
- masm.store32(temp, initLengthAddr);
-}
-
-void
CodeGenerator::visitNotO(LNotO* lir)
{
MOZ_ASSERT(lir->mir()->operandMightEmulateUndefined(),
@@ -8150,46 +8091,19 @@ CodeGenerator::emitStoreElementHoleT(T* lir)
OutOfLineStoreElementHole* ool = new(alloc()) OutOfLineStoreElementHole(lir);
addOutOfLineCode(ool, lir->mir());
- Register obj = ToRegister(lir->object());
Register elements = ToRegister(lir->elements());
const LAllocation* index = lir->index();
RegisterOrInt32Constant key = ToRegisterOrInt32Constant(index);
- JSValueType unboxedType = lir->mir()->unboxedType();
- if (unboxedType == JSVAL_TYPE_MAGIC) {
- Address initLength(elements, ObjectElements::offsetOfInitializedLength());
- masm.branch32(Assembler::BelowOrEqual, initLength, key, ool->entry());
+ Address initLength(elements, ObjectElements::offsetOfInitializedLength());
+ masm.branch32(Assembler::BelowOrEqual, initLength, key, ool->entry());
- if (lir->mir()->needsBarrier())
- emitPreBarrier(elements, index, 0);
-
- masm.bind(ool->rejoinStore());
- emitStoreElementTyped(lir->value(), lir->mir()->value()->type(), lir->mir()->elementType(),
- elements, index, 0);
- } else {
- Register temp = ToRegister(lir->getTemp(0));
- Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
- masm.load32(initLength, temp);
- masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp);
- masm.branch32(Assembler::BelowOrEqual, temp, key, ool->entry());
-
- ConstantOrRegister v = ToConstantOrRegister(lir->value(), lir->mir()->value()->type());
-
- if (index->isConstant()) {
- Address address(elements, ToInt32(index) * UnboxedTypeSize(unboxedType));
- EmitUnboxedPreBarrier(masm, address, unboxedType);
-
- masm.bind(ool->rejoinStore());
- masm.storeUnboxedProperty(address, unboxedType, v, nullptr);
- } else {
- BaseIndex address(elements, ToRegister(index),
- ScaleFromElemWidth(UnboxedTypeSize(unboxedType)));
- EmitUnboxedPreBarrier(masm, address, unboxedType);
+ if (lir->mir()->needsBarrier())
+ emitPreBarrier(elements, index, 0);
- masm.bind(ool->rejoinStore());
- masm.storeUnboxedProperty(address, unboxedType, v, nullptr);
- }
- }
+ masm.bind(ool->rejoinStore());
+ emitStoreElementTyped(lir->value(), lir->mir()->value()->type(), lir->mir()->elementType(),
+ elements, index, 0);
masm.bind(ool->rejoin());
}
@@ -8209,47 +8123,22 @@ CodeGenerator::emitStoreElementHoleV(T* lir)
OutOfLineStoreElementHole* ool = new(alloc()) OutOfLineStoreElementHole(lir);
addOutOfLineCode(ool, lir->mir());
- Register obj = ToRegister(lir->object());
Register elements = ToRegister(lir->elements());
const LAllocation* index = lir->index();
const ValueOperand value = ToValue(lir, T::Value);
RegisterOrInt32Constant key = ToRegisterOrInt32Constant(index);
- JSValueType unboxedType = lir->mir()->unboxedType();
- if (unboxedType == JSVAL_TYPE_MAGIC) {
- Address initLength(elements, ObjectElements::offsetOfInitializedLength());
- masm.branch32(Assembler::BelowOrEqual, initLength, key, ool->entry());
-
- if (lir->mir()->needsBarrier())
- emitPreBarrier(elements, index, 0);
+ Address initLength(elements, ObjectElements::offsetOfInitializedLength());
+ masm.branch32(Assembler::BelowOrEqual, initLength, key, ool->entry());
- masm.bind(ool->rejoinStore());
- if (index->isConstant())
- masm.storeValue(value, Address(elements, ToInt32(index) * sizeof(js::Value)));
- else
- masm.storeValue(value, BaseIndex(elements, ToRegister(index), TimesEight));
- } else {
- Register temp = ToRegister(lir->getTemp(0));
- Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
- masm.load32(initLength, temp);
- masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp);
- masm.branch32(Assembler::BelowOrEqual, temp, key, ool->entry());
-
- if (index->isConstant()) {
- Address address(elements, ToInt32(index) * UnboxedTypeSize(unboxedType));
- EmitUnboxedPreBarrier(masm, address, unboxedType);
-
- masm.bind(ool->rejoinStore());
- masm.storeUnboxedProperty(address, unboxedType, ConstantOrRegister(value), nullptr);
- } else {
- BaseIndex address(elements, ToRegister(index),
- ScaleFromElemWidth(UnboxedTypeSize(unboxedType)));
- EmitUnboxedPreBarrier(masm, address, unboxedType);
+ if (lir->mir()->needsBarrier())
+ emitPreBarrier(elements, index, 0);
- masm.bind(ool->rejoinStore());
- masm.storeUnboxedProperty(address, unboxedType, ConstantOrRegister(value), nullptr);
- }
- }
+ masm.bind(ool->rejoinStore());
+ if (index->isConstant())
+ masm.storeValue(value, Address(elements, ToInt32(index) * sizeof(js::Value)));
+ else
+ masm.storeValue(value, BaseIndex(elements, ToRegister(index), TimesEight));
masm.bind(ool->rejoin());
}
@@ -8320,11 +8209,10 @@ CodeGenerator::visitFallibleStoreElementV(LFallibleStoreElementV* lir)
masm.bind(&isFrozen);
}
-typedef bool (*SetDenseOrUnboxedArrayElementFn)(JSContext*, HandleObject, int32_t,
- HandleValue, bool strict);
-static const VMFunction SetDenseOrUnboxedArrayElementInfo =
- FunctionInfo<SetDenseOrUnboxedArrayElementFn>(SetDenseOrUnboxedArrayElement,
- "SetDenseOrUnboxedArrayElement");
+typedef bool (*SetDenseElementFn)(JSContext*, HandleNativeObject, int32_t, HandleValue,
+ bool strict);
+static const VMFunction SetDenseElementInfo =
+ FunctionInfo<SetDenseElementFn>(jit::SetDenseElement, "SetDenseElement");
void
CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
@@ -8334,8 +8222,6 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
const LAllocation* index;
MIRType valueType;
ConstantOrRegister value;
- JSValueType unboxedType;
- LDefinition *temp = nullptr;
if (ins->isStoreElementHoleV()) {
LStoreElementHoleV* store = ins->toStoreElementHoleV();
@@ -8344,8 +8230,6 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
index = store->index();
valueType = store->mir()->value()->type();
value = TypedOrValueRegister(ToValue(store, LStoreElementHoleV::Value));
- unboxedType = store->mir()->unboxedType();
- temp = store->getTemp(0);
} else if (ins->isFallibleStoreElementV()) {
LFallibleStoreElementV* store = ins->toFallibleStoreElementV();
object = ToRegister(store->object());
@@ -8353,8 +8237,6 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
index = store->index();
valueType = store->mir()->value()->type();
value = TypedOrValueRegister(ToValue(store, LFallibleStoreElementV::Value));
- unboxedType = store->mir()->unboxedType();
- temp = store->getTemp(0);
} else if (ins->isStoreElementHoleT()) {
LStoreElementHoleT* store = ins->toStoreElementHoleT();
object = ToRegister(store->object());
@@ -8365,8 +8247,6 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
value = ConstantOrRegister(store->value()->toConstant()->toJSValue());
else
value = TypedOrValueRegister(valueType, ToAnyRegister(store->value()));
- unboxedType = store->mir()->unboxedType();
- temp = store->getTemp(0);
} else { // ins->isFallibleStoreElementT()
LFallibleStoreElementT* store = ins->toFallibleStoreElementT();
object = ToRegister(store->object());
@@ -8377,8 +8257,6 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
value = ConstantOrRegister(store->value()->toConstant()->toJSValue());
else
value = TypedOrValueRegister(valueType, ToAnyRegister(store->value()));
- unboxedType = store->mir()->unboxedType();
- temp = store->getTemp(0);
}
RegisterOrInt32Constant key = ToRegisterOrInt32Constant(index);
@@ -8389,54 +8267,32 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
Label callStub;
#if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
// Had to reimplement for MIPS because there are no flags.
- if (unboxedType == JSVAL_TYPE_MAGIC) {
- Address initLength(elements, ObjectElements::offsetOfInitializedLength());
- masm.branch32(Assembler::NotEqual, initLength, key, &callStub);
- } else {
- Address initLength(object, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
- masm.load32(initLength, ToRegister(temp));
- masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), ToRegister(temp));
- masm.branch32(Assembler::NotEqual, ToRegister(temp), key, &callStub);
- }
+ Address initLength(elements, ObjectElements::offsetOfInitializedLength());
+ masm.branch32(Assembler::NotEqual, initLength, key, &callStub);
#else
masm.j(Assembler::NotEqual, &callStub);
#endif
- if (unboxedType == JSVAL_TYPE_MAGIC) {
- // Check array capacity.
- masm.branch32(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()),
- key, &callStub);
-
- // Update initialized length. The capacity guard above ensures this won't overflow,
- // due to MAX_DENSE_ELEMENTS_COUNT.
- masm.inc32(&key);
- masm.store32(key, Address(elements, ObjectElements::offsetOfInitializedLength()));
-
- // Update length if length < initializedLength.
- Label dontUpdate;
- masm.branch32(Assembler::AboveOrEqual, Address(elements, ObjectElements::offsetOfLength()),
- key, &dontUpdate);
- masm.store32(key, Address(elements, ObjectElements::offsetOfLength()));
- masm.bind(&dontUpdate);
+ // Check array capacity.
+ masm.branch32(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()),
+ key, &callStub);
- masm.dec32(&key);
- } else {
- // Check array capacity.
- masm.checkUnboxedArrayCapacity(object, key, ToRegister(temp), &callStub);
+ // Update initialized length. The capacity guard above ensures this won't overflow,
+ // due to MAX_DENSE_ELEMENTS_COUNT.
+ masm.inc32(&key);
+ masm.store32(key, Address(elements, ObjectElements::offsetOfInitializedLength()));
- // Update initialized length.
- masm.add32(Imm32(1), Address(object, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
+ // Update length if length < initializedLength.
+ Label dontUpdate;
+ masm.branch32(Assembler::AboveOrEqual, Address(elements, ObjectElements::offsetOfLength()),
+ key, &dontUpdate);
+ masm.store32(key, Address(elements, ObjectElements::offsetOfLength()));
+ masm.bind(&dontUpdate);
- // Update length if length < initializedLength.
- Address lengthAddr(object, UnboxedArrayObject::offsetOfLength());
- Label dontUpdate;
- masm.branch32(Assembler::Above, lengthAddr, key, &dontUpdate);
- masm.add32(Imm32(1), lengthAddr);
- masm.bind(&dontUpdate);
- }
+ masm.dec32(&key);
if ((ins->isStoreElementHoleT() || ins->isFallibleStoreElementT()) &&
- unboxedType == JSVAL_TYPE_MAGIC && valueType != MIRType::Double)
+ valueType != MIRType::Double)
{
// The inline path for StoreElementHoleT and FallibleStoreElementT does not always store
// the type tag, so we do the store on the OOL path. We use MIRType::None for the element
@@ -8465,7 +8321,7 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
else
pushArg(ToRegister(index));
pushArg(object);
- callVM(SetDenseOrUnboxedArrayElementInfo, ins);
+ callVM(SetDenseElementInfo, ins);
restoreLive(ins);
masm.jump(ool->rejoin());
@@ -8522,14 +8378,6 @@ CodeGenerator::visitStoreUnboxedPointer(LStoreUnboxedPointer* lir)
}
}
-typedef bool (*ConvertUnboxedObjectToNativeFn)(JSContext*, JSObject*);
-static const VMFunction ConvertUnboxedPlainObjectToNativeInfo =
- FunctionInfo<ConvertUnboxedObjectToNativeFn>(UnboxedPlainObject::convertToNative,
- "UnboxedPlainObject::convertToNative");
-static const VMFunction ConvertUnboxedArrayObjectToNativeInfo =
- FunctionInfo<ConvertUnboxedObjectToNativeFn>(UnboxedArrayObject::convertToNative,
- "UnboxedArrayObject::convertToNative");
-
typedef bool (*ArrayPopShiftFn)(JSContext*, HandleObject, MutableHandleValue);
static const VMFunction ArrayPopDenseInfo =
FunctionInfo<ArrayPopShiftFn>(jit::ArrayPopDense, "ArrayPopDense");
@@ -8554,20 +8402,11 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R
// Load elements and length, and VM call if length != initializedLength.
RegisterOrInt32Constant key = RegisterOrInt32Constant(lengthTemp);
- if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
- masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
- masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), lengthTemp);
-
- Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
- masm.branch32(Assembler::NotEqual, initLength, key, ool->entry());
- } else {
- masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), elementsTemp);
- masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), lengthTemp);
- masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), lengthTemp);
+ masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
+ masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), lengthTemp);
- Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength());
- masm.branch32(Assembler::NotEqual, lengthAddr, key, ool->entry());
- }
+ Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
+ masm.branch32(Assembler::NotEqual, initLength, key, ool->entry());
// Test for length != 0. On zero length either take a VM call or generate
// an undefined value, depending on whether the call is known to produce
@@ -8579,13 +8418,10 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R
// According to the spec we need to set the length 0 (which is already 0).
// This is observable when the array length is made non-writable.
- // Handle this case in the OOL. When freezing an unboxed array it is converted
- // to an normal array.
- if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
- Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags());
- Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH);
- masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry());
- }
+ // Handle this case in the OOL.
+ Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags());
+ Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH);
+ masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry());
masm.moveValue(UndefinedValue(), out.valueReg());
masm.jump(&done);
@@ -8597,41 +8433,25 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R
masm.dec32(&key);
if (mir->mode() == MArrayPopShift::Pop) {
- if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
- BaseIndex addr(elementsTemp, lengthTemp, TimesEight);
- masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry());
- } else {
- size_t elemSize = UnboxedTypeSize(mir->unboxedType());
- BaseIndex addr(elementsTemp, lengthTemp, ScaleFromElemWidth(elemSize));
- masm.loadUnboxedProperty(addr, mir->unboxedType(), out);
- }
+ BaseIndex addr(elementsTemp, lengthTemp, TimesEight);
+ masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry());
} else {
MOZ_ASSERT(mir->mode() == MArrayPopShift::Shift);
Address addr(elementsTemp, 0);
- if (mir->unboxedType() == JSVAL_TYPE_MAGIC)
- masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry());
- else
- masm.loadUnboxedProperty(addr, mir->unboxedType(), out);
+ masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry());
}
- if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
- // Handle the failure case when the array length is non-writable in the
- // OOL path. (Unlike in the adding-an-element cases, we can't rely on the
- // capacity <= length invariant for such arrays to avoid an explicit
- // check.)
- Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags());
- Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH);
- masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry());
+ // Handle the failure case when the array length is non-writable in the
+ // OOL path. (Unlike in the adding-an-element cases, we can't rely on the
+ // capacity <= length invariant for such arrays to avoid an explicit
+ // check.)
+ Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags());
+ Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH);
+ masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry());
- // Now adjust length and initializedLength.
- masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfLength()));
- masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
- } else {
- // Unboxed arrays always have writable lengths. Adjust length and
- // initializedLength.
- masm.store32(lengthTemp, Address(obj, UnboxedArrayObject::offsetOfLength()));
- masm.add32(Imm32(-1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
- }
+ // Now adjust length and initializedLength.
+ masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfLength()));
+ masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
if (mir->mode() == MArrayPopShift::Shift) {
// Don't save the temp registers.
@@ -8670,7 +8490,7 @@ CodeGenerator::visitArrayPopShiftT(LArrayPopShiftT* lir)
emitArrayPopShift(lir, lir->mir(), obj, elements, length, out);
}
-typedef bool (*ArrayPushDenseFn)(JSContext*, HandleObject, HandleValue, uint32_t*);
+typedef bool (*ArrayPushDenseFn)(JSContext*, HandleArrayObject, HandleValue, uint32_t*);
static const VMFunction ArrayPushDenseInfo =
FunctionInfo<ArrayPushDenseFn>(jit::ArrayPushDense, "ArrayPushDense");
@@ -8681,50 +8501,27 @@ CodeGenerator::emitArrayPush(LInstruction* lir, const MArrayPush* mir, Register
OutOfLineCode* ool = oolCallVM(ArrayPushDenseInfo, lir, ArgList(obj, value), StoreRegisterTo(length));
RegisterOrInt32Constant key = RegisterOrInt32Constant(length);
- if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
- // Load elements and length.
- masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
- masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length);
-
- // Guard length == initializedLength.
- Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
- masm.branch32(Assembler::NotEqual, initLength, key, ool->entry());
- // Guard length < capacity.
- Address capacity(elementsTemp, ObjectElements::offsetOfCapacity());
- masm.branch32(Assembler::BelowOrEqual, capacity, key, ool->entry());
+ // Load elements and length.
+ masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
+ masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length);
- // Do the store.
- masm.storeConstantOrRegister(value, BaseIndex(elementsTemp, length, TimesEight));
- } else {
- // Load initialized length.
- masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), length);
- masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), length);
-
- // Guard length == initializedLength.
- Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength());
- masm.branch32(Assembler::NotEqual, lengthAddr, key, ool->entry());
+ // Guard length == initializedLength.
+ Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
+ masm.branch32(Assembler::NotEqual, initLength, key, ool->entry());
- // Guard length < capacity.
- masm.checkUnboxedArrayCapacity(obj, key, elementsTemp, ool->entry());
+ // Guard length < capacity.
+ Address capacity(elementsTemp, ObjectElements::offsetOfCapacity());
+ masm.branch32(Assembler::BelowOrEqual, capacity, key, ool->entry());
- // Load elements and do the store.
- masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), elementsTemp);
- size_t elemSize = UnboxedTypeSize(mir->unboxedType());
- BaseIndex addr(elementsTemp, length, ScaleFromElemWidth(elemSize));
- masm.storeUnboxedProperty(addr, mir->unboxedType(), value, nullptr);
- }
+ // Do the store.
+ masm.storeConstantOrRegister(value, BaseIndex(elementsTemp, length, TimesEight));
masm.inc32(&key);
// Update length and initialized length.
- if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
- masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength()));
- masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
- } else {
- masm.store32(length, Address(obj, UnboxedArrayObject::offsetOfLength()));
- masm.add32(Imm32(1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
- }
+ masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength()));
+ masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
masm.bind(ool->rejoin());
}
@@ -8873,11 +8670,11 @@ CodeGenerator::visitIteratorStartO(LIteratorStartO* lir)
masm.loadPtr(Address(niTemp, offsetof(NativeIterator, guard_array)), temp2);
// Compare object with the first receiver guard. The last iterator can only
- // match for native objects and unboxed objects.
+ // match for native objects.
{
Address groupAddr(temp2, offsetof(ReceiverGuard, group));
Address shapeAddr(temp2, offsetof(ReceiverGuard, shape));
- Label guardDone, shapeMismatch, noExpando;
+ Label guardDone, shapeMismatch;
masm.loadObjShape(obj, temp1);
masm.branchPtr(Assembler::NotEqual, shapeAddr, temp1, &shapeMismatch);
@@ -8889,12 +8686,6 @@ CodeGenerator::visitIteratorStartO(LIteratorStartO* lir)
masm.bind(&shapeMismatch);
masm.loadObjGroup(obj, temp1);
masm.branchPtr(Assembler::NotEqual, groupAddr, temp1, ool->entry());
- masm.loadPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), temp1);
- masm.branchTestPtr(Assembler::Zero, temp1, temp1, &noExpando);
- branchIfNotEmptyObjectElements(temp1, ool->entry());
- masm.loadObjShape(temp1, temp1);
- masm.bind(&noExpando);
- masm.branchPtr(Assembler::NotEqual, shapeAddr, temp1, ool->entry());
masm.bind(&guardDone);
}
@@ -10529,22 +10320,11 @@ CodeGenerator::visitLoadElementHole(LLoadElementHole* lir)
else
masm.branch32(Assembler::BelowOrEqual, initLength, ToRegister(lir->index()), &undefined);
- if (mir->unboxedType() != JSVAL_TYPE_MAGIC) {
- size_t width = UnboxedTypeSize(mir->unboxedType());
- if (lir->index()->isConstant()) {
- Address addr(elements, ToInt32(lir->index()) * width);
- masm.loadUnboxedProperty(addr, mir->unboxedType(), out);
- } else {
- BaseIndex addr(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
- masm.loadUnboxedProperty(addr, mir->unboxedType(), out);
- }
+ if (lir->index()->isConstant()) {
+ NativeObject::elementsSizeMustNotOverflow();
+ masm.loadValue(Address(elements, ToInt32(lir->index()) * sizeof(Value)), out);
} else {
- if (lir->index()->isConstant()) {
- NativeObject::elementsSizeMustNotOverflow();
- masm.loadValue(Address(elements, ToInt32(lir->index()) * sizeof(Value)), out);
- } else {
- masm.loadValue(BaseObjectElementIndex(elements, ToRegister(lir->index())), out);
- }
+ masm.loadValue(BaseObjectElementIndex(elements, ToRegister(lir->index())), out);
}
// If a hole check is needed, and the value wasn't a hole, we're done.
@@ -10922,7 +10702,7 @@ CodeGenerator::visitInArray(LInArray* lir)
}
masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(index), failedInitLength);
- if (mir->needsHoleCheck() && mir->unboxedType() == JSVAL_TYPE_MAGIC) {
+ if (mir->needsHoleCheck()) {
NativeObject::elementsSizeMustNotOverflow();
Address address = Address(elements, index * sizeof(Value));
masm.branchTestMagic(Assembler::Equal, address, &falseBranch);
@@ -10935,7 +10715,7 @@ CodeGenerator::visitInArray(LInArray* lir)
failedInitLength = &negativeIntCheck;
masm.branch32(Assembler::BelowOrEqual, initLength, index, failedInitLength);
- if (mir->needsHoleCheck() && mir->unboxedType() == JSVAL_TYPE_MAGIC) {
+ if (mir->needsHoleCheck()) {
BaseIndex address = BaseIndex(elements, ToRegister(lir->index()), TimesEight);
masm.branchTestMagic(Assembler::Equal, address, &falseBranch);
}
diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h
index 292f163b5..12f1238ef 100644
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -165,8 +165,8 @@ class CodeGenerator final : public CodeGeneratorSpecific
void visitOutOfLineCallPostWriteElementBarrier(OutOfLineCallPostWriteElementBarrier* ool);
void visitCallNative(LCallNative* call);
void emitCallInvokeFunction(LInstruction* call, Register callereg,
- bool isConstructing, uint32_t argc,
- uint32_t unusedStack);
+ bool isConstructing, bool ignoresReturnValue,
+ uint32_t argc, uint32_t unusedStack);
void visitCallGeneric(LCallGeneric* call);
void emitCallInvokeFunctionShuffleNewTarget(LCallKnown *call,
Register calleeReg,
@@ -234,10 +234,6 @@ class CodeGenerator final : public CodeGeneratorSpecific
void visitSubstr(LSubstr* lir);
void visitInitializedLength(LInitializedLength* lir);
void visitSetInitializedLength(LSetInitializedLength* lir);
- void visitUnboxedArrayLength(LUnboxedArrayLength* lir);
- void visitUnboxedArrayInitializedLength(LUnboxedArrayInitializedLength* lir);
- void visitIncrementUnboxedArrayInitializedLength(LIncrementUnboxedArrayInitializedLength* lir);
- void visitSetUnboxedArrayInitializedLength(LSetUnboxedArrayInitializedLength* lir);
void visitNotO(LNotO* ins);
void visitNotV(LNotV* ins);
void visitBoundsCheck(LBoundsCheck* lir);
@@ -255,7 +251,6 @@ class CodeGenerator final : public CodeGeneratorSpecific
void emitSetPropertyPolymorphic(LInstruction* lir, Register obj,
Register scratch, const ConstantOrRegister& value);
void visitSetPropertyPolymorphicV(LSetPropertyPolymorphicV* ins);
- void visitArraySplice(LArraySplice* splice);
void visitSetPropertyPolymorphicT(LSetPropertyPolymorphicT* ins);
void visitAbsI(LAbsI* lir);
void visitAtan2D(LAtan2D* lir);
diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h
index 18535389a..1d0506f74 100644
--- a/js/src/jit/InlinableNatives.h
+++ b/js/src/jit/InlinableNatives.h
@@ -15,7 +15,6 @@
_(ArrayShift) \
_(ArrayPush) \
_(ArraySlice) \
- _(ArraySplice) \
\
_(AtomicsCompareExchange) \
_(AtomicsExchange) \
diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp
index c61b414e0..b8a2d2fba 100644
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2153,7 +2153,7 @@ IonCompile(JSContext* cx, JSScript* script,
// Make sure the script's canonical function isn't lazy. We can't de-lazify
// it in a helper thread.
- script->ensureNonLazyCanonicalFunction(cx);
+ script->ensureNonLazyCanonicalFunction();
TrackPropertiesForSingletonScopes(cx, script, baselineFrame);
diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp
index 41c71c9c3..5fc624fb1 100644
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -4055,7 +4055,7 @@ AnalyzePoppedThis(JSContext* cx, ObjectGroup* group,
// Add the property to the object, being careful not to update type information.
DebugOnly<unsigned> slotSpan = baseobj->slotSpan();
MOZ_ASSERT(!baseobj->containsPure(id));
- if (!baseobj->addDataProperty(cx, id, baseobj->slotSpan(), JSPROP_ENUMERATE))
+ if (!NativeObject::addDataProperty(cx, baseobj, id, baseobj->slotSpan(), JSPROP_ENUMERATE))
return false;
MOZ_ASSERT(baseobj->slotSpan() != slotSpan);
MOZ_ASSERT(!baseobj->inDictionaryMode());
@@ -4132,7 +4132,7 @@ CmpInstructions(const void* a, const void* b)
}
bool
-jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx, JSFunction* fun,
+jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx, HandleFunction fun,
ObjectGroup* group, HandlePlainObject baseobj,
Vector<TypeNewScript::Initializer>* initializerList)
{
@@ -4142,7 +4142,7 @@ jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx, JSFunction* fun,
// which will definitely be added to the created object before it has a
// chance to escape and be accessed elsewhere.
- RootedScript script(cx, fun->getOrCreateScript(cx));
+ RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
if (!script)
return false;
diff --git a/js/src/jit/IonAnalysis.h b/js/src/jit/IonAnalysis.h
index 1ce8edc80..efd31415b 100644
--- a/js/src/jit/IonAnalysis.h
+++ b/js/src/jit/IonAnalysis.h
@@ -196,7 +196,7 @@ MCompare*
ConvertLinearInequality(TempAllocator& alloc, MBasicBlock* block, const LinearSum& sum);
MOZ_MUST_USE bool
-AnalyzeNewScriptDefiniteProperties(JSContext* cx, JSFunction* fun,
+AnalyzeNewScriptDefiniteProperties(JSContext* cx, HandleFunction fun,
ObjectGroup* group, HandlePlainObject baseobj,
Vector<TypeNewScript::Initializer>* initializerList);
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index af85011be..2d053de5a 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -466,7 +466,8 @@ IonBuilder::canInlineTarget(JSFunction* target, CallInfo& callInfo)
// Allow constructing lazy scripts when performing the definite properties
// analysis, as baseline has not been used to warm the caller up yet.
if (target->isInterpreted() && info().analysisMode() == Analysis_DefiniteProperties) {
- RootedScript script(analysisContext, target->getOrCreateScript(analysisContext));
+ RootedFunction fun(analysisContext, target);
+ RootedScript script(analysisContext, JSFunction::getOrCreateScript(analysisContext, fun));
if (!script)
return InliningDecision_Error;
@@ -1938,6 +1939,7 @@ IonBuilder::inspectOpcode(JSOp op)
return jsop_funapply(GET_ARGC(pc));
case JSOP_CALL:
+ case JSOP_CALL_IGNORES_RV:
case JSOP_CALLITER:
case JSOP_NEW:
case JSOP_SUPERCALL:
@@ -1945,7 +1947,8 @@ IonBuilder::inspectOpcode(JSOp op)
if (!outermostBuilder()->iterators_.append(current->peek(-1)))
return false;
}
- return jsop_call(GET_ARGC(pc), (JSOp)*pc == JSOP_NEW || (JSOp)*pc == JSOP_SUPERCALL);
+ return jsop_call(GET_ARGC(pc), (JSOp)*pc == JSOP_NEW || (JSOp)*pc == JSOP_SUPERCALL,
+ (JSOp)*pc == JSOP_CALL_IGNORES_RV);
case JSOP_EVAL:
case JSOP_STRICTEVAL:
@@ -2218,6 +2221,8 @@ IonBuilder::inspectOpcode(JSOp op)
// update that stale value.
#endif
default:
+ // Any unused opcodes and JSOP_LIMIT will end up here without having
+ // to explicitly specify
break;
}
@@ -5871,7 +5876,7 @@ IonBuilder::inlineGenericFallback(JSFunction* target, CallInfo& callInfo, MBasic
return false;
// Create a new CallInfo to track modified state within this block.
- CallInfo fallbackInfo(alloc(), callInfo.constructing());
+ CallInfo fallbackInfo(alloc(), callInfo.constructing(), callInfo.ignoresReturnValue());
if (!fallbackInfo.init(callInfo))
return false;
fallbackInfo.popFormals(fallbackBlock);
@@ -5910,7 +5915,7 @@ IonBuilder::inlineObjectGroupFallback(CallInfo& callInfo, MBasicBlock* dispatchB
MOZ_ASSERT(cache->idempotent());
// Create a new CallInfo to track modified state within the fallback path.
- CallInfo fallbackInfo(alloc(), callInfo.constructing());
+ CallInfo fallbackInfo(alloc(), callInfo.constructing(), callInfo.ignoresReturnValue());
if (!fallbackInfo.init(callInfo))
return false;
@@ -6086,7 +6091,7 @@ IonBuilder::inlineCalls(CallInfo& callInfo, const ObjectVector& targets, BoolVec
inlineBlock->rewriteSlot(funIndex, funcDef);
// Create a new CallInfo to track modified state within the inline block.
- CallInfo inlineInfo(alloc(), callInfo.constructing());
+ CallInfo inlineInfo(alloc(), callInfo.constructing(), callInfo.ignoresReturnValue());
if (!inlineInfo.init(callInfo))
return false;
inlineInfo.popFormals(inlineBlock);
@@ -6535,7 +6540,8 @@ IonBuilder::jsop_funcall(uint32_t argc)
TemporaryTypeSet* calleeTypes = current->peek(calleeDepth)->resultTypeSet();
JSFunction* native = getSingleCallTarget(calleeTypes);
if (!native || !native->isNative() || native->native() != &fun_call) {
- CallInfo callInfo(alloc(), false);
+ CallInfo callInfo(alloc(), /* constructing = */ false,
+ /* ignoresReturnValue = */ BytecodeIsPopped(pc));
if (!callInfo.init(current, argc))
return false;
return makeCall(native, callInfo);
@@ -6560,7 +6566,8 @@ IonBuilder::jsop_funcall(uint32_t argc)
argc -= 1;
}
- CallInfo callInfo(alloc(), false);
+ CallInfo callInfo(alloc(), /* constructing = */ false,
+ /* ignoresReturnValue = */ BytecodeIsPopped(pc));
if (!callInfo.init(current, argc))
return false;
@@ -6597,7 +6604,8 @@ IonBuilder::jsop_funapply(uint32_t argc)
TemporaryTypeSet* calleeTypes = current->peek(calleeDepth)->resultTypeSet();
JSFunction* native = getSingleCallTarget(calleeTypes);
if (argc != 2 || info().analysisMode() == Analysis_ArgumentsUsage) {
- CallInfo callInfo(alloc(), false);
+ CallInfo callInfo(alloc(), /* constructing = */ false,
+ /* ignoresReturnValue = */ BytecodeIsPopped(pc));
if (!callInfo.init(current, argc))
return false;
return makeCall(native, callInfo);
@@ -6626,7 +6634,8 @@ IonBuilder::jsop_funapply(uint32_t argc)
return jsop_funapplyarray(argc);
}
- CallInfo callInfo(alloc(), false);
+ CallInfo callInfo(alloc(), /* constructing = */ false,
+ /* ignoresReturnValue = */ BytecodeIsPopped(pc));
if (!callInfo.init(current, argc))
return false;
return makeCall(native, callInfo);
@@ -6735,7 +6744,8 @@ IonBuilder::jsop_funapplyarguments(uint32_t argc)
// can inline the apply() target and don't care about the actual arguments
// that were passed in.
- CallInfo callInfo(alloc(), false);
+ CallInfo callInfo(alloc(), /* constructing = */ false,
+ /* ignoresReturnValue = */ BytecodeIsPopped(pc));
// Vp
MDefinition* vp = current->pop();
@@ -6781,7 +6791,7 @@ IonBuilder::jsop_funapplyarguments(uint32_t argc)
}
bool
-IonBuilder::jsop_call(uint32_t argc, bool constructing)
+IonBuilder::jsop_call(uint32_t argc, bool constructing, bool ignoresReturnValue)
{
startTrackingOptimizations();
@@ -6807,7 +6817,7 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing)
if (calleeTypes && !getPolyCallTargets(calleeTypes, constructing, targets, 4))
return false;
- CallInfo callInfo(alloc(), constructing);
+ CallInfo callInfo(alloc(), constructing, ignoresReturnValue);
if (!callInfo.init(current, argc))
return false;
@@ -6943,7 +6953,8 @@ IonBuilder::makeCallHelper(JSFunction* target, CallInfo& callInfo)
}
MCall* call = MCall::New(alloc(), target, targetArgs + 1 + callInfo.constructing(),
- callInfo.argc(), callInfo.constructing(), isDOMCall);
+ callInfo.argc(), callInfo.constructing(),
+ callInfo.ignoresReturnValue(), isDOMCall);
if (!call)
return nullptr;
@@ -7044,7 +7055,7 @@ IonBuilder::jsop_eval(uint32_t argc)
// Emit a normal call if the eval has never executed. This keeps us from
// disabling compilation for the script when testing with --ion-eager.
if (calleeTypes && calleeTypes->empty())
- return jsop_call(argc, /* constructing = */ false);
+ return jsop_call(argc, /* constructing = */ false, false);
JSFunction* singleton = getSingleCallTarget(calleeTypes);
if (!singleton)
@@ -7060,7 +7071,8 @@ IonBuilder::jsop_eval(uint32_t argc)
if (info().funMaybeLazy()->isArrow())
return abort("Direct eval from arrow function");
- CallInfo callInfo(alloc(), /* constructing = */ false);
+ CallInfo callInfo(alloc(), /* constructing = */ false,
+ /* ignoresReturnValue = */ BytecodeIsPopped(pc));
if (!callInfo.init(current, argc))
return false;
callInfo.setImplicitlyUsedUnchecked();
@@ -7099,7 +7111,8 @@ IonBuilder::jsop_eval(uint32_t argc)
current->push(dynamicName);
current->push(constant(UndefinedValue())); // thisv
- CallInfo evalCallInfo(alloc(), /* constructing = */ false);
+ CallInfo evalCallInfo(alloc(), /* constructing = */ false,
+ /* ignoresReturnValue = */ BytecodeIsPopped(pc));
if (!evalCallInfo.init(current, /* argc = */ 0))
return false;
@@ -7116,7 +7129,7 @@ IonBuilder::jsop_eval(uint32_t argc)
return resumeAfter(ins) && pushTypeBarrier(ins, types, BarrierKind::TypeSet);
}
- return jsop_call(argc, /* constructing = */ false);
+ return jsop_call(argc, /* constructing = */ false, false);
}
bool
@@ -7336,12 +7349,6 @@ IonBuilder::newArrayTryTemplateObject(bool* emitted, JSObject* templateObject, u
if (!templateObject)
return true;
- if (templateObject->is<UnboxedArrayObject>()) {
- MOZ_ASSERT(templateObject->as<UnboxedArrayObject>().capacity() >= length);
- if (!templateObject->as<UnboxedArrayObject>().hasInlineElements())
- return true;
- }
-
MOZ_ASSERT(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT);
size_t arraySlots =
@@ -7597,7 +7604,6 @@ IonBuilder::jsop_initelem_array()
// intializer, and that arrays are marked as non-packed when writing holes
// to them during initialization.
bool needStub = false;
- JSValueType unboxedType = JSVAL_TYPE_MAGIC;
if (shouldAbortOnPreliminaryGroups(obj)) {
needStub = true;
} else if (!obj->resultTypeSet() ||
@@ -7608,12 +7614,6 @@ IonBuilder::jsop_initelem_array()
} else {
MOZ_ASSERT(obj->resultTypeSet()->getObjectCount() == 1);
TypeSet::ObjectKey* initializer = obj->resultTypeSet()->getObject(0);
- if (initializer->clasp() == &UnboxedArrayObject::class_) {
- if (initializer->group()->unboxedLayout().nativeGroup())
- needStub = true;
- else
- unboxedType = initializer->group()->unboxedLayout().elementType();
- }
if (value->type() == MIRType::MagicHole) {
if (!initializer->hasFlags(constraints(), OBJECT_FLAG_NON_PACKED))
needStub = true;
@@ -7633,60 +7633,46 @@ IonBuilder::jsop_initelem_array()
return resumeAfter(store);
}
- return initializeArrayElement(obj, index, value, unboxedType, /* addResumePoint = */ true);
+ return initializeArrayElement(obj, index, value, /* addResumePoint = */ true);
}
bool
IonBuilder::initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value,
- JSValueType unboxedType,
bool addResumePointAndIncrementInitializedLength)
{
MConstant* id = MConstant::New(alloc(), Int32Value(index));
current->add(id);
// Get the elements vector.
- MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
+ MElements* elements = MElements::New(alloc(), obj);
current->add(elements);
- if (unboxedType != JSVAL_TYPE_MAGIC) {
- // Note: storeUnboxedValue takes care of any post barriers on the value.
- storeUnboxedValue(obj, elements, 0, id, unboxedType, value, /* preBarrier = */ false);
-
- if (addResumePointAndIncrementInitializedLength) {
- MInstruction* increment = MIncrementUnboxedArrayInitializedLength::New(alloc(), obj);
- current->add(increment);
-
- if (!resumeAfter(increment))
- return false;
- }
- } else {
- if (NeedsPostBarrier(value))
- current->add(MPostWriteBarrier::New(alloc(), obj, value));
+ if (NeedsPostBarrier(value))
+ current->add(MPostWriteBarrier::New(alloc(), obj, value));
- if ((obj->isNewArray() && obj->toNewArray()->convertDoubleElements()) ||
- (obj->isNullarySharedStub() &&
- obj->resultTypeSet()->convertDoubleElements(constraints()) == TemporaryTypeSet::AlwaysConvertToDoubles))
- {
- MInstruction* valueDouble = MToDouble::New(alloc(), value);
- current->add(valueDouble);
- value = valueDouble;
- }
+ if ((obj->isNewArray() && obj->toNewArray()->convertDoubleElements()) ||
+ (obj->isNullarySharedStub() &&
+ obj->resultTypeSet()->convertDoubleElements(constraints()) == TemporaryTypeSet::AlwaysConvertToDoubles))
+ {
+ MInstruction* valueDouble = MToDouble::New(alloc(), value);
+ current->add(valueDouble);
+ value = valueDouble;
+ }
- // Store the value.
- MStoreElement* store = MStoreElement::New(alloc(), elements, id, value,
+ // Store the value.
+ MStoreElement* store = MStoreElement::New(alloc(), elements, id, value,
/* needsHoleCheck = */ false);
- current->add(store);
+ current->add(store);
- if (addResumePointAndIncrementInitializedLength) {
- // Update the initialized length. (The template object for this
- // array has the array's ultimate length, so the length field is
- // already correct: no updating needed.)
- MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id);
- current->add(initLength);
+ if (addResumePointAndIncrementInitializedLength) {
+ // Update the initialized length. (The template object for this
+ // array has the array's ultimate length, so the length field is
+ // already correct: no updating needed.)
+ MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id);
+ current->add(initLength);
- if (!resumeAfter(initLength))
- return false;
- }
+ if (!resumeAfter(initLength))
+ return false;
}
return true;
@@ -8175,8 +8161,7 @@ IonBuilder::maybeMarkEmpty(MDefinition* ins)
static bool
ClassHasEffectlessLookup(const Class* clasp)
{
- return (clasp == &UnboxedArrayObject::class_) ||
- IsTypedObjectClass(clasp) ||
+ return IsTypedObjectClass(clasp) ||
(clasp->isNative() && !clasp->getOpsLookupProperty());
}
@@ -9452,12 +9437,9 @@ IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index)
{
MOZ_ASSERT(*emitted == false);
- JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, index);
- if (unboxedType == JSVAL_TYPE_MAGIC) {
- if (!ElementAccessIsDenseNative(constraints(), obj, index)) {
- trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
- return true;
- }
+ if (!ElementAccessIsDenseNative(constraints(), obj, index)) {
+ trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
+ return true;
}
// Don't generate a fast path if there have been bounds check failures
@@ -9474,7 +9456,7 @@ IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index)
return true;
}
- if (!jsop_getelem_dense(obj, index, unboxedType))
+ if (!jsop_getelem_dense(obj, index))
return false;
trackOptimizationSuccess();
@@ -9826,7 +9808,7 @@ IonBuilder::computeHeapType(const TemporaryTypeSet* objTypes, const jsid id)
}
bool
-IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType unboxedType)
+IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index)
{
TemporaryTypeSet* types = bytecodeTypes(pc);
@@ -9850,7 +9832,7 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType
!ElementAccessHasExtraIndexedProperty(this, obj);
MIRType knownType = MIRType::Value;
- if (unboxedType == JSVAL_TYPE_MAGIC && barrier == BarrierKind::NoBarrier)
+ if (barrier == BarrierKind::NoBarrier)
knownType = GetElemKnownType(needsHoleCheck, types);
// Ensure index is an integer.
@@ -9859,13 +9841,13 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType
index = idInt32;
// Get the elements vector.
- MInstruction* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
+ MInstruction* elements = MElements::New(alloc(), obj);
current->add(elements);
// Note: to help GVN, use the original MElements instruction and not
// MConvertElementsToDoubles as operand. This is fine because converting
// elements to double does not change the initialized length.
- MInstruction* initLength = initializedLength(obj, elements, unboxedType);
+ MInstruction* initLength = initializedLength(obj, elements);
// If we can load the element as a definite double, make sure to check that
// the array has been converted to homogenous doubles first.
@@ -9881,7 +9863,6 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType
}
bool loadDouble =
- unboxedType == JSVAL_TYPE_MAGIC &&
barrier == BarrierKind::NoBarrier &&
loopDepth_ &&
inBounds &&
@@ -9900,18 +9881,13 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType
// hoisting.
index = addBoundsCheck(index, initLength);
- if (unboxedType != JSVAL_TYPE_MAGIC) {
- load = loadUnboxedValue(elements, 0, index, unboxedType, barrier, types);
- } else {
- load = MLoadElement::New(alloc(), elements, index, needsHoleCheck, loadDouble);
- current->add(load);
- }
+ load = MLoadElement::New(alloc(), elements, index, needsHoleCheck, loadDouble);
+ current->add(load);
} else {
// This load may return undefined, so assume that we *can* read holes,
// or that we can read out-of-bounds accesses. In this case, the bounds
// check is part of the opcode.
- load = MLoadElementHole::New(alloc(), elements, index, initLength,
- unboxedType, needsHoleCheck);
+ load = MLoadElementHole::New(alloc(), elements, index, initLength, needsHoleCheck);
current->add(load);
// If maybeUndefined was true, the typeset must have undefined, and
@@ -9921,8 +9897,7 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType
}
if (knownType != MIRType::Value) {
- if (unboxedType == JSVAL_TYPE_MAGIC)
- load->setResultType(knownType);
+ load->setResultType(knownType);
load->setResultTypeSet(types);
}
@@ -10369,12 +10344,9 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object,
{
MOZ_ASSERT(*emitted == false);
- JSValueType unboxedType = UnboxedArrayElementType(constraints(), object, index);
- if (unboxedType == JSVAL_TYPE_MAGIC) {
- if (!ElementAccessIsDenseNative(constraints(), object, index)) {
- trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
- return true;
- }
+ if (!ElementAccessIsDenseNative(constraints(), object, index)) {
+ trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
+ return true;
}
if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
@@ -10408,7 +10380,7 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object,
}
// Emit dense setelem variant.
- if (!jsop_setelem_dense(conversion, object, index, value, unboxedType, writeHole, emitted))
+ if (!jsop_setelem_dense(conversion, object, index, value, writeHole, emitted))
return false;
if (!*emitted) {
@@ -10498,13 +10470,11 @@ IonBuilder::setElemTryCache(bool* emitted, MDefinition* object,
bool
IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
MDefinition* obj, MDefinition* id, MDefinition* value,
- JSValueType unboxedType, bool writeHole, bool* emitted)
+ bool writeHole, bool* emitted)
{
MOZ_ASSERT(*emitted == false);
- MIRType elementType = MIRType::None;
- if (unboxedType == JSVAL_TYPE_MAGIC)
- elementType = DenseNativeElementType(constraints(), obj);
+ MIRType elementType = DenseNativeElementType(constraints(), obj);
bool packed = ElementAccessIsPacked(constraints(), obj);
// Writes which are on holes in the object do not have to bail out if they
@@ -10534,7 +10504,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
// Get the elements vector.
- MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
+ MElements* elements = MElements::New(alloc(), obj);
current->add(elements);
// Ensure the value is a double, if double conversion might be needed.
@@ -10571,7 +10541,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
MInstruction* store;
MStoreElementCommon* common = nullptr;
if (writeHole && hasNoExtraIndexedProperty && !mayBeFrozen) {
- MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue, unboxedType);
+ MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue);
store = ins;
common = ins;
@@ -10583,27 +10553,23 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
bool strict = IsStrictSetPC(pc);
MFallibleStoreElement* ins = MFallibleStoreElement::New(alloc(), obj, elements, id,
- newValue, unboxedType, strict);
+ newValue, strict);
store = ins;
common = ins;
current->add(ins);
current->push(value);
} else {
- MInstruction* initLength = initializedLength(obj, elements, unboxedType);
+ MInstruction* initLength = initializedLength(obj, elements);
id = addBoundsCheck(id, initLength);
bool needsHoleCheck = !packed && !hasNoExtraIndexedProperty;
- if (unboxedType != JSVAL_TYPE_MAGIC) {
- store = storeUnboxedValue(obj, elements, 0, id, unboxedType, newValue);
- } else {
- MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck);
- store = ins;
- common = ins;
+ MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck);
+ store = ins;
+ common = ins;
- current->add(store);
- }
+ current->add(store);
current->push(value);
}
@@ -10721,18 +10687,6 @@ IonBuilder::jsop_length_fastPath()
return true;
}
- // Compute the length for unboxed array objects.
- if (UnboxedArrayElementType(constraints(), obj, nullptr) != JSVAL_TYPE_MAGIC &&
- !objTypes->hasObjectFlags(constraints(), OBJECT_FLAG_LENGTH_OVERFLOW))
- {
- current->pop();
-
- MUnboxedArrayLength* length = MUnboxedArrayLength::New(alloc(), obj);
- current->add(length);
- current->push(length);
- return true;
- }
-
// Compute the length for array typed objects.
TypedObjectPrediction prediction = typedObjectPrediction(obj);
if (!prediction.isUseless()) {
@@ -10982,63 +10936,6 @@ IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_
return slot;
}
-uint32_t
-IonBuilder::getUnboxedOffset(TemporaryTypeSet* types, PropertyName* name, JSValueType* punboxedType)
-{
- if (!types || types->unknownObject() || !types->objectOrSentinel()) {
- trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
- return UINT32_MAX;
- }
-
- uint32_t offset = UINT32_MAX;
-
- for (size_t i = 0; i < types->getObjectCount(); i++) {
- TypeSet::ObjectKey* key = types->getObject(i);
- if (!key)
- continue;
-
- if (key->unknownProperties()) {
- trackOptimizationOutcome(TrackedOutcome::UnknownProperties);
- return UINT32_MAX;
- }
-
- if (key->isSingleton()) {
- trackOptimizationOutcome(TrackedOutcome::Singleton);
- return UINT32_MAX;
- }
-
- UnboxedLayout* layout = key->group()->maybeUnboxedLayout();
- if (!layout) {
- trackOptimizationOutcome(TrackedOutcome::NotUnboxed);
- return UINT32_MAX;
- }
-
- const UnboxedLayout::Property* property = layout->lookup(name);
- if (!property) {
- trackOptimizationOutcome(TrackedOutcome::StructNoField);
- return UINT32_MAX;
- }
-
- if (layout->nativeGroup()) {
- trackOptimizationOutcome(TrackedOutcome::UnboxedConvertedToNative);
- return UINT32_MAX;
- }
-
- if (offset == UINT32_MAX) {
- offset = property->offset;
- *punboxedType = property->type;
- } else if (offset != property->offset) {
- trackOptimizationOutcome(TrackedOutcome::InconsistentFieldOffset);
- return UINT32_MAX;
- } else if (*punboxedType != property->type) {
- trackOptimizationOutcome(TrackedOutcome::InconsistentFieldType);
- return UINT32_MAX;
- }
- }
-
- return offset;
-}
-
bool
IonBuilder::jsop_runonce()
{
@@ -12005,72 +11902,6 @@ IonBuilder::getPropTryModuleNamespace(bool* emitted, MDefinition* obj, PropertyN
return true;
}
-MInstruction*
-IonBuilder::loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
- BarrierKind barrier, TemporaryTypeSet* types)
-{
- // loadUnboxedValue is designed to load any value as if it were contained in
- // an array. Thus a property offset is converted to an index, when the
- // object is reinterpreted as an array of properties of the same size.
- size_t index = offset / UnboxedTypeSize(unboxedType);
- MInstruction* indexConstant = MConstant::New(alloc(), Int32Value(index));
- current->add(indexConstant);
-
- return loadUnboxedValue(obj, UnboxedPlainObject::offsetOfData(),
- indexConstant, unboxedType, barrier, types);
-}
-
-MInstruction*
-IonBuilder::loadUnboxedValue(MDefinition* elements, size_t elementsOffset,
- MDefinition* index, JSValueType unboxedType,
- BarrierKind barrier, TemporaryTypeSet* types)
-{
- MInstruction* load;
- switch (unboxedType) {
- case JSVAL_TYPE_BOOLEAN:
- load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Uint8,
- DoesNotRequireMemoryBarrier, elementsOffset);
- load->setResultType(MIRType::Boolean);
- break;
-
- case JSVAL_TYPE_INT32:
- load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Int32,
- DoesNotRequireMemoryBarrier, elementsOffset);
- load->setResultType(MIRType::Int32);
- break;
-
- case JSVAL_TYPE_DOUBLE:
- load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Float64,
- DoesNotRequireMemoryBarrier, elementsOffset,
- /* canonicalizeDoubles = */ false);
- load->setResultType(MIRType::Double);
- break;
-
- case JSVAL_TYPE_STRING:
- load = MLoadUnboxedString::New(alloc(), elements, index, elementsOffset);
- break;
-
- case JSVAL_TYPE_OBJECT: {
- MLoadUnboxedObjectOrNull::NullBehavior nullBehavior;
- if (types->hasType(TypeSet::NullType()))
- nullBehavior = MLoadUnboxedObjectOrNull::HandleNull;
- else if (barrier != BarrierKind::NoBarrier)
- nullBehavior = MLoadUnboxedObjectOrNull::BailOnNull;
- else
- nullBehavior = MLoadUnboxedObjectOrNull::NullNotPossible;
- load = MLoadUnboxedObjectOrNull::New(alloc(), elements, index, nullBehavior,
- elementsOffset);
- break;
- }
-
- default:
- MOZ_CRASH();
- }
-
- current->add(load);
- return load;
-}
-
MDefinition*
IonBuilder::addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape,
const BaselineInspector::ReceiverVector& receivers,
@@ -12180,7 +12011,8 @@ IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName
current->push(obj);
- CallInfo callInfo(alloc(), false);
+ CallInfo callInfo(alloc(), /* constructing = */ false,
+ /* ignoresReturnValue = */ BytecodeIsPopped(pc));
if (!callInfo.init(current, 0))
return false;
@@ -12675,7 +12507,8 @@ IonBuilder::setPropTryCommonSetter(bool* emitted, MDefinition* obj,
// Call the setter. Note that we have to push the original value, not
// the setter's return value.
- CallInfo callInfo(alloc(), false);
+ CallInfo callInfo(alloc(), /* constructing = */ false,
+ /* ignoresReturnValue = */ BytecodeIsPopped(pc));
if (!callInfo.init(current, 1))
return false;
@@ -12892,66 +12725,6 @@ IonBuilder::setPropTryDefiniteSlot(bool* emitted, MDefinition* obj,
return true;
}
-MInstruction*
-IonBuilder::storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
- MDefinition* value)
-{
- size_t scaledOffsetConstant = offset / UnboxedTypeSize(unboxedType);
- MInstruction* scaledOffset = MConstant::New(alloc(), Int32Value(scaledOffsetConstant));
- current->add(scaledOffset);
-
- return storeUnboxedValue(obj, obj, UnboxedPlainObject::offsetOfData(),
- scaledOffset, unboxedType, value);
-}
-
-MInstruction*
-IonBuilder::storeUnboxedValue(MDefinition* obj, MDefinition* elements, int32_t elementsOffset,
- MDefinition* scaledOffset, JSValueType unboxedType,
- MDefinition* value, bool preBarrier /* = true */)
-{
- MInstruction* store;
- switch (unboxedType) {
- case JSVAL_TYPE_BOOLEAN:
- store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Uint8,
- MStoreUnboxedScalar::DontTruncateInput,
- DoesNotRequireMemoryBarrier, elementsOffset);
- break;
-
- case JSVAL_TYPE_INT32:
- store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Int32,
- MStoreUnboxedScalar::DontTruncateInput,
- DoesNotRequireMemoryBarrier, elementsOffset);
- break;
-
- case JSVAL_TYPE_DOUBLE:
- store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Float64,
- MStoreUnboxedScalar::DontTruncateInput,
- DoesNotRequireMemoryBarrier, elementsOffset);
- break;
-
- case JSVAL_TYPE_STRING:
- store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value,
- elementsOffset, preBarrier);
- break;
-
- case JSVAL_TYPE_OBJECT:
- MOZ_ASSERT(value->type() == MIRType::Object ||
- value->type() == MIRType::Null ||
- value->type() == MIRType::Value);
- MOZ_ASSERT(!value->mightBeType(MIRType::Undefined),
- "MToObjectOrNull slow path is invalid for unboxed objects");
- store = MStoreUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, value, obj,
- elementsOffset, preBarrier);
- break;
-
- default:
- MOZ_CRASH();
- }
-
- current->add(store);
- return store;
-}
-
bool
IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj,
PropertyName* name, MDefinition* value,
@@ -13687,11 +13460,8 @@ IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id)
if (shouldAbortOnPreliminaryGroups(obj))
return true;
- JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, id);
- if (unboxedType == JSVAL_TYPE_MAGIC) {
- if (!ElementAccessIsDenseNative(constraints(), obj, id))
- return true;
- }
+ if (!ElementAccessIsDenseNative(constraints(), obj, id))
+ return true;
if (ElementAccessHasExtraIndexedProperty(this, obj))
return true;
@@ -13706,10 +13476,10 @@ IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id)
id = idInt32;
// Get the elements vector.
- MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
+ MElements* elements = MElements::New(alloc(), obj);
current->add(elements);
- MInstruction* initLength = initializedLength(obj, elements, unboxedType);
+ MInstruction* initLength = initializedLength(obj, elements);
// If there are no holes, speculate the InArray check will not fail.
if (!needsHoleCheck && !failedBoundsCheck_) {
@@ -13719,8 +13489,7 @@ IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id)
}
// Check if id < initLength and elem[id] not a hole.
- MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck,
- unboxedType);
+ MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck);
current->add(ins);
current->push(ins);
@@ -14398,32 +14167,24 @@ IonBuilder::constantInt(int32_t i)
}
MInstruction*
-IonBuilder::initializedLength(MDefinition* obj, MDefinition* elements, JSValueType unboxedType)
+IonBuilder::initializedLength(MDefinition* obj, MDefinition* elements)
{
- MInstruction* res;
- if (unboxedType != JSVAL_TYPE_MAGIC)
- res = MUnboxedArrayInitializedLength::New(alloc(), obj);
- else
- res = MInitializedLength::New(alloc(), elements);
+ MInstruction* res = MInitializedLength::New(alloc(), elements);
current->add(res);
return res;
}
MInstruction*
-IonBuilder::setInitializedLength(MDefinition* obj, JSValueType unboxedType, size_t count)
+IonBuilder::setInitializedLength(MDefinition* obj, size_t count)
{
MOZ_ASSERT(count);
- MInstruction* res;
- if (unboxedType != JSVAL_TYPE_MAGIC) {
- res = MSetUnboxedArrayInitializedLength::New(alloc(), obj, constant(Int32Value(count)));
- } else {
- // MSetInitializedLength takes the index of the last element, rather
- // than the count itself.
- MInstruction* elements = MElements::New(alloc(), obj, /* unboxed = */ false);
- current->add(elements);
- res = MSetInitializedLength::New(alloc(), elements, constant(Int32Value(count - 1)));
- }
+ // MSetInitializedLength takes the index of the last element, rather
+ // than the count itself.
+ MInstruction* elements = MElements::New(alloc(), obj, /* unboxed = */ false);
+ current->add(elements);
+ MInstruction* res =
+ MSetInitializedLength::New(alloc(), elements, constant(Int32Value(count - 1)));
current->add(res);
return res;
}
diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h
index 77528ad37..1f763a4f2 100644
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -346,9 +346,8 @@ class IonBuilder
MConstant* constant(const Value& v);
MConstant* constantInt(int32_t i);
- MInstruction* initializedLength(MDefinition* obj, MDefinition* elements,
- JSValueType unboxedType);
- MInstruction* setInitializedLength(MDefinition* obj, JSValueType unboxedType, size_t count);
+ MInstruction* initializedLength(MDefinition* obj, MDefinition* elements);
+ MInstruction* setInitializedLength(MDefinition* obj, size_t count);
// Improve the type information at tests
MOZ_MUST_USE bool improveTypesAtTest(MDefinition* ins, bool trueBranch, MTest* test);
@@ -611,7 +610,6 @@ class IonBuilder
TypedObjectPrediction elemTypeReprs,
uint32_t elemSize);
MOZ_MUST_USE bool initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value,
- JSValueType unboxedType,
bool addResumePointAndIncrementInitializedLength);
// jsop_getelem() helpers.
@@ -701,6 +699,7 @@ class IonBuilder
MOZ_MUST_USE bool jsop_funapplyarguments(uint32_t argc);
MOZ_MUST_USE bool jsop_funapplyarray(uint32_t argc);
MOZ_MUST_USE bool jsop_call(uint32_t argc, bool constructing);
+ MOZ_MUST_USE bool jsop_call(uint32_t argc, bool constructing, bool ignoresReturnValue);
MOZ_MUST_USE bool jsop_eval(uint32_t argc);
MOZ_MUST_USE bool jsop_ifeq(JSOp op);
MOZ_MUST_USE bool jsop_try();
@@ -723,15 +722,13 @@ class IonBuilder
MOZ_MUST_USE bool jsop_bindname(PropertyName* name);
MOZ_MUST_USE bool jsop_bindvar();
MOZ_MUST_USE bool jsop_getelem();
- MOZ_MUST_USE bool jsop_getelem_dense(MDefinition* obj, MDefinition* index,
- JSValueType unboxedType);
+ MOZ_MUST_USE bool jsop_getelem_dense(MDefinition* obj, MDefinition* index);
MOZ_MUST_USE bool jsop_getelem_typed(MDefinition* obj, MDefinition* index,
ScalarTypeDescr::Type arrayType);
MOZ_MUST_USE bool jsop_setelem();
MOZ_MUST_USE bool jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
MDefinition* object, MDefinition* index,
- MDefinition* value, JSValueType unboxedType,
- bool writeHole, bool* emitted);
+ MDefinition* value, bool writeHole, bool* emitted);
MOZ_MUST_USE bool jsop_setelem_typed(ScalarTypeDescr::Type arrayType,
MDefinition* object, MDefinition* index,
MDefinition* value);
@@ -824,7 +821,6 @@ class IonBuilder
InliningStatus inlineArrayPush(CallInfo& callInfo);
InliningStatus inlineArraySlice(CallInfo& callInfo);
InliningStatus inlineArrayJoin(CallInfo& callInfo);
- InliningStatus inlineArraySplice(CallInfo& callInfo);
// Math natives.
InliningStatus inlineMathAbs(CallInfo& callInfo);
@@ -1052,19 +1048,6 @@ class IonBuilder
ResultWithOOM<bool> testNotDefinedProperty(MDefinition* obj, jsid id);
uint32_t getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed);
- uint32_t getUnboxedOffset(TemporaryTypeSet* types, PropertyName* name,
- JSValueType* punboxedType);
- MInstruction* loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
- BarrierKind barrier, TemporaryTypeSet* types);
- MInstruction* loadUnboxedValue(MDefinition* elements, size_t elementsOffset,
- MDefinition* scaledOffset, JSValueType unboxedType,
- BarrierKind barrier, TemporaryTypeSet* types);
- MInstruction* storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
- MDefinition* value);
- MInstruction* storeUnboxedValue(MDefinition* obj,
- MDefinition* elements, int32_t elementsOffset,
- MDefinition* scaledOffset, JSValueType unboxedType,
- MDefinition* value, bool preBarrier = true);
MOZ_MUST_USE bool checkPreliminaryGroups(MDefinition *obj);
MOZ_MUST_USE bool freezePropTypeSets(TemporaryTypeSet* types,
JSObject* foundProto, PropertyName* name);
@@ -1369,16 +1352,21 @@ class CallInfo
MDefinition* newTargetArg_;
MDefinitionVector args_;
- bool constructing_;
- bool setter_;
+ bool constructing_:1;
+
+ // True if the caller does not use the return value.
+ bool ignoresReturnValue_:1;
+
+ bool setter_:1;
public:
- CallInfo(TempAllocator& alloc, bool constructing)
+ CallInfo(TempAllocator& alloc, bool constructing, bool ignoresReturnValue)
: fun_(nullptr),
thisArg_(nullptr),
newTargetArg_(nullptr),
args_(alloc),
constructing_(constructing),
+ ignoresReturnValue_(ignoresReturnValue),
setter_(false)
{ }
@@ -1387,6 +1375,7 @@ class CallInfo
fun_ = callInfo.fun();
thisArg_ = callInfo.thisArg();
+ ignoresReturnValue_ = callInfo.ignoresReturnValue();
if (constructing())
newTargetArg_ = callInfo.getNewTarget();
@@ -1483,6 +1472,10 @@ class CallInfo
return constructing_;
}
+ bool ignoresReturnValue() const {
+ return ignoresReturnValue_;
+ }
+
void setNewTarget(MDefinition* newTarget) {
MOZ_ASSERT(constructing());
newTargetArg_ = newTarget;
diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp
index 0208db6ae..48e0792bb 100644
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -31,7 +31,6 @@
#include "jit/shared/Lowering-shared-inl.h"
#include "vm/Interpreter-inl.h"
#include "vm/Shape-inl.h"
-#include "vm/UnboxedObject-inl.h"
using namespace js;
using namespace js::jit;
@@ -620,29 +619,7 @@ TestMatchingReceiver(MacroAssembler& masm, IonCache::StubAttacher& attacher,
Register object, JSObject* obj, Label* failure,
bool alwaysCheckGroup = false)
{
- if (obj->is<UnboxedPlainObject>()) {
- MOZ_ASSERT(failure);
-
- masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
- Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
- if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
- masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure);
- Label success;
- masm.push(object);
- masm.loadPtr(expandoAddress, object);
- masm.branchTestObjShape(Assembler::Equal, object, expando->lastProperty(),
- &success);
- masm.pop(object);
- masm.jump(failure);
- masm.bind(&success);
- masm.pop(object);
- } else {
- masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure);
- }
- } else if (obj->is<UnboxedArrayObject>()) {
- MOZ_ASSERT(failure);
- masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
- } else if (obj->is<TypedObject>()) {
+ if (obj->is<TypedObject>()) {
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
Address(object, JSObject::offsetOfGroup()),
ImmGCPtr(obj->group()), failure);
@@ -759,7 +736,6 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm,
// jump directly. Otherwise, jump to the end of the stub, so there's a
// common point to patch.
bool multipleFailureJumps = (obj != holder)
- || obj->is<UnboxedPlainObject>()
|| (checkTDZ && output.hasValue())
|| (failures != nullptr && failures->used());
@@ -778,7 +754,6 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm,
Register scratchReg = Register::FromCode(0); // Quell compiler warning.
if (obj != holder ||
- obj->is<UnboxedPlainObject>() ||
!holder->as<NativeObject>().isFixedSlot(shape->slot()))
{
if (output.hasValue()) {
@@ -839,10 +814,6 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm,
holderReg = InvalidReg;
}
- } else if (obj->is<UnboxedPlainObject>()) {
- holder = obj->as<UnboxedPlainObject>().maybeExpando();
- holderReg = scratchReg;
- masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), holderReg);
} else {
holderReg = object;
}
@@ -870,30 +841,6 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm,
attacher.jumpNextStub(masm);
}
-static void
-GenerateReadUnboxed(JSContext* cx, IonScript* ion, MacroAssembler& masm,
- IonCache::StubAttacher& attacher, JSObject* obj,
- const UnboxedLayout::Property* property,
- Register object, TypedOrValueRegister output,
- Label* failures = nullptr)
-{
- // Guard on the group of the object.
- attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
- Address(object, JSObject::offsetOfGroup()),
- ImmGCPtr(obj->group()), failures);
-
- Address address(object, UnboxedPlainObject::offsetOfData() + property->offset);
-
- masm.loadUnboxedProperty(address, property->type, output);
-
- attacher.jumpRejoin(masm);
-
- if (failures) {
- masm.bind(failures);
- attacher.jumpNextStub(masm);
- }
-}
-
static bool
EmitGetterCall(JSContext* cx, MacroAssembler& masm,
IonCache::StubAttacher& attacher, JSObject* obj,
@@ -1188,39 +1135,6 @@ GenerateArrayLength(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher&
return true;
}
-static void
-GenerateUnboxedArrayLength(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
- JSObject* array, Register object, TypedOrValueRegister output,
- Label* failures)
-{
- Register outReg;
- if (output.hasValue()) {
- outReg = output.valueReg().scratchReg();
- } else {
- MOZ_ASSERT(output.type() == MIRType::Int32);
- outReg = output.typedReg().gpr();
- }
- MOZ_ASSERT(object != outReg);
-
- TestMatchingReceiver(masm, attacher, object, array, failures);
-
- // Load length.
- masm.load32(Address(object, UnboxedArrayObject::offsetOfLength()), outReg);
-
- // Check for a length that fits in an int32.
- masm.branchTest32(Assembler::Signed, outReg, outReg, failures);
-
- if (output.hasValue())
- masm.tagValue(JSVAL_TYPE_INT32, outReg, output.valueReg());
-
- // Success.
- attacher.jumpRejoin(masm);
-
- // Failure.
- masm.bind(failures);
- attacher.jumpNextStub(masm);
-}
-
// In this case, the code for TypedArray and SharedTypedArray is not the same,
// because the code embeds pointers to the respective class arrays. Code that
// caches the stub code must distinguish between the two cases.
@@ -1534,101 +1448,6 @@ GetPropertyIC::tryAttachNative(JSContext* cx, HandleScript outerScript, IonScrip
}
bool
-GetPropertyIC::tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion,
- HandleObject obj, HandleId id, void* returnAddr, bool* emitted)
-{
- MOZ_ASSERT(canAttachStub());
- MOZ_ASSERT(!*emitted);
- MOZ_ASSERT(outerScript->ionScript() == ion);
-
- if (!obj->is<UnboxedPlainObject>())
- return true;
- const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
- if (!property)
- return true;
-
- *emitted = true;
-
- MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
-
- Label failures;
- emitIdGuard(masm, id, &failures);
- Label* maybeFailures = failures.used() ? &failures : nullptr;
-
- StubAttacher attacher(*this);
- GenerateReadUnboxed(cx, ion, masm, attacher, obj, property, object(), output(), maybeFailures);
- return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed",
- JS::TrackedOutcome::ICGetPropStub_UnboxedRead);
-}
-
-bool
-GetPropertyIC::tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, IonScript* ion,
- HandleObject obj, HandleId id, void* returnAddr, bool* emitted)
-{
- MOZ_ASSERT(canAttachStub());
- MOZ_ASSERT(!*emitted);
- MOZ_ASSERT(outerScript->ionScript() == ion);
-
- if (!obj->is<UnboxedPlainObject>())
- return true;
- Rooted<UnboxedExpandoObject*> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando());
- if (!expando)
- return true;
-
- Shape* shape = expando->lookup(cx, id);
- if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
- return true;
-
- *emitted = true;
-
- MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
-
- Label failures;
- emitIdGuard(masm, id, &failures);
- Label* maybeFailures = failures.used() ? &failures : nullptr;
-
- StubAttacher attacher(*this);
- GenerateReadSlot(cx, ion, masm, attacher, DontCheckTDZ, obj, obj,
- shape, object(), output(), maybeFailures);
- return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed expando",
- JS::TrackedOutcome::ICGetPropStub_UnboxedReadExpando);
-}
-
-bool
-GetPropertyIC::tryAttachUnboxedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
- HandleObject obj, HandleId id, void* returnAddr,
- bool* emitted)
-{
- MOZ_ASSERT(canAttachStub());
- MOZ_ASSERT(!*emitted);
- MOZ_ASSERT(outerScript->ionScript() == ion);
-
- if (!obj->is<UnboxedArrayObject>())
- return true;
-
- if (!JSID_IS_ATOM(id, cx->names().length))
- return true;
-
- if (obj->as<UnboxedArrayObject>().length() > INT32_MAX)
- return true;
-
- if (!allowArrayLength(cx))
- return true;
-
- *emitted = true;
-
- MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
-
- Label failures;
- emitIdGuard(masm, id, &failures);
-
- StubAttacher attacher(*this);
- GenerateUnboxedArrayLength(cx, masm, attacher, obj, object(), output(), &failures);
- return linkAndAttachStub(cx, masm, attacher, ion, "unboxed array length",
- JS::TrackedOutcome::ICGetPropStub_UnboxedArrayLength);
-}
-
-bool
GetPropertyIC::tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject obj, HandleId id, bool* emitted)
{
@@ -2197,15 +2016,6 @@ GetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript*
if (!*emitted && !tryAttachNative(cx, outerScript, ion, obj, id, returnAddr, emitted))
return false;
- if (!*emitted && !tryAttachUnboxed(cx, outerScript, ion, obj, id, returnAddr, emitted))
- return false;
-
- if (!*emitted && !tryAttachUnboxedExpando(cx, outerScript, ion, obj, id, returnAddr, emitted))
- return false;
-
- if (!*emitted && !tryAttachUnboxedArrayLength(cx, outerScript, ion, obj, id, returnAddr, emitted))
- return false;
-
if (!*emitted && !tryAttachTypedArrayLength(cx, outerScript, ion, obj, id, emitted))
return false;
}
@@ -2384,12 +2194,6 @@ GenerateSetSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att
NativeObject::slotsSizeMustNotOverflow();
- if (obj->is<UnboxedPlainObject>()) {
- obj = obj->as<UnboxedPlainObject>().maybeExpando();
- masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), tempReg);
- object = tempReg;
- }
-
if (obj->as<NativeObject>().isFixedSlot(shape->slot())) {
Address addr(object, NativeObject::getFixedSlotOffset(shape->slot()));
@@ -3027,23 +2831,13 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att
masm.branchTestObjGroup(Assembler::NotEqual, object, oldGroup, failures);
if (obj->maybeShape()) {
masm.branchTestObjShape(Assembler::NotEqual, object, oldShape, failures);
- } else {
- MOZ_ASSERT(obj->is<UnboxedPlainObject>());
-
- Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
- masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failures);
-
- masm.loadPtr(expandoAddress, tempReg);
- masm.branchTestObjShape(Assembler::NotEqual, tempReg, oldShape, failures);
}
Shape* newShape = obj->maybeShape();
- if (!newShape)
- newShape = obj->as<UnboxedPlainObject>().maybeExpando()->lastProperty();
// Guard that the incoming value is in the type set for the property
// if a type barrier is required.
- if (checkTypeset)
+ if (newShape && checkTypeset)
CheckTypeSetForWrite(masm, obj, newShape->propid(), tempReg, value, failures);
// Guard shapes along prototype chain.
@@ -3064,9 +2858,7 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att
}
// Call a stub to (re)allocate dynamic slots, if necessary.
- uint32_t newNumDynamicSlots = obj->is<UnboxedPlainObject>()
- ? obj->as<UnboxedPlainObject>().maybeExpando()->numDynamicSlots()
- : obj->as<NativeObject>().numDynamicSlots();
+ uint32_t newNumDynamicSlots = obj->as<NativeObject>().numDynamicSlots();
if (NativeObject::dynamicSlotsCount(oldShape) != newNumDynamicSlots) {
AllocatableRegisterSet regs(RegisterSet::Volatile());
LiveRegisterSet save(regs.asLiveSet());
@@ -3077,12 +2869,6 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att
Register temp1 = regs.takeAnyGeneral();
Register temp2 = regs.takeAnyGeneral();
- if (obj->is<UnboxedPlainObject>()) {
- // Pass the expando object to the stub.
- masm.Push(object);
- masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), object);
- }
-
masm.setupUnalignedABICall(temp1);
masm.loadJSContext(temp1);
masm.passABIArg(temp1);
@@ -3099,27 +2885,16 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att
masm.jump(&allocDone);
masm.bind(&allocFailed);
- if (obj->is<UnboxedPlainObject>())
- masm.Pop(object);
masm.PopRegsInMask(save);
masm.jump(failures);
masm.bind(&allocDone);
masm.setFramePushed(framePushedAfterCall);
- if (obj->is<UnboxedPlainObject>())
- masm.Pop(object);
masm.PopRegsInMask(save);
}
bool popObject = false;
- if (obj->is<UnboxedPlainObject>()) {
- masm.push(object);
- popObject = true;
- obj = obj->as<UnboxedPlainObject>().maybeExpando();
- masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), object);
- }
-
// Write the object or expando object's new shape.
Address shapeAddr(object, ShapedObject::offsetOfShape());
if (cx->zone()->needsIncrementalBarrier())
@@ -3127,8 +2902,6 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att
masm.storePtr(ImmGCPtr(newShape), shapeAddr);
if (oldGroup != obj->group()) {
- MOZ_ASSERT(!obj->is<UnboxedPlainObject>());
-
// Changing object's group from a partially to fully initialized group,
// per the acquired properties analysis. Only change the group if the
// old group still has a newScript.
@@ -3371,141 +3144,6 @@ CanAttachNativeSetProp(JSContext* cx, HandleObject obj, HandleId id, const Const
return SetPropertyIC::CanAttachNone;
}
-static void
-GenerateSetUnboxed(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
- JSObject* obj, jsid id, uint32_t unboxedOffset, JSValueType unboxedType,
- Register object, Register tempReg, const ConstantOrRegister& value,
- bool checkTypeset, Label* failures)
-{
- // Guard on the type of the object.
- masm.branchPtr(Assembler::NotEqual,
- Address(object, JSObject::offsetOfGroup()),
- ImmGCPtr(obj->group()), failures);
-
- if (checkTypeset)
- CheckTypeSetForWrite(masm, obj, id, tempReg, value, failures);
-
- Address address(object, UnboxedPlainObject::offsetOfData() + unboxedOffset);
-
- if (cx->zone()->needsIncrementalBarrier()) {
- if (unboxedType == JSVAL_TYPE_OBJECT)
- masm.callPreBarrier(address, MIRType::Object);
- else if (unboxedType == JSVAL_TYPE_STRING)
- masm.callPreBarrier(address, MIRType::String);
- else
- MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(unboxedType));
- }
-
- masm.storeUnboxedProperty(address, unboxedType, value, failures);
-
- attacher.jumpRejoin(masm);
-
- masm.bind(failures);
- attacher.jumpNextStub(masm);
-}
-
-static bool
-CanAttachSetUnboxed(JSContext* cx, HandleObject obj, HandleId id, const ConstantOrRegister& val,
- bool needsTypeBarrier, bool* checkTypeset,
- uint32_t* unboxedOffset, JSValueType* unboxedType)
-{
- if (!obj->is<UnboxedPlainObject>())
- return false;
-
- const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
- if (property) {
- *checkTypeset = false;
- if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset))
- return false;
- *unboxedOffset = property->offset;
- *unboxedType = property->type;
- return true;
- }
-
- return false;
-}
-
-static bool
-CanAttachSetUnboxedExpando(JSContext* cx, HandleObject obj, HandleId id,
- const ConstantOrRegister& val,
- bool needsTypeBarrier, bool* checkTypeset, Shape** pshape)
-{
- if (!obj->is<UnboxedPlainObject>())
- return false;
-
- Rooted<UnboxedExpandoObject*> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando());
- if (!expando)
- return false;
-
- Shape* shape = expando->lookupPure(id);
- if (!shape || !shape->hasDefaultSetter() || !shape->hasSlot() || !shape->writable())
- return false;
-
- *checkTypeset = false;
- if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset))
- return false;
-
- *pshape = shape;
- return true;
-}
-
-static bool
-CanAttachAddUnboxedExpando(JSContext* cx, HandleObject obj, HandleShape oldShape,
- HandleId id, const ConstantOrRegister& val,
- bool needsTypeBarrier, bool* checkTypeset)
-{
- if (!obj->is<UnboxedPlainObject>())
- return false;
-
- Rooted<UnboxedExpandoObject*> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando());
- if (!expando || expando->inDictionaryMode())
- return false;
-
- Shape* newShape = expando->lastProperty();
- if (newShape->isEmptyShape() || newShape->propid() != id || newShape->previous() != oldShape)
- return false;
-
- MOZ_ASSERT(newShape->hasDefaultSetter() && newShape->hasSlot() && newShape->writable());
-
- if (PrototypeChainShadowsPropertyAdd(cx, obj, id))
- return false;
-
- *checkTypeset = false;
- if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset))
- return false;
-
- return true;
-}
-
-bool
-SetPropertyIC::tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion,
- HandleObject obj, HandleId id, bool* emitted)
-{
- MOZ_ASSERT(!*emitted);
-
- bool checkTypeset = false;
- uint32_t unboxedOffset;
- JSValueType unboxedType;
- if (!CanAttachSetUnboxed(cx, obj, id, value(), needsTypeBarrier(), &checkTypeset,
- &unboxedOffset, &unboxedType))
- {
- return true;
- }
-
- *emitted = true;
-
- MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
- StubAttacher attacher(*this);
-
- Label failures;
- emitIdGuard(masm, id, &failures);
-
- GenerateSetUnboxed(cx, masm, attacher, obj, id, unboxedOffset, unboxedType,
- object(), temp(), value(), checkTypeset, &failures);
- return linkAndAttachStub(cx, masm, attacher, ion, "set_unboxed",
- JS::TrackedOutcome::ICSetPropStub_SetUnboxed);
-}
-
bool
SetPropertyIC::tryAttachProxy(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject obj, HandleId id, bool* emitted)
@@ -3587,26 +3225,6 @@ SetPropertyIC::tryAttachNative(JSContext* cx, HandleScript outerScript, IonScrip
}
bool
-SetPropertyIC::tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, IonScript* ion,
- HandleObject obj, HandleId id, bool* emitted)
-{
- MOZ_ASSERT(!*emitted);
-
- RootedShape shape(cx);
- bool checkTypeset = false;
- if (!CanAttachSetUnboxedExpando(cx, obj, id, value(), needsTypeBarrier(),
- &checkTypeset, shape.address()))
- {
- return true;
- }
-
- if (!attachSetSlot(cx, outerScript, ion, obj, shape, checkTypeset))
- return false;
- *emitted = true;
- return true;
-}
-
-bool
SetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject obj, HandleValue idval, HandleValue value,
MutableHandleId id, bool* emitted, bool* tryNativeAddSlot)
@@ -3631,12 +3249,6 @@ SetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript*
if (!*emitted && !tryAttachNative(cx, outerScript, ion, obj, id, emitted, tryNativeAddSlot))
return false;
-
- if (!*emitted && !tryAttachUnboxed(cx, outerScript, ion, obj, id, emitted))
- return false;
-
- if (!*emitted && !tryAttachUnboxedExpando(cx, outerScript, ion, obj, id, emitted))
- return false;
}
if (idval.isInt32()) {
@@ -3688,16 +3300,6 @@ SetPropertyIC::tryAttachAddSlot(JSContext* cx, HandleScript outerScript, IonScri
return true;
}
- checkTypeset = false;
- if (CanAttachAddUnboxedExpando(cx, obj, oldShape, id, value(), needsTypeBarrier(),
- &checkTypeset))
- {
- if (!attachAddSlot(cx, outerScript, ion, obj, id, oldShape, oldGroup, checkTypeset))
- return false;
- *emitted = true;
- return true;
- }
-
return true;
}
@@ -3714,16 +3316,11 @@ SetPropertyIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex
RootedObjectGroup oldGroup(cx);
RootedShape oldShape(cx);
if (cache.canAttachStub()) {
- oldGroup = obj->getGroup(cx);
+ oldGroup = JSObject::getGroup(cx, obj);
if (!oldGroup)
return false;
oldShape = obj->maybeShape();
- if (obj->is<UnboxedPlainObject>()) {
- MOZ_ASSERT(!oldShape);
- if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
- oldShape = expando->lastProperty();
- }
}
RootedId id(cx);
@@ -4026,7 +3623,7 @@ GetPropertyIC::tryAttachDenseElementHole(JSContext* cx, HandleScript outerScript
GetPropertyIC::canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& idval,
TypedOrValueRegister output)
{
- if (!obj->is<TypedArrayObject>() && !obj->is<UnboxedArrayObject>())
+ if (!obj->is<TypedArrayObject>())
return false;
MOZ_ASSERT(idval.isInt32() || idval.isString());
@@ -4057,13 +3654,6 @@ GetPropertyIC::canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& i
return output.hasValue() || !output.typedReg().isFloat();
}
- if (index >= obj->as<UnboxedArrayObject>().initializedLength())
- return false;
-
- JSValueType elementType = obj->as<UnboxedArrayObject>().elementType();
- if (elementType == JSVAL_TYPE_DOUBLE)
- return output.hasValue();
-
return output.hasValue() || !output.typedReg().isFloat();
}
@@ -4140,46 +3730,27 @@ GenerateGetTypedOrUnboxedArrayElement(JSContext* cx, MacroAssembler& masm,
Label popObjectAndFail;
- if (array->is<TypedArrayObject>()) {
- // Guard on the initialized length.
- Address length(object, TypedArrayObject::lengthOffset());
- masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures);
+ // Guard on the initialized length.
+ Address length(object, TypedArrayObject::lengthOffset());
+ masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures);
- // Save the object register on the stack in case of failure.
- Register elementReg = object;
- masm.push(object);
+ // Save the object register on the stack in case of failure.
+ Register elementReg = object;
+ masm.push(object);
- // Load elements vector.
- masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elementReg);
+ // Load elements vector.
+ masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elementReg);
- // Load the value. We use an invalid register because the destination
- // register is necessary a non double register.
- Scalar::Type arrayType = array->as<TypedArrayObject>().type();
- int width = Scalar::byteSize(arrayType);
- BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(width));
- if (output.hasValue()) {
- masm.loadFromTypedArray(arrayType, source, output.valueReg(), allowDoubleResult,
- elementReg, &popObjectAndFail);
- } else {
- masm.loadFromTypedArray(arrayType, source, output.typedReg(), elementReg, &popObjectAndFail);
- }
+ // Load the value. We use an invalid register because the destination
+ // register is necessary a non double register.
+ Scalar::Type arrayType = array->as<TypedArrayObject>().type();
+ int width = Scalar::byteSize(arrayType);
+ BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(width));
+ if (output.hasValue()) {
+ masm.loadFromTypedArray(arrayType, source, output.valueReg(), allowDoubleResult,
+ elementReg, &popObjectAndFail);
} else {
- // Save the object register on the stack in case of failure.
- masm.push(object);
-
- // Guard on the initialized length.
- masm.load32(Address(object, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), object);
- masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), object);
- masm.branch32(Assembler::BelowOrEqual, object, indexReg, &popObjectAndFail);
-
- // Load elements vector.
- Register elementReg = object;
- masm.loadPtr(Address(masm.getStackPointer(), 0), object);
- masm.loadPtr(Address(object, UnboxedArrayObject::offsetOfElements()), elementReg);
-
- JSValueType elementType = array->as<UnboxedArrayObject>().elementType();
- BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(UnboxedTypeSize(elementType)));
- masm.loadUnboxedProperty(source, elementType, output);
+ masm.loadFromTypedArray(arrayType, source, output.typedReg(), elementReg, &popObjectAndFail);
}
masm.pop(object);
diff --git a/js/src/jit/IonCaches.h b/js/src/jit/IonCaches.h
index 173e06c6b..b00646538 100644
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -529,18 +529,6 @@ class GetPropertyIC : public IonCache
HandleObject obj, HandleId id, void* returnAddr,
bool* emitted);
- MOZ_MUST_USE bool tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion,
- HandleObject obj, HandleId id, void* returnAddr,
- bool* emitted);
-
- MOZ_MUST_USE bool tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript,
- IonScript* ion, HandleObject obj, HandleId id,
- void* returnAddr, bool* emitted);
-
- MOZ_MUST_USE bool tryAttachUnboxedArrayLength(JSContext* cx, HandleScript outerScript,
- IonScript* ion, HandleObject obj, HandleId id,
- void* returnAddr, bool* emitted);
-
MOZ_MUST_USE bool tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript,
IonScript* ion, HandleObject obj, HandleId id,
bool* emitted);
diff --git a/js/src/jit/JitOptions.cpp b/js/src/jit/JitOptions.cpp
index b9a7c7b27..3f9d9db88 100644
--- a/js/src/jit/JitOptions.cpp
+++ b/js/src/jit/JitOptions.cpp
@@ -221,9 +221,6 @@ DefaultJitOptions::DefaultJitOptions()
Warn(forcedRegisterAllocatorEnv, env);
}
- // Toggles whether unboxed plain objects can be created by the VM.
- SET_DEFAULT(disableUnboxedObjects, true);
-
// Test whether Atomics are allowed in asm.js code.
SET_DEFAULT(asmJSAtomicsEnable, false);
diff --git a/js/src/jit/JitOptions.h b/js/src/jit/JitOptions.h
index 076980b4e..719ee14d9 100644
--- a/js/src/jit/JitOptions.h
+++ b/js/src/jit/JitOptions.h
@@ -91,9 +91,6 @@ struct DefaultJitOptions
mozilla::Maybe<uint32_t> forcedDefaultIonSmallFunctionWarmUpThreshold;
mozilla::Maybe<IonRegisterAllocator> forcedRegisterAllocator;
- // The options below affect the rest of the VM, and not just the JIT.
- bool disableUnboxedObjects;
-
DefaultJitOptions();
bool isSmallFunction(JSScript* script) const;
void setEagerCompilation();
diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp
index aafa57c09..22f1d5f70 100644
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -657,16 +657,6 @@ LIRGenerator::visitAssertRecoveredOnBailout(MAssertRecoveredOnBailout* assertion
}
void
-LIRGenerator::visitArraySplice(MArraySplice* ins)
-{
- LArraySplice* lir = new(alloc()) LArraySplice(useRegisterAtStart(ins->object()),
- useRegisterAtStart(ins->start()),
- useRegisterAtStart(ins->deleteCount()));
- add(lir, ins);
- assignSafepoint(lir, ins);
-}
-
-void
LIRGenerator::visitGetDynamicName(MGetDynamicName* ins)
{
MDefinition* envChain = ins->getEnvironmentChain();
@@ -2889,32 +2879,6 @@ LIRGenerator::visitSetInitializedLength(MSetInitializedLength* ins)
}
void
-LIRGenerator::visitUnboxedArrayLength(MUnboxedArrayLength* ins)
-{
- define(new(alloc()) LUnboxedArrayLength(useRegisterAtStart(ins->object())), ins);
-}
-
-void
-LIRGenerator::visitUnboxedArrayInitializedLength(MUnboxedArrayInitializedLength* ins)
-{
- define(new(alloc()) LUnboxedArrayInitializedLength(useRegisterAtStart(ins->object())), ins);
-}
-
-void
-LIRGenerator::visitIncrementUnboxedArrayInitializedLength(MIncrementUnboxedArrayInitializedLength* ins)
-{
- add(new(alloc()) LIncrementUnboxedArrayInitializedLength(useRegister(ins->object())), ins);
-}
-
-void
-LIRGenerator::visitSetUnboxedArrayInitializedLength(MSetUnboxedArrayInitializedLength* ins)
-{
- add(new(alloc()) LSetUnboxedArrayInitializedLength(useRegister(ins->object()),
- useRegisterOrConstant(ins->length()),
- temp()), ins);
-}
-
-void
LIRGenerator::visitNot(MNot* ins)
{
MDefinition* op = ins->input();
@@ -3163,22 +3127,16 @@ LIRGenerator::visitStoreElementHole(MStoreElementHole* ins)
const LUse elements = useRegister(ins->elements());
const LAllocation index = useRegisterOrConstant(ins->index());
- // Use a temp register when adding new elements to unboxed arrays.
- LDefinition tempDef = LDefinition::BogusTemp();
- if (ins->unboxedType() != JSVAL_TYPE_MAGIC)
- tempDef = temp();
-
LInstruction* lir;
switch (ins->value()->type()) {
case MIRType::Value:
- lir = new(alloc()) LStoreElementHoleV(object, elements, index, useBox(ins->value()),
- tempDef);
+ lir = new(alloc()) LStoreElementHoleV(object, elements, index, useBox(ins->value()));
break;
default:
{
const LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
- lir = new(alloc()) LStoreElementHoleT(object, elements, index, value, tempDef);
+ lir = new(alloc()) LStoreElementHoleT(object, elements, index, value);
break;
}
}
@@ -3197,20 +3155,14 @@ LIRGenerator::visitFallibleStoreElement(MFallibleStoreElement* ins)
const LUse elements = useRegister(ins->elements());
const LAllocation index = useRegisterOrConstant(ins->index());
- // Use a temp register when adding new elements to unboxed arrays.
- LDefinition tempDef = LDefinition::BogusTemp();
- if (ins->unboxedType() != JSVAL_TYPE_MAGIC)
- tempDef = temp();
-
LInstruction* lir;
switch (ins->value()->type()) {
case MIRType::Value:
- lir = new(alloc()) LFallibleStoreElementV(object, elements, index, useBox(ins->value()),
- tempDef);
+ lir = new(alloc()) LFallibleStoreElementV(object, elements, index, useBox(ins->value()));
break;
default:
const LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
- lir = new(alloc()) LFallibleStoreElementT(object, elements, index, value, tempDef);
+ lir = new(alloc()) LFallibleStoreElementT(object, elements, index, value);
break;
}
diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h
index e36620bce..9342ef471 100644
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -107,7 +107,6 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitCall(MCall* call);
void visitApplyArgs(MApplyArgs* apply);
void visitApplyArray(MApplyArray* apply);
- void visitArraySplice(MArraySplice* splice);
void visitBail(MBail* bail);
void visitUnreachable(MUnreachable* unreachable);
void visitEncodeSnapshot(MEncodeSnapshot* ins);
@@ -216,10 +215,6 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitTypedObjectDescr(MTypedObjectDescr* ins);
void visitInitializedLength(MInitializedLength* ins);
void visitSetInitializedLength(MSetInitializedLength* ins);
- void visitUnboxedArrayLength(MUnboxedArrayLength* ins);
- void visitUnboxedArrayInitializedLength(MUnboxedArrayInitializedLength* ins);
- void visitIncrementUnboxedArrayInitializedLength(MIncrementUnboxedArrayInitializedLength* ins);
- void visitSetUnboxedArrayInitializedLength(MSetUnboxedArrayInitializedLength* ins);
void visitNot(MNot* ins);
void visitBoundsCheck(MBoundsCheck* ins);
void visitBoundsCheckLower(MBoundsCheckLower* ins);
diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp
index 41ccd0ca7..5eee30e49 100644
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -30,7 +30,6 @@
#include "jit/shared/Lowering-shared-inl.h"
#include "vm/NativeObject-inl.h"
#include "vm/StringObject-inl.h"
-#include "vm/UnboxedObject-inl.h"
using mozilla::ArrayLength;
using mozilla::AssertedCast;
@@ -86,8 +85,6 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
return inlineArrayPush(callInfo);
case InlinableNative::ArraySlice:
return inlineArraySlice(callInfo);
- case InlinableNative::ArraySplice:
- return inlineArraySplice(callInfo);
// Atomic natives.
case InlinableNative::AtomicsCompareExchange:
@@ -475,11 +472,6 @@ IonBuilder::inlineArray(CallInfo& callInfo)
return InliningStatus_NotInlined;
}
- if (templateObject->is<UnboxedArrayObject>()) {
- if (templateObject->group()->unboxedLayout().nativeGroup())
- return InliningStatus_NotInlined;
- }
-
// Multiple arguments imply array initialization, not just construction.
if (callInfo.argc() >= 2) {
initLength = callInfo.argc();
@@ -527,7 +519,7 @@ IonBuilder::inlineArray(CallInfo& callInfo)
// Make sure initLength matches the template object's length. This is
// not guaranteed to be the case, for instance if we're inlining the
// MConstant may come from an outer script.
- if (initLength != GetAnyBoxedOrUnboxedArrayLength(templateObject))
+ if (initLength != templateObject->as<ArrayObject>().length())
return InliningStatus_NotInlined;
// Don't inline large allocations.
@@ -542,16 +534,15 @@ IonBuilder::inlineArray(CallInfo& callInfo)
MDefinition* array = current->peek(-1);
if (callInfo.argc() >= 2) {
- JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject);
for (uint32_t i = 0; i < initLength; i++) {
if (!alloc().ensureBallast())
return InliningStatus_Error;
MDefinition* value = callInfo.getArg(i);
- if (!initializeArrayElement(array, i, value, unboxedType, /* addResumePoint = */ false))
+ if (!initializeArrayElement(array, i, value, /* addResumePoint = */ false))
return InliningStatus_Error;
}
- MInstruction* setLength = setInitializedLength(array, unboxedType, initLength);
+ MInstruction* setLength = setInitializedLength(array, initLength);
if (!resumeAfter(setLength))
return InliningStatus_Error;
}
@@ -584,7 +575,7 @@ IonBuilder::inlineArrayIsArray(CallInfo& callInfo)
if (!clasp || clasp->isProxy())
return InliningStatus_NotInlined;
- isArray = (clasp == &ArrayObject::class_ || clasp == &UnboxedArrayObject::class_);
+ isArray = (clasp == &ArrayObject::class_);
}
pushConstant(BooleanValue(isArray));
@@ -620,29 +611,22 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
if (!thisTypes)
return InliningStatus_NotInlined;
const Class* clasp = thisTypes->getKnownClass(constraints());
- if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
+ if (clasp != &ArrayObject::class_)
return InliningStatus_NotInlined;
if (thisTypes->hasObjectFlags(constraints(), unhandledFlags)) {
trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
return InliningStatus_NotInlined;
}
- if (ArrayPrototypeHasIndexedProperty(this, script())) {
+ // Watch out for extra indexed properties on the object or its prototype.
+ if (ElementAccessHasExtraIndexedProperty(this, obj)) {
trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
return InliningStatus_NotInlined;
}
- JSValueType unboxedType = JSVAL_TYPE_MAGIC;
- if (clasp == &UnboxedArrayObject::class_) {
- unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr);
- if (unboxedType == JSVAL_TYPE_MAGIC)
- return InliningStatus_NotInlined;
- }
-
callInfo.setImplicitlyUsedUnchecked();
- if (clasp == &ArrayObject::class_)
- obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
+ obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
TemporaryTypeSet* returnTypes = getInlineReturnTypeSet();
bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED);
@@ -653,8 +637,7 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
if (barrier != BarrierKind::NoBarrier)
returnType = MIRType::Value;
- MArrayPopShift* ins = MArrayPopShift::New(alloc(), obj, mode,
- unboxedType, needsHoleCheck, maybeUndefined);
+ MArrayPopShift* ins = MArrayPopShift::New(alloc(), obj, mode, needsHoleCheck, maybeUndefined);
current->add(ins);
current->push(ins);
ins->setResultType(returnType);
@@ -669,46 +652,6 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
}
IonBuilder::InliningStatus
-IonBuilder::inlineArraySplice(CallInfo& callInfo)
-{
- if (callInfo.argc() != 2 || callInfo.constructing()) {
- trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
- return InliningStatus_NotInlined;
- }
-
- // Ensure |this|, argument and result are objects.
- if (getInlineReturnType() != MIRType::Object)
- return InliningStatus_NotInlined;
- if (callInfo.thisArg()->type() != MIRType::Object)
- return InliningStatus_NotInlined;
- if (callInfo.getArg(0)->type() != MIRType::Int32)
- return InliningStatus_NotInlined;
- if (callInfo.getArg(1)->type() != MIRType::Int32)
- return InliningStatus_NotInlined;
-
- callInfo.setImplicitlyUsedUnchecked();
-
- // Specialize arr.splice(start, deleteCount) with unused return value and
- // avoid creating the result array in this case.
- if (!BytecodeIsPopped(pc)) {
- trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
- return InliningStatus_NotInlined;
- }
-
- MArraySplice* ins = MArraySplice::New(alloc(),
- callInfo.thisArg(),
- callInfo.getArg(0),
- callInfo.getArg(1));
-
- current->add(ins);
- pushConstant(UndefinedValue());
-
- if (!resumeAfter(ins))
- return InliningStatus_Error;
- return InliningStatus_Inlined;
-}
-
-IonBuilder::InliningStatus
IonBuilder::inlineArrayJoin(CallInfo& callInfo)
{
if (callInfo.argc() != 1 || callInfo.constructing()) {
@@ -761,16 +704,10 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo)
if (!thisTypes)
return InliningStatus_NotInlined;
const Class* clasp = thisTypes->getKnownClass(constraints());
- if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
+ if (clasp != &ArrayObject::class_)
return InliningStatus_NotInlined;
- if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
- OBJECT_FLAG_LENGTH_OVERFLOW))
- {
- trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
- return InliningStatus_NotInlined;
- }
- if (ArrayPrototypeHasIndexedProperty(this, script())) {
+ if (ElementAccessHasExtraIndexedProperty(this, obj)) {
trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
return InliningStatus_NotInlined;
}
@@ -782,13 +719,6 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo)
return InliningStatus_NotInlined;
}
- JSValueType unboxedType = JSVAL_TYPE_MAGIC;
- if (clasp == &UnboxedArrayObject::class_) {
- unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr);
- if (unboxedType == JSVAL_TYPE_MAGIC)
- return InliningStatus_NotInlined;
- }
-
callInfo.setImplicitlyUsedUnchecked();
if (conversion == TemporaryTypeSet::AlwaysConvertToDoubles ||
@@ -799,13 +729,12 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo)
value = valueDouble;
}
- if (unboxedType == JSVAL_TYPE_MAGIC)
- obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
+ obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
if (NeedsPostBarrier(value))
current->add(MPostWriteBarrier::New(alloc(), obj, value));
- MArrayPush* ins = MArrayPush::New(alloc(), obj, value, unboxedType);
+ MArrayPush* ins = MArrayPush::New(alloc(), obj, value);
current->add(ins);
current->push(ins);
@@ -846,24 +775,11 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo)
return InliningStatus_NotInlined;
const Class* clasp = thisTypes->getKnownClass(constraints());
- if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
- return InliningStatus_NotInlined;
- if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
- OBJECT_FLAG_LENGTH_OVERFLOW))
- {
- trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
+ if (clasp != &ArrayObject::class_)
return InliningStatus_NotInlined;
- }
-
- JSValueType unboxedType = JSVAL_TYPE_MAGIC;
- if (clasp == &UnboxedArrayObject::class_) {
- unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr);
- if (unboxedType == JSVAL_TYPE_MAGIC)
- return InliningStatus_NotInlined;
- }
- // Watch out for indexed properties on the prototype.
- if (ArrayPrototypeHasIndexedProperty(this, script())) {
+ // Watch out for indexed properties on the object or its prototype.
+ if (ElementAccessHasExtraIndexedProperty(this, obj)) {
trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
return InliningStatus_NotInlined;
}
@@ -882,15 +798,8 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo)
if (!templateObj)
return InliningStatus_NotInlined;
- if (unboxedType == JSVAL_TYPE_MAGIC) {
- if (!templateObj->is<ArrayObject>())
- return InliningStatus_NotInlined;
- } else {
- if (!templateObj->is<UnboxedArrayObject>())
- return InliningStatus_NotInlined;
- if (templateObj->as<UnboxedArrayObject>().elementType() != unboxedType)
- return InliningStatus_NotInlined;
- }
+ if (!templateObj->is<ArrayObject>())
+ return InliningStatus_NotInlined;
callInfo.setImplicitlyUsedUnchecked();
@@ -909,16 +818,12 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo)
end = MArrayLength::New(alloc(), elements);
current->add(end->toInstruction());
- } else {
- end = MUnboxedArrayLength::New(alloc(), obj);
- current->add(end->toInstruction());
}
MArraySlice* ins = MArraySlice::New(alloc(), constraints(),
obj, begin, end,
templateObj,
- templateObj->group()->initialHeap(constraints()),
- unboxedType);
+ templateObj->group()->initialHeap(constraints()));
current->add(ins);
current->push(ins);
@@ -1437,7 +1342,7 @@ IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo)
// Check if exist a template object in stub.
JSString* stringStr = nullptr;
JSString* stringSep = nullptr;
- JSObject* templateObject = nullptr;
+ ArrayObject* templateObject = nullptr;
if (!inspector->isOptimizableCallStringSplit(pc, &stringStr, &stringSep, &templateObject))
return InliningStatus_NotInlined;
@@ -1463,13 +1368,13 @@ IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo)
if (!key.maybeTypes()->hasType(TypeSet::StringType()))
return InliningStatus_NotInlined;
- uint32_t initLength = GetAnyBoxedOrUnboxedArrayLength(templateObject);
- if (GetAnyBoxedOrUnboxedInitializedLength(templateObject) != initLength)
+ uint32_t initLength = templateObject->length();
+ if (templateObject->getDenseInitializedLength() != initLength)
return InliningStatus_NotInlined;
Vector<MConstant*, 0, SystemAllocPolicy> arrayValues;
for (uint32_t i = 0; i < initLength; i++) {
- Value str = GetAnyBoxedOrUnboxedDenseElement(templateObject, i);
+ Value str = templateObject->getDenseElement(i);
MOZ_ASSERT(str.toString()->isAtom());
MConstant* value = MConstant::New(alloc().fallible(), str, constraints());
if (!value)
@@ -1500,8 +1405,6 @@ IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo)
return InliningStatus_Inlined;
}
- JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject);
-
// Store all values, no need to initialize the length after each as
// jsop_initelem_array is doing because we do not expect to bailout
// because the memory is supposed to be allocated by now.
@@ -1512,11 +1415,11 @@ IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo)
MConstant* value = arrayValues[i];
current->add(value);
- if (!initializeArrayElement(array, i, value, unboxedType, /* addResumePoint = */ false))
+ if (!initializeArrayElement(array, i, value, /* addResumePoint = */ false))
return InliningStatus_Error;
}
- MInstruction* setLength = setInitializedLength(array, unboxedType, initLength);
+ MInstruction* setLength = setInitializedLength(array, initLength);
if (!resumeAfter(setLength))
return InliningStatus_Error;
diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp
index 1f33b2174..0cf31adb3 100644
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1970,7 +1970,7 @@ WrappedFunction::WrappedFunction(JSFunction* fun)
MCall*
MCall::New(TempAllocator& alloc, JSFunction* target, size_t maxArgc, size_t numActualArgs,
- bool construct, bool isDOMCall)
+ bool construct, bool ignoresReturnValue, bool isDOMCall)
{
WrappedFunction* wrappedTarget = target ? new(alloc) WrappedFunction(target) : nullptr;
MOZ_ASSERT(maxArgc >= numActualArgs);
@@ -1979,7 +1979,7 @@ MCall::New(TempAllocator& alloc, JSFunction* target, size_t maxArgc, size_t numA
MOZ_ASSERT(!construct);
ins = new(alloc) MCallDOMNative(wrappedTarget, numActualArgs);
} else {
- ins = new(alloc) MCall(wrappedTarget, numActualArgs, construct);
+ ins = new(alloc) MCall(wrappedTarget, numActualArgs, construct, ignoresReturnValue);
}
if (!ins->init(alloc, maxArgc + NumNonArgumentOperands))
return nullptr;
@@ -2630,40 +2630,6 @@ jit::EqualTypes(MIRType type1, TemporaryTypeSet* typeset1,
return typeset1->equals(typeset2);
}
-// Tests whether input/inputTypes can always be stored to an unboxed
-// object/array property with the given unboxed type.
-bool
-jit::CanStoreUnboxedType(TempAllocator& alloc,
- JSValueType unboxedType, MIRType input, TypeSet* inputTypes)
-{
- TemporaryTypeSet types;
-
- switch (unboxedType) {
- case JSVAL_TYPE_BOOLEAN:
- case JSVAL_TYPE_INT32:
- case JSVAL_TYPE_DOUBLE:
- case JSVAL_TYPE_STRING:
- types.addType(TypeSet::PrimitiveType(unboxedType), alloc.lifoAlloc());
- break;
-
- case JSVAL_TYPE_OBJECT:
- types.addType(TypeSet::AnyObjectType(), alloc.lifoAlloc());
- types.addType(TypeSet::NullType(), alloc.lifoAlloc());
- break;
-
- default:
- MOZ_CRASH("Bad unboxed type");
- }
-
- return TypeSetIncludes(&types, input, inputTypes);
-}
-
-static bool
-CanStoreUnboxedType(TempAllocator& alloc, JSValueType unboxedType, MDefinition* value)
-{
- return CanStoreUnboxedType(alloc, unboxedType, value->type(), value->resultTypeSet());
-}
-
bool
MPhi::specializeType(TempAllocator& alloc)
{
@@ -4817,15 +4783,14 @@ MCreateThisWithTemplate::canRecoverOnBailout() const
MObjectState::MObjectState(MObjectState* state)
: numSlots_(state->numSlots_),
- numFixedSlots_(state->numFixedSlots_),
- operandIndex_(state->operandIndex_)
+ numFixedSlots_(state->numFixedSlots_)
{
// This instruction is only used as a summary for bailout paths.
setResultType(MIRType::Object);
setRecoveredOnBailout();
}
-MObjectState::MObjectState(JSObject *templateObject, OperandIndexMap* operandIndex)
+MObjectState::MObjectState(JSObject* templateObject)
{
// This instruction is only used as a summary for bailout paths.
setResultType(MIRType::Object);
@@ -4836,8 +4801,6 @@ MObjectState::MObjectState(JSObject *templateObject, OperandIndexMap* operandInd
NativeObject* nativeObject = &templateObject->as<NativeObject>();
numSlots_ = nativeObject->slotSpan();
numFixedSlots_ = nativeObject->numFixedSlots();
-
- operandIndex_ = operandIndex;
}
JSObject*
@@ -4897,7 +4860,7 @@ MObjectState::New(TempAllocator& alloc, MDefinition* obj)
JSObject* templateObject = templateObjectOf(obj);
MOZ_ASSERT(templateObject, "Unexpected object creation.");
- MObjectState* res = new(alloc) MObjectState(templateObject, nullptr);
+ MObjectState* res = new(alloc) MObjectState(templateObject);
if (!res || !res->init(alloc, obj))
return nullptr;
return res;
@@ -5823,46 +5786,6 @@ jit::ElementAccessIsDenseNative(CompilerConstraintList* constraints,
return clasp && clasp->isNative() && !IsTypedArrayClass(clasp);
}
-JSValueType
-jit::UnboxedArrayElementType(CompilerConstraintList* constraints, MDefinition* obj,
- MDefinition* id)
-{
- if (obj->mightBeType(MIRType::String))
- return JSVAL_TYPE_MAGIC;
-
- if (id && id->type() != MIRType::Int32 && id->type() != MIRType::Double)
- return JSVAL_TYPE_MAGIC;
-
- TemporaryTypeSet* types = obj->resultTypeSet();
- if (!types || types->unknownObject())
- return JSVAL_TYPE_MAGIC;
-
- JSValueType elementType = JSVAL_TYPE_MAGIC;
- for (unsigned i = 0; i < types->getObjectCount(); i++) {
- TypeSet::ObjectKey* key = types->getObject(i);
- if (!key)
- continue;
-
- if (key->unknownProperties() || !key->isGroup())
- return JSVAL_TYPE_MAGIC;
-
- if (key->clasp() != &UnboxedArrayObject::class_)
- return JSVAL_TYPE_MAGIC;
-
- const UnboxedLayout &layout = key->group()->unboxedLayout();
-
- if (layout.nativeGroup())
- return JSVAL_TYPE_MAGIC;
-
- if (elementType == layout.elementType() || elementType == JSVAL_TYPE_MAGIC)
- elementType = layout.elementType();
- else
- return JSVAL_TYPE_MAGIC;
- }
-
- return elementType;
-}
-
bool
jit::ElementAccessIsTypedArray(CompilerConstraintList* constraints,
MDefinition* obj, MDefinition* id,
@@ -6022,11 +5945,6 @@ ObjectSubsumes(TypeSet::ObjectKey* first, TypeSet::ObjectKey* second)
firstElements.maybeTypes()->equals(secondElements.maybeTypes());
}
- if (first->clasp() == &UnboxedArrayObject::class_) {
- return first->group()->unboxedLayout().elementType() ==
- second->group()->unboxedLayout().elementType();
- }
-
return false;
}
@@ -6266,15 +6184,6 @@ PrototypeHasIndexedProperty(IonBuilder* builder, JSObject* obj)
return false;
}
-// Whether Array.prototype, or an object on its proto chain, has an indexed property.
-bool
-jit::ArrayPrototypeHasIndexedProperty(IonBuilder* builder, JSScript* script)
-{
- if (JSObject* proto = script->global().maybeGetArrayPrototype())
- return PrototypeHasIndexedProperty(builder, proto);
- return true;
-}
-
// Whether obj or any of its prototypes have an indexed property.
bool
jit::TypeCanHaveExtraIndexedProperties(IonBuilder* builder, TemporaryTypeSet* types)
diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h
index 3e0421789..6c376d528 100644
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -375,8 +375,7 @@ class AliasSet {
Element = 1 << 1, // A Value member of obj->elements or
// a typed object.
UnboxedElement = 1 << 2, // An unboxed scalar or reference member of
- // a typed array, typed object, or unboxed
- // object.
+ // typed object.
DynamicSlot = 1 << 3, // A Value member of obj->slots.
FixedSlot = 1 << 4, // A Value member of obj->fixedSlots().
DOMProperty = 1 << 5, // A DOM property
@@ -433,9 +432,6 @@ class AliasSet {
MOZ_ASSERT(flags && !(flags & Store_));
return AliasSet(flags | Store_);
}
- static uint32_t BoxedOrUnboxedElements(JSValueType type) {
- return (type == JSVAL_TYPE_MAGIC) ? Element : UnboxedElement;
- }
};
typedef Vector<MDefinition*, 6, JitAllocPolicy> MDefinitionVector;
@@ -3762,14 +3758,9 @@ class MObjectState
{
private:
uint32_t numSlots_;
- uint32_t numFixedSlots_; // valid if isUnboxed() == false.
- OperandIndexMap* operandIndex_; // valid if isUnboxed() == true.
-
- bool isUnboxed() const {
- return operandIndex_ != nullptr;
- }
+ uint32_t numFixedSlots_;
- MObjectState(JSObject *templateObject, OperandIndexMap* operandIndex);
+ MObjectState(JSObject *templateObject);
explicit MObjectState(MObjectState* state);
MOZ_MUST_USE bool init(TempAllocator& alloc, MDefinition* obj);
@@ -3794,7 +3785,6 @@ class MObjectState
MOZ_MUST_USE bool initFromTemplateObject(TempAllocator& alloc, MDefinition* undefinedVal);
size_t numFixedSlots() const {
- MOZ_ASSERT(!isUnboxed());
return numFixedSlots_;
}
size_t numSlots() const {
@@ -3830,18 +3820,6 @@ class MObjectState
setSlot(slot + numFixedSlots(), def);
}
- // Interface reserved for unboxed objects.
- bool hasOffset(uint32_t offset) const {
- MOZ_ASSERT(isUnboxed());
- return offset < operandIndex_->map.length() && operandIndex_->map[offset] != 0;
- }
- MDefinition* getOffset(uint32_t offset) const {
- return getOperand(operandIndex_->map[offset]);
- }
- void setOffset(uint32_t offset, MDefinition* def) {
- replaceOperand(operandIndex_->map[offset], def);
- }
-
MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
bool canRecoverOnBailout() const override {
return true;
@@ -4065,14 +4043,18 @@ class MCall
uint32_t numActualArgs_;
// True if the call is for JSOP_NEW.
- bool construct_;
+ bool construct_:1;
+
+ // True if the caller does not use the return value.
+ bool ignoresReturnValue_:1;
- bool needsArgCheck_;
+ bool needsArgCheck_:1;
- MCall(WrappedFunction* target, uint32_t numActualArgs, bool construct)
+ MCall(WrappedFunction* target, uint32_t numActualArgs, bool construct, bool ignoresReturnValue)
: target_(target),
numActualArgs_(numActualArgs),
construct_(construct),
+ ignoresReturnValue_(ignoresReturnValue),
needsArgCheck_(true)
{
setResultType(MIRType::Value);
@@ -4081,7 +4063,7 @@ class MCall
public:
INSTRUCTION_HEADER(Call)
static MCall* New(TempAllocator& alloc, JSFunction* target, size_t maxArgc, size_t numActualArgs,
- bool construct, bool isDOMCall);
+ bool construct, bool ignoresReturnValue, bool isDOMCall);
void initFunction(MDefinition* func) {
initOperand(FunctionOperandIndex, func);
@@ -4126,6 +4108,10 @@ class MCall
return construct_;
}
+ bool ignoresReturnValue() const {
+ return ignoresReturnValue_;
+ }
+
// The number of stack arguments is the max between the number of formal
// arguments and the number of actual arguments. The number of stack
// argument includes the |undefined| padding added in case of underflow.
@@ -4169,7 +4155,7 @@ class MCallDOMNative : public MCall
// virtual things from MCall.
protected:
MCallDOMNative(WrappedFunction* target, uint32_t numActualArgs)
- : MCall(target, numActualArgs, false)
+ : MCall(target, numActualArgs, false, false)
{
MOZ_ASSERT(getJitInfo()->type() != JSJitInfo::InlinableNative);
@@ -4184,7 +4170,8 @@ class MCallDOMNative : public MCall
}
friend MCall* MCall::New(TempAllocator& alloc, JSFunction* target, size_t maxArgc,
- size_t numActualArgs, bool construct, bool isDOMCall);
+ size_t numActualArgs, bool construct, bool ignoresReturnValue,
+ bool isDOMCall);
const JSJitInfo* getJitInfo() const;
public:
@@ -4199,27 +4186,6 @@ class MCallDOMNative : public MCall
virtual void computeMovable() override;
};
-// arr.splice(start, deleteCount) with unused return value.
-class MArraySplice
- : public MTernaryInstruction,
- public Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2> >::Data
-{
- private:
-
- MArraySplice(MDefinition* object, MDefinition* start, MDefinition* deleteCount)
- : MTernaryInstruction(object, start, deleteCount)
- { }
-
- public:
- INSTRUCTION_HEADER(ArraySplice)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object), (1, start), (2, deleteCount))
-
- bool possiblyCalls() const override {
- return true;
- }
-};
-
// fun.apply(self, arguments)
class MApplyArgs
: public MAryInstruction<3>,
@@ -8745,102 +8711,6 @@ class MSetInitializedLength
ALLOW_CLONE(MSetInitializedLength)
};
-// Load the length from an unboxed array.
-class MUnboxedArrayLength
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MUnboxedArrayLength(MDefinition* object)
- : MUnaryInstruction(object)
- {
- setResultType(MIRType::Int32);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(UnboxedArrayLength)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::ObjectFields);
- }
-
- ALLOW_CLONE(MUnboxedArrayLength)
-};
-
-// Load the initialized length from an unboxed array.
-class MUnboxedArrayInitializedLength
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MUnboxedArrayInitializedLength(MDefinition* object)
- : MUnaryInstruction(object)
- {
- setResultType(MIRType::Int32);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(UnboxedArrayInitializedLength)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- bool congruentTo(const MDefinition* ins) const override {
- return congruentIfOperandsEqual(ins);
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::ObjectFields);
- }
-
- ALLOW_CLONE(MUnboxedArrayInitializedLength)
-};
-
-// Increment the initialized length of an unboxed array object.
-class MIncrementUnboxedArrayInitializedLength
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MIncrementUnboxedArrayInitializedLength(MDefinition* obj)
- : MUnaryInstruction(obj)
- {}
-
- public:
- INSTRUCTION_HEADER(IncrementUnboxedArrayInitializedLength)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::ObjectFields);
- }
-
- ALLOW_CLONE(MIncrementUnboxedArrayInitializedLength)
-};
-
-// Set the initialized length of an unboxed array object.
-class MSetUnboxedArrayInitializedLength
- : public MBinaryInstruction,
- public SingleObjectPolicy::Data
-{
- explicit MSetUnboxedArrayInitializedLength(MDefinition* obj, MDefinition* length)
- : MBinaryInstruction(obj, length)
- {}
-
- public:
- INSTRUCTION_HEADER(SetUnboxedArrayInitializedLength)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object), (1, length))
-
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::ObjectFields);
- }
-
- ALLOW_CLONE(MSetUnboxedArrayInitializedLength)
-};
-
// Load the array length from an elements header.
class MArrayLength
: public MUnaryInstruction,
@@ -9334,23 +9204,19 @@ class MLoadElement
ALLOW_CLONE(MLoadElement)
};
-// Load a value from the elements vector for a dense native or unboxed array.
+// Load a value from the elements vector of a native object.
// If the index is out-of-bounds, or the indexed slot has a hole, undefined is
// returned instead.
class MLoadElementHole
: public MTernaryInstruction,
public SingleObjectPolicy::Data
{
- // Unboxed element type, JSVAL_TYPE_MAGIC for dense native elements.
- JSValueType unboxedType_;
-
bool needsNegativeIntCheck_;
bool needsHoleCheck_;
MLoadElementHole(MDefinition* elements, MDefinition* index, MDefinition* initLength,
- JSValueType unboxedType, bool needsHoleCheck)
+ bool needsHoleCheck)
: MTernaryInstruction(elements, index, initLength),
- unboxedType_(unboxedType),
needsNegativeIntCheck_(true),
needsHoleCheck_(needsHoleCheck)
{
@@ -9372,9 +9238,6 @@ class MLoadElementHole
TRIVIAL_NEW_WRAPPERS
NAMED_OPERANDS((0, elements), (1, index), (2, initLength))
- JSValueType unboxedType() const {
- return unboxedType_;
- }
bool needsNegativeIntCheck() const {
return needsNegativeIntCheck_;
}
@@ -9385,8 +9248,6 @@ class MLoadElementHole
if (!ins->isLoadElementHole())
return false;
const MLoadElementHole* other = ins->toLoadElementHole();
- if (unboxedType() != other->unboxedType())
- return false;
if (needsHoleCheck() != other->needsHoleCheck())
return false;
if (needsNegativeIntCheck() != other->needsNegativeIntCheck())
@@ -9394,7 +9255,7 @@ class MLoadElementHole
return congruentIfOperandsEqual(other);
}
AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::BoxedOrUnboxedElements(unboxedType()));
+ return AliasSet::Load(AliasSet::Element);
}
void collectRangeInfoPreTrunc() override;
@@ -9574,20 +9435,17 @@ class MStoreElement
ALLOW_CLONE(MStoreElement)
};
-// Like MStoreElement, but supports indexes >= initialized length, and can
-// handle unboxed arrays. The downside is that we cannot hoist the elements
-// vector and bounds check, since this instruction may update the (initialized)
-// length and reallocate the elements vector.
+// Like MStoreElement, but supports indexes >= initialized length. The downside
+// is that we cannot hoist the elements vector and bounds check, since this
+// instruction may update the (initialized) length and reallocate the elements
+// vector.
class MStoreElementHole
: public MAryInstruction<4>,
public MStoreElementCommon,
public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3> >::Data
{
- JSValueType unboxedType_;
-
MStoreElementHole(MDefinition* object, MDefinition* elements,
- MDefinition* index, MDefinition* value, JSValueType unboxedType)
- : unboxedType_(unboxedType)
+ MDefinition* index, MDefinition* value)
{
initOperand(0, object);
initOperand(1, elements);
@@ -9602,14 +9460,10 @@ class MStoreElementHole
TRIVIAL_NEW_WRAPPERS
NAMED_OPERANDS((0, object), (1, elements), (2, index), (3, value))
- JSValueType unboxedType() const {
- return unboxedType_;
- }
AliasSet getAliasSet() const override {
// StoreElementHole can update the initialized length, the array length
// or reallocate obj->elements.
- return AliasSet::Store(AliasSet::ObjectFields |
- AliasSet::BoxedOrUnboxedElements(unboxedType()));
+ return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element);
}
ALLOW_CLONE(MStoreElementHole)
@@ -9622,13 +9476,11 @@ class MFallibleStoreElement
public MStoreElementCommon,
public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3> >::Data
{
- JSValueType unboxedType_;
bool strict_;
MFallibleStoreElement(MDefinition* object, MDefinition* elements,
MDefinition* index, MDefinition* value,
- JSValueType unboxedType, bool strict)
- : unboxedType_(unboxedType)
+ bool strict)
{
initOperand(0, object);
initOperand(1, elements);
@@ -9644,12 +9496,8 @@ class MFallibleStoreElement
TRIVIAL_NEW_WRAPPERS
NAMED_OPERANDS((0, object), (1, elements), (2, index), (3, value))
- JSValueType unboxedType() const {
- return unboxedType_;
- }
AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::ObjectFields |
- AliasSet::BoxedOrUnboxedElements(unboxedType()));
+ return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element);
}
bool strict() const {
return strict_;
@@ -9754,13 +9602,12 @@ class MArrayPopShift
private:
Mode mode_;
- JSValueType unboxedType_;
bool needsHoleCheck_;
bool maybeUndefined_;
- MArrayPopShift(MDefinition* object, Mode mode, JSValueType unboxedType,
+ MArrayPopShift(MDefinition* object, Mode mode,
bool needsHoleCheck, bool maybeUndefined)
- : MUnaryInstruction(object), mode_(mode), unboxedType_(unboxedType),
+ : MUnaryInstruction(object), mode_(mode),
needsHoleCheck_(needsHoleCheck), maybeUndefined_(maybeUndefined)
{ }
@@ -9778,12 +9625,8 @@ class MArrayPopShift
bool mode() const {
return mode_;
}
- JSValueType unboxedType() const {
- return unboxedType_;
- }
AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::ObjectFields |
- AliasSet::BoxedOrUnboxedElements(unboxedType()));
+ return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element);
}
ALLOW_CLONE(MArrayPopShift)
@@ -9794,10 +9637,8 @@ class MArrayPush
: public MBinaryInstruction,
public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >::Data
{
- JSValueType unboxedType_;
-
- MArrayPush(MDefinition* object, MDefinition* value, JSValueType unboxedType)
- : MBinaryInstruction(object, value), unboxedType_(unboxedType)
+ MArrayPush(MDefinition* object, MDefinition* value)
+ : MBinaryInstruction(object, value)
{
setResultType(MIRType::Int32);
}
@@ -9807,12 +9648,8 @@ class MArrayPush
TRIVIAL_NEW_WRAPPERS
NAMED_OPERANDS((0, object), (1, value))
- JSValueType unboxedType() const {
- return unboxedType_;
- }
AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::ObjectFields |
- AliasSet::BoxedOrUnboxedElements(unboxedType()));
+ return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element);
}
void computeRange(TempAllocator& alloc) override;
@@ -9826,15 +9663,13 @@ class MArraySlice
{
CompilerObject templateObj_;
gc::InitialHeap initialHeap_;
- JSValueType unboxedType_;
MArraySlice(CompilerConstraintList* constraints, MDefinition* obj,
MDefinition* begin, MDefinition* end,
- JSObject* templateObj, gc::InitialHeap initialHeap, JSValueType unboxedType)
+ JSObject* templateObj, gc::InitialHeap initialHeap)
: MTernaryInstruction(obj, begin, end),
templateObj_(templateObj),
- initialHeap_(initialHeap),
- unboxedType_(unboxedType)
+ initialHeap_(initialHeap)
{
setResultType(MIRType::Object);
}
@@ -9852,10 +9687,6 @@ class MArraySlice
return initialHeap_;
}
- JSValueType unboxedType() const {
- return unboxedType_;
- }
-
bool possiblyCalls() const override {
return true;
}
@@ -12224,15 +12055,13 @@ class MInArray
{
bool needsHoleCheck_;
bool needsNegativeIntCheck_;
- JSValueType unboxedType_;
MInArray(MDefinition* elements, MDefinition* index,
MDefinition* initLength, MDefinition* object,
- bool needsHoleCheck, JSValueType unboxedType)
+ bool needsHoleCheck)
: MQuaternaryInstruction(elements, index, initLength, object),
needsHoleCheck_(needsHoleCheck),
- needsNegativeIntCheck_(true),
- unboxedType_(unboxedType)
+ needsNegativeIntCheck_(true)
{
setResultType(MIRType::Boolean);
setMovable();
@@ -12252,9 +12081,6 @@ class MInArray
bool needsNegativeIntCheck() const {
return needsNegativeIntCheck_;
}
- JSValueType unboxedType() const {
- return unboxedType_;
- }
void collectRangeInfoPreTrunc() override;
AliasSet getAliasSet() const override {
return AliasSet::Load(AliasSet::Element);
@@ -12267,8 +12093,6 @@ class MInArray
return false;
if (needsNegativeIntCheck() != other->needsNegativeIntCheck())
return false;
- if (unboxedType() != other->unboxedType())
- return false;
return congruentIfOperandsEqual(other);
}
};
@@ -14169,8 +13993,6 @@ MDefinition::maybeConstantValue()
bool ElementAccessIsDenseNative(CompilerConstraintList* constraints,
MDefinition* obj, MDefinition* id);
-JSValueType UnboxedArrayElementType(CompilerConstraintList* constraints, MDefinition* obj,
- MDefinition* id);
bool ElementAccessIsTypedArray(CompilerConstraintList* constraints,
MDefinition* obj, MDefinition* id,
Scalar::Type* arrayType);
@@ -14202,7 +14024,6 @@ bool PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList*
MBasicBlock* current, MDefinition** pobj,
PropertyName* name, MDefinition** pvalue,
bool canModify, MIRType implicitType = MIRType::None);
-bool ArrayPrototypeHasIndexedProperty(IonBuilder* builder, JSScript* script);
bool TypeCanHaveExtraIndexedProperties(IonBuilder* builder, TemporaryTypeSet* types);
inline MIRType
diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h
index b80d1baf9..9e460a2ba 100644
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -69,7 +69,6 @@ namespace jit {
_(Call) \
_(ApplyArgs) \
_(ApplyArray) \
- _(ArraySplice) \
_(Bail) \
_(Unreachable) \
_(EncodeSnapshot) \
@@ -198,10 +197,6 @@ namespace jit {
_(SetTypedObjectOffset) \
_(InitializedLength) \
_(SetInitializedLength) \
- _(UnboxedArrayLength) \
- _(UnboxedArrayInitializedLength) \
- _(IncrementUnboxedArrayInitializedLength) \
- _(SetUnboxedArrayInitializedLength) \
_(Not) \
_(BoundsCheck) \
_(BoundsCheckLower) \
diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp
index f633b9b7b..a739b9325 100644
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -126,20 +126,14 @@ MacroAssembler::guardTypeSetMightBeIncomplete(TypeSet* types, Register obj, Regi
{
// Type set guards might miss when an object's group changes. In this case
// either its old group's properties will become unknown, or it will change
- // to a native object with an original unboxed group. Jump to label if this
- // might have happened for the input object.
+ // to a native object. Jump to label if this might have happened for the
+ // input object.
if (types->unknownObject()) {
jump(label);
return;
}
- loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch);
- load32(Address(scratch, ObjectGroup::offsetOfFlags()), scratch);
- and32(Imm32(OBJECT_FLAG_ADDENDUM_MASK), scratch);
- branch32(Assembler::Equal,
- scratch, Imm32(ObjectGroup::addendumOriginalUnboxedGroupValue()), label);
-
for (size_t i = 0; i < types->getObjectCount(); i++) {
if (JSObject* singleton = types->getSingletonNoBarrier(i)) {
movePtr(ImmGCPtr(singleton), scratch);
@@ -468,268 +462,6 @@ template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const A
template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const BaseIndex& src, const ValueOperand& dest,
bool allowDouble, Register temp, Label* fail);
-template <typename T>
-void
-MacroAssembler::loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output)
-{
- switch (type) {
- case JSVAL_TYPE_INT32: {
- // Handle loading an int32 into a double reg.
- if (output.type() == MIRType::Double) {
- convertInt32ToDouble(address, output.typedReg().fpu());
- break;
- }
- MOZ_FALLTHROUGH;
- }
-
- case JSVAL_TYPE_BOOLEAN:
- case JSVAL_TYPE_STRING: {
- Register outReg;
- if (output.hasValue()) {
- outReg = output.valueReg().scratchReg();
- } else {
- MOZ_ASSERT(output.type() == MIRTypeFromValueType(type));
- outReg = output.typedReg().gpr();
- }
-
- switch (type) {
- case JSVAL_TYPE_BOOLEAN:
- load8ZeroExtend(address, outReg);
- break;
- case JSVAL_TYPE_INT32:
- load32(address, outReg);
- break;
- case JSVAL_TYPE_STRING:
- loadPtr(address, outReg);
- break;
- default:
- MOZ_CRASH();
- }
-
- if (output.hasValue())
- tagValue(type, outReg, output.valueReg());
- break;
- }
-
- case JSVAL_TYPE_OBJECT:
- if (output.hasValue()) {
- Register scratch = output.valueReg().scratchReg();
- loadPtr(address, scratch);
-
- Label notNull, done;
- branchPtr(Assembler::NotEqual, scratch, ImmWord(0), &notNull);
-
- moveValue(NullValue(), output.valueReg());
- jump(&done);
-
- bind(&notNull);
- tagValue(JSVAL_TYPE_OBJECT, scratch, output.valueReg());
-
- bind(&done);
- } else {
- // Reading null can't be possible here, as otherwise the result
- // would be a value (either because null has been read before or
- // because there is a barrier).
- Register reg = output.typedReg().gpr();
- loadPtr(address, reg);
-#ifdef DEBUG
- Label ok;
- branchTestPtr(Assembler::NonZero, reg, reg, &ok);
- assumeUnreachable("Null not possible");
- bind(&ok);
-#endif
- }
- break;
-
- case JSVAL_TYPE_DOUBLE:
- // Note: doubles in unboxed objects are not accessed through other
- // views and do not need canonicalization.
- if (output.hasValue())
- loadValue(address, output.valueReg());
- else
- loadDouble(address, output.typedReg().fpu());
- break;
-
- default:
- MOZ_CRASH();
- }
-}
-
-template void
-MacroAssembler::loadUnboxedProperty(Address address, JSValueType type,
- TypedOrValueRegister output);
-
-template void
-MacroAssembler::loadUnboxedProperty(BaseIndex address, JSValueType type,
- TypedOrValueRegister output);
-
-static void
-StoreUnboxedFailure(MacroAssembler& masm, Label* failure)
-{
- // Storing a value to an unboxed property is a fallible operation and
- // the caller must provide a failure label if a particular unboxed store
- // might fail. Sometimes, however, a store that cannot succeed (such as
- // storing a string to an int32 property) will be marked as infallible.
- // This can only happen if the code involved is unreachable.
- if (failure)
- masm.jump(failure);
- else
- masm.assumeUnreachable("Incompatible write to unboxed property");
-}
-
-template <typename T>
-void
-MacroAssembler::storeUnboxedProperty(T address, JSValueType type,
- const ConstantOrRegister& value, Label* failure)
-{
- switch (type) {
- case JSVAL_TYPE_BOOLEAN:
- if (value.constant()) {
- if (value.value().isBoolean())
- store8(Imm32(value.value().toBoolean()), address);
- else
- StoreUnboxedFailure(*this, failure);
- } else if (value.reg().hasTyped()) {
- if (value.reg().type() == MIRType::Boolean)
- store8(value.reg().typedReg().gpr(), address);
- else
- StoreUnboxedFailure(*this, failure);
- } else {
- if (failure)
- branchTestBoolean(Assembler::NotEqual, value.reg().valueReg(), failure);
- storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ 1);
- }
- break;
-
- case JSVAL_TYPE_INT32:
- if (value.constant()) {
- if (value.value().isInt32())
- store32(Imm32(value.value().toInt32()), address);
- else
- StoreUnboxedFailure(*this, failure);
- } else if (value.reg().hasTyped()) {
- if (value.reg().type() == MIRType::Int32)
- store32(value.reg().typedReg().gpr(), address);
- else
- StoreUnboxedFailure(*this, failure);
- } else {
- if (failure)
- branchTestInt32(Assembler::NotEqual, value.reg().valueReg(), failure);
- storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ 4);
- }
- break;
-
- case JSVAL_TYPE_DOUBLE:
- if (value.constant()) {
- if (value.value().isNumber()) {
- loadConstantDouble(value.value().toNumber(), ScratchDoubleReg);
- storeDouble(ScratchDoubleReg, address);
- } else {
- StoreUnboxedFailure(*this, failure);
- }
- } else if (value.reg().hasTyped()) {
- if (value.reg().type() == MIRType::Int32) {
- convertInt32ToDouble(value.reg().typedReg().gpr(), ScratchDoubleReg);
- storeDouble(ScratchDoubleReg, address);
- } else if (value.reg().type() == MIRType::Double) {
- storeDouble(value.reg().typedReg().fpu(), address);
- } else {
- StoreUnboxedFailure(*this, failure);
- }
- } else {
- ValueOperand reg = value.reg().valueReg();
- Label notInt32, end;
- branchTestInt32(Assembler::NotEqual, reg, &notInt32);
- int32ValueToDouble(reg, ScratchDoubleReg);
- storeDouble(ScratchDoubleReg, address);
- jump(&end);
- bind(&notInt32);
- if (failure)
- branchTestDouble(Assembler::NotEqual, reg, failure);
- storeValue(reg, address);
- bind(&end);
- }
- break;
-
- case JSVAL_TYPE_OBJECT:
- if (value.constant()) {
- if (value.value().isObjectOrNull())
- storePtr(ImmGCPtr(value.value().toObjectOrNull()), address);
- else
- StoreUnboxedFailure(*this, failure);
- } else if (value.reg().hasTyped()) {
- MOZ_ASSERT(value.reg().type() != MIRType::Null);
- if (value.reg().type() == MIRType::Object)
- storePtr(value.reg().typedReg().gpr(), address);
- else
- StoreUnboxedFailure(*this, failure);
- } else {
- if (failure) {
- Label ok;
- branchTestNull(Assembler::Equal, value.reg().valueReg(), &ok);
- branchTestObject(Assembler::NotEqual, value.reg().valueReg(), failure);
- bind(&ok);
- }
- storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ sizeof(uintptr_t));
- }
- break;
-
- case JSVAL_TYPE_STRING:
- if (value.constant()) {
- if (value.value().isString())
- storePtr(ImmGCPtr(value.value().toString()), address);
- else
- StoreUnboxedFailure(*this, failure);
- } else if (value.reg().hasTyped()) {
- if (value.reg().type() == MIRType::String)
- storePtr(value.reg().typedReg().gpr(), address);
- else
- StoreUnboxedFailure(*this, failure);
- } else {
- if (failure)
- branchTestString(Assembler::NotEqual, value.reg().valueReg(), failure);
- storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ sizeof(uintptr_t));
- }
- break;
-
- default:
- MOZ_CRASH();
- }
-}
-
-template void
-MacroAssembler::storeUnboxedProperty(Address address, JSValueType type,
- const ConstantOrRegister& value, Label* failure);
-
-template void
-MacroAssembler::storeUnboxedProperty(BaseIndex address, JSValueType type,
- const ConstantOrRegister& value, Label* failure);
-
-void
-MacroAssembler::checkUnboxedArrayCapacity(Register obj, const RegisterOrInt32Constant& index,
- Register temp, Label* failure)
-{
- Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
- Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength());
-
- Label capacityIsIndex, done;
- load32(initLengthAddr, temp);
- branchTest32(Assembler::NonZero, temp, Imm32(UnboxedArrayObject::CapacityMask), &capacityIsIndex);
- branch32(Assembler::BelowOrEqual, lengthAddr, index, failure);
- jump(&done);
- bind(&capacityIsIndex);
-
- // Do a partial shift so that we can get an absolute offset from the base
- // of CapacityArray to use.
- JS_STATIC_ASSERT(sizeof(UnboxedArrayObject::CapacityArray[0]) == 4);
- rshiftPtr(Imm32(UnboxedArrayObject::CapacityShift - 2), temp);
- and32(Imm32(~0x3), temp);
-
- addPtr(ImmPtr(&UnboxedArrayObject::CapacityArray), temp);
- branch32(Assembler::BelowOrEqual, Address(temp, 0), index, failure);
- bind(&done);
-}
-
// Inlined version of gc::CheckAllocatorState that checks the bare essentials
// and bails for anything that cannot be handled with our jit allocators.
void
@@ -1277,20 +1009,6 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject* templateObj,
nbytes = (nbytes < sizeof(uintptr_t)) ? 0 : nbytes - sizeof(uintptr_t);
offset += sizeof(uintptr_t);
}
- } else if (templateObj->is<UnboxedPlainObject>()) {
- storePtr(ImmWord(0), Address(obj, UnboxedPlainObject::offsetOfExpando()));
- if (initContents)
- initUnboxedObjectContents(obj, &templateObj->as<UnboxedPlainObject>());
- } else if (templateObj->is<UnboxedArrayObject>()) {
- MOZ_ASSERT(templateObj->as<UnboxedArrayObject>().hasInlineElements());
- int elementsOffset = UnboxedArrayObject::offsetOfInlineElements();
- computeEffectiveAddress(Address(obj, elementsOffset), temp);
- storePtr(temp, Address(obj, UnboxedArrayObject::offsetOfElements()));
- store32(Imm32(templateObj->as<UnboxedArrayObject>().length()),
- Address(obj, UnboxedArrayObject::offsetOfLength()));
- uint32_t capacityIndex = templateObj->as<UnboxedArrayObject>().capacityIndex();
- store32(Imm32(capacityIndex << UnboxedArrayObject::CapacityShift),
- Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
} else {
MOZ_CRASH("Unknown object");
}
@@ -1312,29 +1030,6 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject* templateObj,
}
void
-MacroAssembler::initUnboxedObjectContents(Register object, UnboxedPlainObject* templateObject)
-{
- const UnboxedLayout& layout = templateObject->layoutDontCheckGeneration();
-
- // Initialize reference fields of the object, per UnboxedPlainObject::create.
- if (const int32_t* list = layout.traceList()) {
- while (*list != -1) {
- storePtr(ImmGCPtr(GetJitContext()->runtime->names().empty),
- Address(object, UnboxedPlainObject::offsetOfData() + *list));
- list++;
- }
- list++;
- while (*list != -1) {
- storePtr(ImmWord(0),
- Address(object, UnboxedPlainObject::offsetOfData() + *list));
- list++;
- }
- // Unboxed objects don't have Values to initialize.
- MOZ_ASSERT(*(list + 1) == -1);
- }
-}
-
-void
MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register result,
Label* fail)
{
diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h
index b6616321c..d5cc95839 100644
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -36,7 +36,6 @@
#include "vm/ProxyObject.h"
#include "vm/Shape.h"
#include "vm/TypedArrayObject.h"
-#include "vm/UnboxedObject.h"
using mozilla::FloatingPoint;
@@ -1626,20 +1625,6 @@ class MacroAssembler : public MacroAssemblerSpecific
void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const Address& dest,
unsigned numElems = 0);
- // Load a property from an UnboxedPlainObject or UnboxedArrayObject.
- template <typename T>
- void loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output);
-
- // Store a property to an UnboxedPlainObject, without triggering barriers.
- // If failure is null, the value definitely has a type suitable for storing
- // in the property.
- template <typename T>
- void storeUnboxedProperty(T address, JSValueType type,
- const ConstantOrRegister& value, Label* failure);
-
- void checkUnboxedArrayCapacity(Register obj, const RegisterOrInt32Constant& index,
- Register temp, Label* failure);
-
Register extractString(const Address& address, Register scratch) {
return extractObject(address, scratch);
}
@@ -1716,8 +1701,6 @@ class MacroAssembler : public MacroAssemblerSpecific
LiveRegisterSet liveRegs, Label* fail,
TypedArrayObject* templateObj, TypedArrayLength lengthKind);
- void initUnboxedObjectContents(Register object, UnboxedPlainObject* templateObject);
-
void newGCString(Register result, Register temp, Label* fail);
void newGCFatInlineString(Register result, Register temp, Label* fail);
diff --git a/js/src/jit/OptimizationTracking.cpp b/js/src/jit/OptimizationTracking.cpp
index b42634d43..7d72795a0 100644
--- a/js/src/jit/OptimizationTracking.cpp
+++ b/js/src/jit/OptimizationTracking.cpp
@@ -15,11 +15,9 @@
#include "jit/JitcodeMap.h"
#include "jit/JitSpewer.h"
#include "js/TrackedOptimizationInfo.h"
-#include "vm/UnboxedObject.h"
#include "vm/ObjectGroup-inl.h"
#include "vm/TypeInference-inl.h"
-#include "vm/UnboxedObject-inl.h"
using namespace js;
using namespace js::jit;
@@ -846,8 +844,6 @@ MaybeConstructorFromType(TypeSet::Type ty)
return nullptr;
ObjectGroup* obj = ty.group();
TypeNewScript* newScript = obj->newScript();
- if (!newScript && obj->maybeUnboxedLayout())
- newScript = obj->unboxedLayout().newScript();
return newScript ? newScript->function() : nullptr;
}
diff --git a/js/src/jit/Recover.cpp b/js/src/jit/Recover.cpp
index 6fd71f377..793b631df 100644
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -30,7 +30,6 @@
#include "vm/Interpreter-inl.h"
#include "vm/NativeObject-inl.h"
-#include "vm/UnboxedObject-inl.h"
using namespace js;
using namespace js::jit;
@@ -1355,7 +1354,7 @@ RNewArray::recover(JSContext* cx, SnapshotIterator& iter) const
RootedValue result(cx);
RootedObjectGroup group(cx, templateObject->group());
- JSObject* resultObject = NewFullyAllocatedArrayTryUseGroup(cx, group, count_);
+ ArrayObject* resultObject = NewFullyAllocatedArrayTryUseGroup(cx, group, count_);
if (!resultObject)
return false;
@@ -1540,37 +1539,12 @@ RObjectState::recover(JSContext* cx, SnapshotIterator& iter) const
RootedObject object(cx, &iter.read().toObject());
RootedValue val(cx);
- if (object->is<UnboxedPlainObject>()) {
- const UnboxedLayout& layout = object->as<UnboxedPlainObject>().layout();
+ RootedNativeObject nativeObject(cx, &object->as<NativeObject>());
+ MOZ_ASSERT(nativeObject->slotSpan() == numSlots());
- RootedId id(cx);
- RootedValue receiver(cx, ObjectValue(*object));
- const UnboxedLayout::PropertyVector& properties = layout.properties();
- for (size_t i = 0; i < properties.length(); i++) {
- val = iter.read();
-
- // This is the default placeholder value of MObjectState, when no
- // properties are defined yet.
- if (val.isUndefined())
- continue;
-
- id = NameToId(properties[i].name);
- ObjectOpResult result;
-
- // SetProperty can only fail due to OOM.
- if (!SetProperty(cx, object, id, val, receiver, result))
- return false;
- if (!result)
- return result.reportError(cx, object, id);
- }
- } else {
- RootedNativeObject nativeObject(cx, &object->as<NativeObject>());
- MOZ_ASSERT(nativeObject->slotSpan() == numSlots());
-
- for (size_t i = 0; i < numSlots(); i++) {
- val = iter.read();
- nativeObject->setSlot(i, val);
- }
+ for (size_t i = 0; i < numSlots(); i++) {
+ val = iter.read();
+ nativeObject->setSlot(i, val);
}
val.setObject(*object);
diff --git a/js/src/jit/ScalarReplacement.cpp b/js/src/jit/ScalarReplacement.cpp
index 2065c0371..97ba52349 100644
--- a/js/src/jit/ScalarReplacement.cpp
+++ b/js/src/jit/ScalarReplacement.cpp
@@ -285,10 +285,6 @@ class ObjectMemoryView : public MDefinitionVisitorDefaultNoop
void visitGuardShape(MGuardShape* ins);
void visitFunctionEnvironment(MFunctionEnvironment* ins);
void visitLambda(MLambda* ins);
-
- private:
- void storeOffset(MInstruction* ins, size_t offset, MDefinition* value);
- void loadOffset(MInstruction* ins, size_t offset);
};
const char* ObjectMemoryView::phaseName = "Scalar Replacement of Object";
@@ -630,35 +626,6 @@ ObjectMemoryView::visitLambda(MLambda* ins)
ins->setIncompleteObject();
}
-void
-ObjectMemoryView::storeOffset(MInstruction* ins, size_t offset, MDefinition* value)
-{
- // Clone the state and update the slot value.
- MOZ_ASSERT(state_->hasOffset(offset));
- state_ = BlockState::Copy(alloc_, state_);
- if (!state_) {
- oom_ = true;
- return;
- }
-
- state_->setOffset(offset, value);
- ins->block()->insertBefore(ins, state_);
-
- // Remove original instruction.
- ins->block()->discard(ins);
-}
-
-void
-ObjectMemoryView::loadOffset(MInstruction* ins, size_t offset)
-{
- // Replace load by the slot value.
- MOZ_ASSERT(state_->hasOffset(offset));
- ins->replaceAllUsesWith(state_->getOffset(offset));
-
- // Remove original instruction.
- ins->block()->discard(ins);
-}
-
static bool
IndexOf(MDefinition* ins, int32_t* res)
{
@@ -795,11 +762,6 @@ IsArrayEscaped(MInstruction* ins)
return true;
}
- if (obj->is<UnboxedArrayObject>()) {
- JitSpew(JitSpew_Escape, "Template object is an unboxed plain object.");
- return true;
- }
-
if (length >= 16) {
JitSpew(JitSpew_Escape, "Array has too many elements");
return true;
diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp
index 2475dfb22..05a95824f 100644
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -286,11 +286,6 @@ ICStub::trace(JSTracer* trc)
TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-dense-shape");
break;
}
- case ICStub::GetElem_UnboxedArray: {
- ICGetElem_UnboxedArray* getElemStub = toGetElem_UnboxedArray();
- TraceEdge(trc, &getElemStub->group(), "baseline-getelem-unboxed-array-group");
- break;
- }
case ICStub::GetElem_TypedArray: {
ICGetElem_TypedArray* getElemStub = toGetElem_TypedArray();
TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-typedarray-shape");
@@ -2249,9 +2244,7 @@ IsCacheableProtoChain(JSObject* obj, JSObject* holder, bool isDOMProxy)
if (!isDOMProxy && !obj->isNative()) {
if (obj == holder)
return false;
- if (!obj->is<UnboxedPlainObject>() &&
- !obj->is<UnboxedArrayObject>() &&
- !obj->is<TypedObject>())
+ if (!obj->is<TypedObject>())
{
return false;
}
@@ -2579,12 +2572,6 @@ CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, PropertyName* name,
} else if (curObj != obj) {
// Non-native objects are only handled as the original receiver.
return false;
- } else if (curObj->is<UnboxedPlainObject>()) {
- if (curObj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, NameToId(name)))
- return false;
- } else if (curObj->is<UnboxedArrayObject>()) {
- if (name == cx->names().length)
- return false;
} else if (curObj->is<TypedObject>()) {
if (curObj->as<TypedObject>().typeDescr().hasProperty(cx->names(), NameToId(name)))
return false;
@@ -2849,34 +2836,15 @@ GuardReceiverObject(MacroAssembler& masm, ReceiverGuard guard,
{
Address groupAddress(ICStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfGroup());
Address shapeAddress(ICStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfShape());
- Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
if (guard.group) {
masm.loadPtr(groupAddress, scratch);
masm.branchTestObjGroup(Assembler::NotEqual, object, scratch, failure);
-
- if (guard.group->clasp() == &UnboxedPlainObject::class_ && !guard.shape) {
- // Guard the unboxed object has no expando object.
- masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure);
- }
}
if (guard.shape) {
masm.loadPtr(shapeAddress, scratch);
- if (guard.group && guard.group->clasp() == &UnboxedPlainObject::class_) {
- // Guard the unboxed object has a matching expando object.
- masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure);
- Label done;
- masm.push(object);
- masm.loadPtr(expandoAddress, object);
- masm.branchTestObjShape(Assembler::Equal, object, scratch, &done);
- masm.pop(object);
- masm.jump(failure);
- masm.bind(&done);
- masm.pop(object);
- } else {
- masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure);
- }
+ masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure);
}
}
@@ -4260,8 +4228,7 @@ DoNewObject(JSContext* cx, void* payload, ICNewObject_Fallback* stub, MutableHan
return false;
if (!stub->invalid() &&
- (templateObject->is<UnboxedPlainObject>() ||
- !templateObject->as<PlainObject>().hasDynamicSlots()))
+ !templateObject->as<PlainObject>().hasDynamicSlots())
{
JitCode* code = GenerateNewObjectWithTemplateCode(cx, templateObject);
if (!code)
diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp
index 77b9e3647..1ff7adfd1 100644
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -28,7 +28,7 @@
#include "vm/NativeObject-inl.h"
#include "vm/StringObject-inl.h"
#include "vm/TypeInference-inl.h"
-#include "vm/UnboxedObject-inl.h"
+#include "gc/StoreBuffer-inl.h"
using namespace js;
using namespace js::jit;
@@ -54,8 +54,8 @@ VMFunction::addToFunctions()
}
bool
-InvokeFunction(JSContext* cx, HandleObject obj, bool constructing, uint32_t argc, Value* argv,
- MutableHandleValue rval)
+InvokeFunction(JSContext* cx, HandleObject obj, bool constructing, bool ignoresReturnValue,
+ uint32_t argc, Value* argv, MutableHandleValue rval)
{
TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
TraceLogStartEvent(logger, TraceLogger_Call);
@@ -104,7 +104,7 @@ InvokeFunction(JSContext* cx, HandleObject obj, bool constructing, uint32_t argc
return InternalConstructWithProvidedThis(cx, fval, thisv, cargs, newTarget, rval);
}
- InvokeArgs args(cx);
+ InvokeArgsMaybeIgnoresReturnValue args(cx, ignoresReturnValue);
if (!args.init(cx, argc))
return false;
@@ -120,7 +120,7 @@ InvokeFunctionShuffleNewTarget(JSContext* cx, HandleObject obj, uint32_t numActu
{
MOZ_ASSERT(numFormalArgs > numActualArgs);
argv[1 + numActualArgs] = argv[1 + numFormalArgs];
- return InvokeFunction(cx, obj, true, numActualArgs, argv, rval);
+ return InvokeFunction(cx, obj, true, false, numActualArgs, argv, rval);
}
bool
@@ -304,21 +304,9 @@ template bool StringsEqual<true>(JSContext* cx, HandleString lhs, HandleString r
template bool StringsEqual<false>(JSContext* cx, HandleString lhs, HandleString rhs, bool* res);
bool
-ArraySpliceDense(JSContext* cx, HandleObject obj, uint32_t start, uint32_t deleteCount)
-{
- JS::AutoValueArray<4> argv(cx);
- argv[0].setUndefined();
- argv[1].setObject(*obj);
- argv[2].set(Int32Value(start));
- argv[3].set(Int32Value(deleteCount));
-
- return js::array_splice_impl(cx, 2, argv.begin(), false);
-}
-
-bool
ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
{
- MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
+ MOZ_ASSERT(obj->is<ArrayObject>());
AutoDetectInvalidation adi(cx, rval);
@@ -337,12 +325,11 @@ ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
}
bool
-ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length)
+ArrayPushDense(JSContext* cx, HandleArrayObject arr, HandleValue v, uint32_t* length)
{
- *length = GetAnyBoxedOrUnboxedArrayLength(obj);
- DenseElementResult result =
- SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, *length, v.address(), 1,
- ShouldUpdateTypes::DontUpdate);
+ *length = arr->length();
+ DenseElementResult result = arr->setOrExtendDenseElements(cx, *length, v.address(), 1,
+ ShouldUpdateTypes::DontUpdate);
if (result != DenseElementResult::Incomplete) {
(*length)++;
return result == DenseElementResult::Success;
@@ -350,7 +337,7 @@ ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length)
JS::AutoValueArray<3> argv(cx);
argv[0].setUndefined();
- argv[1].setObject(*obj);
+ argv[1].setObject(*arr);
argv[2].set(v);
if (!js::array_push(cx, 1, argv.begin()))
return false;
@@ -362,7 +349,7 @@ ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length)
bool
ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
{
- MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
+ MOZ_ASSERT(obj->is<ArrayObject>());
AutoDetectInvalidation adi(cx, rval);
@@ -556,7 +543,7 @@ CreateThis(JSContext* cx, HandleObject callee, HandleObject newTarget, MutableHa
if (callee->is<JSFunction>()) {
RootedFunction fun(cx, &callee->as<JSFunction>());
if (fun->isInterpreted() && fun->isConstructor()) {
- JSScript* script = fun->getOrCreateScript(cx);
+ JSScript* script = JSFunction::getOrCreateScript(cx, fun);
if (!script || !script->ensureHasTypes(cx))
return false;
if (fun->isBoundFunction() || script->isDerivedClassConstructor()) {
@@ -1143,16 +1130,14 @@ Recompile(JSContext* cx)
}
bool
-SetDenseOrUnboxedArrayElement(JSContext* cx, HandleObject obj, int32_t index,
- HandleValue value, bool strict)
+SetDenseElement(JSContext* cx, HandleNativeObject obj, int32_t index, HandleValue value, bool strict)
{
// This function is called from Ion code for StoreElementHole's OOL path.
- // In this case we know the object is native or an unboxed array and that
- // no type changes are needed.
+ // In this case we know the object is native and that no type changes are
+ // needed.
- DenseElementResult result =
- SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, index, value.address(), 1,
- ShouldUpdateTypes::DontUpdate);
+ DenseElementResult result = obj->setOrExtendDenseElements(cx, index, value.address(), 1,
+ ShouldUpdateTypes::DontUpdate);
if (result != DenseElementResult::Incomplete)
return result == DenseElementResult::Success;
diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h
index 572f05373..94f741397 100644
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -584,8 +584,8 @@ class AutoDetectInvalidation
};
MOZ_MUST_USE bool
-InvokeFunction(JSContext* cx, HandleObject obj0, bool constructing, uint32_t argc, Value* argv,
- MutableHandleValue rval);
+InvokeFunction(JSContext* cx, HandleObject obj0, bool constructing, bool ignoresReturnValue,
+ uint32_t argc, Value* argv, MutableHandleValue rval);
MOZ_MUST_USE bool
InvokeFunctionShuffleNewTarget(JSContext* cx, HandleObject obj, uint32_t numActualArgs,
uint32_t numFormalArgs, Value* argv, MutableHandleValue rval);
@@ -622,7 +622,7 @@ template<bool Equal>
bool StringsEqual(JSContext* cx, HandleString left, HandleString right, bool* res);
MOZ_MUST_USE bool ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval);
-MOZ_MUST_USE bool ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length);
+MOZ_MUST_USE bool ArrayPushDense(JSContext* cx, HandleArrayObject obj, HandleValue v, uint32_t* length);
MOZ_MUST_USE bool ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval);
JSString* ArrayJoin(JSContext* cx, HandleObject array, HandleString sep);
@@ -739,17 +739,14 @@ JSObject* CreateDerivedTypedObj(JSContext* cx, HandleObject descr,
HandleObject owner, int32_t offset);
MOZ_MUST_USE bool
-ArraySpliceDense(JSContext* cx, HandleObject obj, uint32_t start, uint32_t deleteCount);
-
-MOZ_MUST_USE bool
Recompile(JSContext* cx);
MOZ_MUST_USE bool
ForcedRecompile(JSContext* cx);
JSString* StringReplace(JSContext* cx, HandleString string, HandleString pattern,
HandleString repl);
-MOZ_MUST_USE bool SetDenseOrUnboxedArrayElement(JSContext* cx, HandleObject obj, int32_t index,
- HandleValue value, bool strict);
+MOZ_MUST_USE bool SetDenseElement(JSContext* cx, HandleNativeObject obj, int32_t index,
+ HandleValue value, bool strict);
void AssertValidObjectPtr(JSContext* cx, JSObject* obj);
void AssertValidObjectOrNullPtr(JSContext* cx, JSObject* obj);
diff --git a/js/src/jit/arm64/Architecture-arm64.h b/js/src/jit/arm64/Architecture-arm64.h
index e74340f13..bee212db7 100644
--- a/js/src/jit/arm64/Architecture-arm64.h
+++ b/js/src/jit/arm64/Architecture-arm64.h
@@ -299,10 +299,12 @@ static const uint32_t ION_FRAME_SLACK_SIZE = 24;
static const uint32_t ShadowStackSpace = 0;
-// TODO:
-// This constant needs to be updated to account for whatever near/far branching
-// strategy is used by ARM64.
-static const uint32_t JumpImmediateRange = UINT32_MAX;
+// When our only strategy for far jumps is to encode the offset directly, and
+// not insert any jump islands during assembly for even further jumps, then the
+// architecture restricts us to -2^27 .. 2^27-4, to fit into a signed 28-bit
+// value. We further reduce this range to allow the far-jump inserting code to
+// have some breathing room.
+static const uint32_t JumpImmediateRange = ((1 << 27) - (20 * 1024 * 1024));
static const uint32_t ABIStackAlignment = 16;
static const uint32_t CodeAlignment = 16;
diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h
index f386d5256..ecf02816e 100644
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -1898,6 +1898,9 @@ class LJSCallInstructionHelper : public LCallInstructionHelper<Defs, Operands, T
bool isConstructing() const {
return mir()->isConstructing();
}
+ bool ignoresReturnValue() const {
+ return mir()->ignoresReturnValue();
+ }
};
// Generates a polymorphic callsite, wherein the function being called is
@@ -2218,34 +2221,6 @@ class LApplyArrayGeneric : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES
}
};
-class LArraySplice : public LCallInstructionHelper<0, 3, 0>
-{
- public:
- LIR_HEADER(ArraySplice)
-
- LArraySplice(const LAllocation& object, const LAllocation& start,
- const LAllocation& deleteCount)
- {
- setOperand(0, object);
- setOperand(1, start);
- setOperand(2, deleteCount);
- }
-
- MArraySplice* mir() const {
- return mir_->toArraySplice();
- }
-
- const LAllocation* getObject() {
- return getOperand(0);
- }
- const LAllocation* getStart() {
- return getOperand(1);
- }
- const LAllocation* getDeleteCount() {
- return getOperand(2);
- }
-};
-
class LGetDynamicName : public LCallInstructionHelper<BOX_PIECES, 2, 3>
{
public:
@@ -5165,72 +5140,6 @@ class LSetInitializedLength : public LInstructionHelper<0, 2, 0>
}
};
-class LUnboxedArrayLength : public LInstructionHelper<1, 1, 0>
-{
- public:
- LIR_HEADER(UnboxedArrayLength)
-
- explicit LUnboxedArrayLength(const LAllocation& object) {
- setOperand(0, object);
- }
-
- const LAllocation* object() {
- return getOperand(0);
- }
-};
-
-class LUnboxedArrayInitializedLength : public LInstructionHelper<1, 1, 0>
-{
- public:
- LIR_HEADER(UnboxedArrayInitializedLength)
-
- explicit LUnboxedArrayInitializedLength(const LAllocation& object) {
- setOperand(0, object);
- }
-
- const LAllocation* object() {
- return getOperand(0);
- }
-};
-
-class LIncrementUnboxedArrayInitializedLength : public LInstructionHelper<0, 1, 0>
-{
- public:
- LIR_HEADER(IncrementUnboxedArrayInitializedLength)
-
- explicit LIncrementUnboxedArrayInitializedLength(const LAllocation& object) {
- setOperand(0, object);
- }
-
- const LAllocation* object() {
- return getOperand(0);
- }
-};
-
-class LSetUnboxedArrayInitializedLength : public LInstructionHelper<0, 2, 1>
-{
- public:
- LIR_HEADER(SetUnboxedArrayInitializedLength)
-
- explicit LSetUnboxedArrayInitializedLength(const LAllocation& object,
- const LAllocation& length,
- const LDefinition& temp) {
- setOperand(0, object);
- setOperand(1, length);
- setTemp(0, temp);
- }
-
- const LAllocation* object() {
- return getOperand(0);
- }
- const LAllocation* length() {
- return getOperand(1);
- }
- const LDefinition* temp() {
- return getTemp(0);
- }
-};
-
// Load the length from an elements header.
class LArrayLength : public LInstructionHelper<1, 1, 0>
{
@@ -5735,19 +5644,17 @@ class LStoreElementT : public LInstructionHelper<0, 3, 0>
};
// Like LStoreElementV, but supports indexes >= initialized length.
-class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 1>
+class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 0>
{
public:
LIR_HEADER(StoreElementHoleV)
LStoreElementHoleV(const LAllocation& object, const LAllocation& elements,
- const LAllocation& index, const LBoxAllocation& value,
- const LDefinition& temp) {
+ const LAllocation& index, const LBoxAllocation& value) {
setOperand(0, object);
setOperand(1, elements);
setOperand(2, index);
setBoxOperand(Value, value);
- setTemp(0, temp);
}
static const size_t Value = 3;
@@ -5767,19 +5674,17 @@ class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 1>
};
// Like LStoreElementT, but supports indexes >= initialized length.
-class LStoreElementHoleT : public LInstructionHelper<0, 4, 1>
+class LStoreElementHoleT : public LInstructionHelper<0, 4, 0>
{
public:
LIR_HEADER(StoreElementHoleT)
LStoreElementHoleT(const LAllocation& object, const LAllocation& elements,
- const LAllocation& index, const LAllocation& value,
- const LDefinition& temp) {
+ const LAllocation& index, const LAllocation& value) {
setOperand(0, object);
setOperand(1, elements);
setOperand(2, index);
setOperand(3, value);
- setTemp(0, temp);
}
const MStoreElementHole* mir() const {
@@ -5800,19 +5705,17 @@ class LStoreElementHoleT : public LInstructionHelper<0, 4, 1>
};
// Like LStoreElementV, but can just ignore assignment (for eg. frozen objects)
-class LFallibleStoreElementV : public LInstructionHelper<0, 3 + BOX_PIECES, 1>
+class LFallibleStoreElementV : public LInstructionHelper<0, 3 + BOX_PIECES, 0>
{
public:
LIR_HEADER(FallibleStoreElementV)
LFallibleStoreElementV(const LAllocation& object, const LAllocation& elements,
- const LAllocation& index, const LBoxAllocation& value,
- const LDefinition& temp) {
+ const LAllocation& index, const LBoxAllocation& value) {
setOperand(0, object);
setOperand(1, elements);
setOperand(2, index);
setBoxOperand(Value, value);
- setTemp(0, temp);
}
static const size_t Value = 3;
@@ -5832,19 +5735,17 @@ class LFallibleStoreElementV : public LInstructionHelper<0, 3 + BOX_PIECES, 1>
};
// Like LStoreElementT, but can just ignore assignment (for eg. frozen objects)
-class LFallibleStoreElementT : public LInstructionHelper<0, 4, 1>
+class LFallibleStoreElementT : public LInstructionHelper<0, 4, 0>
{
public:
LIR_HEADER(FallibleStoreElementT)
LFallibleStoreElementT(const LAllocation& object, const LAllocation& elements,
- const LAllocation& index, const LAllocation& value,
- const LDefinition& temp) {
+ const LAllocation& index, const LAllocation& value) {
setOperand(0, object);
setOperand(1, elements);
setOperand(2, index);
setOperand(3, value);
- setTemp(0, temp);
}
const MFallibleStoreElement* mir() const {
diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h
index 6912e8904..e260d7e94 100644
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -69,7 +69,6 @@
_(NewArrayDynamicLength) \
_(NewTypedArray) \
_(NewTypedArrayDynamicLength) \
- _(ArraySplice) \
_(NewObject) \
_(NewTypedObject) \
_(NewNamedLambdaObject) \
@@ -267,10 +266,6 @@
_(PostWriteElementBarrierV) \
_(InitializedLength) \
_(SetInitializedLength) \
- _(UnboxedArrayLength) \
- _(UnboxedArrayInitializedLength) \
- _(IncrementUnboxedArrayInitializedLength) \
- _(SetUnboxedArrayInitializedLength) \
_(BoundsCheck) \
_(BoundsCheckRange) \
_(BoundsCheckLower) \
diff --git a/js/src/js.msg b/js/src/js.msg
index 4ded69a25..f57b36a90 100644
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -63,6 +63,7 @@ MSG_DEF(JSMSG_SPREAD_TOO_LARGE, 0, JSEXN_RANGEERR, "array too large due t
MSG_DEF(JSMSG_BAD_WEAKMAP_KEY, 0, JSEXN_TYPEERR, "cannot use the given object as a weak map key")
MSG_DEF(JSMSG_BAD_GETTER_OR_SETTER, 1, JSEXN_TYPEERR, "invalid {0} usage")
MSG_DEF(JSMSG_BAD_ARRAY_LENGTH, 0, JSEXN_RANGEERR, "invalid array length")
+MSG_DEF(JSMSG_REDECLARED_PREV, 2, JSEXN_NOTE, "Previously declared at line {0}, column {1}")
MSG_DEF(JSMSG_REDECLARED_VAR, 2, JSEXN_SYNTAXERR, "redeclaration of {0} {1}")
MSG_DEF(JSMSG_UNDECLARED_VAR, 1, JSEXN_REFERENCEERR, "assignment to undeclared variable {0}")
MSG_DEF(JSMSG_GETTER_ONLY, 1, JSEXN_TYPEERR, "setting getter-only property {0}")
@@ -186,6 +187,7 @@ MSG_DEF(JSMSG_AS_AFTER_IMPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'as'
MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD, 1, JSEXN_SYNTAXERR, "missing keyword 'as' after reserved word '{0}'")
MSG_DEF(JSMSG_ASYNC_GENERATOR, 0, JSEXN_SYNTAXERR, "generator function or method can't be async")
MSG_DEF(JSMSG_AWAIT_IN_DEFAULT, 0, JSEXN_SYNTAXERR, "await can't be used in default expression")
+MSG_DEF(JSMSG_AWAIT_OUTSIDE_ASYNC, 0, JSEXN_SYNTAXERR, "await is only valid in async functions")
MSG_DEF(JSMSG_BAD_ARROW_ARGS, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)")
MSG_DEF(JSMSG_BAD_BINDING, 1, JSEXN_SYNTAXERR, "redefining {0} is deprecated")
MSG_DEF(JSMSG_BAD_CONST_DECL, 0, JSEXN_SYNTAXERR, "missing = in const declaration")
@@ -209,6 +211,8 @@ MSG_DEF(JSMSG_BAD_POW_LEFTSIDE, 0, JSEXN_SYNTAXERR, "unparenthesized unar
MSG_DEF(JSMSG_BAD_PROP_ID, 0, JSEXN_SYNTAXERR, "invalid property id")
MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD, 1, JSEXN_SYNTAXERR, "{0} not in function")
MSG_DEF(JSMSG_BAD_STRICT_ASSIGN, 1, JSEXN_SYNTAXERR, "'{0}' can't be defined or assigned to in strict mode code")
+MSG_DEF(JSMSG_BAD_STRICT_ASSIGN_ARGUMENTS, 0, JSEXN_SYNTAXERR, "'arguments' can't be defined or assigned to in strict mode code")
+MSG_DEF(JSMSG_BAD_STRICT_ASSIGN_EVAL, 0, JSEXN_SYNTAXERR, "'eval' can't be defined or assigned to in strict mode code")
MSG_DEF(JSMSG_BAD_SWITCH, 0, JSEXN_SYNTAXERR, "invalid switch statement")
MSG_DEF(JSMSG_BAD_SUPER, 0, JSEXN_SYNTAXERR, "invalid use of keyword 'super'")
MSG_DEF(JSMSG_BAD_SUPERPROP, 1, JSEXN_SYNTAXERR, "use of super {0} accesses only valid within methods or eval code within methods")
@@ -216,6 +220,7 @@ MSG_DEF(JSMSG_BAD_SUPERCALL, 0, JSEXN_SYNTAXERR, "super() is only vali
MSG_DEF(JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION, 0, JSEXN_SYNTAXERR, "missing ] after array comprehension")
MSG_DEF(JSMSG_BRACKET_AFTER_LIST, 0, JSEXN_SYNTAXERR, "missing ] after element list")
MSG_DEF(JSMSG_BRACKET_IN_INDEX, 0, JSEXN_SYNTAXERR, "missing ] in index expression")
+MSG_DEF(JSMSG_BRACKET_OPENED, 2, JSEXN_NOTE, "[ opened at line {0}, column {1}")
MSG_DEF(JSMSG_CATCH_AFTER_GENERAL, 0, JSEXN_SYNTAXERR, "catch after unconditional catch")
MSG_DEF(JSMSG_CATCH_IDENTIFIER, 0, JSEXN_SYNTAXERR, "missing identifier in catch")
MSG_DEF(JSMSG_CATCH_OR_FINALLY, 0, JSEXN_SYNTAXERR, "missing catch or finally after try")
@@ -226,6 +231,7 @@ MSG_DEF(JSMSG_COLON_IN_COND, 0, JSEXN_SYNTAXERR, "missing : in conditi
MSG_DEF(JSMSG_COMP_PROP_UNTERM_EXPR, 0, JSEXN_SYNTAXERR, "missing ] in computed property name")
MSG_DEF(JSMSG_CONTRARY_NONDIRECTIVE, 1, JSEXN_SYNTAXERR, "'{0}' statement won't be enforced as a directive because it isn't in directive prologue position")
MSG_DEF(JSMSG_CURLY_AFTER_BODY, 0, JSEXN_SYNTAXERR, "missing } after function body")
+MSG_DEF(JSMSG_CURLY_OPENED, 2, JSEXN_NOTE, "{ opened at line {0}, column {1}")
MSG_DEF(JSMSG_CURLY_AFTER_CATCH, 0, JSEXN_SYNTAXERR, "missing } after catch block")
MSG_DEF(JSMSG_CURLY_AFTER_FINALLY, 0, JSEXN_SYNTAXERR, "missing } after finally block")
MSG_DEF(JSMSG_CURLY_AFTER_LIST, 0, JSEXN_SYNTAXERR, "missing } after property list")
@@ -264,6 +270,7 @@ MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 0, JSEXN_SYNTAXERR, "import declarations
MSG_DEF(JSMSG_OF_AFTER_FOR_LOOP_DECL, 0, JSEXN_SYNTAXERR, "a declaration in the head of a for-of loop can't have an initializer")
MSG_DEF(JSMSG_IN_AFTER_LEXICAL_FOR_DECL,0,JSEXN_SYNTAXERR, "a lexical declaration in the head of a for-in loop can't have an initializer")
MSG_DEF(JSMSG_INVALID_FOR_IN_DECL_WITH_INIT,0,JSEXN_SYNTAXERR,"for-in loop head declarations may not have initializers")
+MSG_DEF(JSMSG_INVALID_ID, 1, JSEXN_SYNTAXERR, "{0} is an invalid identifier")
MSG_DEF(JSMSG_LABEL_NOT_FOUND, 0, JSEXN_SYNTAXERR, "label not found")
MSG_DEF(JSMSG_LET_COMP_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable")
MSG_DEF(JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, 1, JSEXN_SYNTAXERR, "{0} declaration not directly within block")
@@ -501,7 +508,7 @@ MSG_DEF(JSMSG_RANGE_WITH_CLASS_ESCAPE, 0, JSEXN_SYNTAXERR, "character class esca
MSG_DEF(JSMSG_RAW_BRACE_IN_REGEP, 0, JSEXN_SYNTAXERR, "raw brace is not allowed in regular expression with unicode flag")
MSG_DEF(JSMSG_RAW_BRACKET_IN_REGEP, 0, JSEXN_SYNTAXERR, "raw bracket is not allowed in regular expression with unicode flag")
MSG_DEF(JSMSG_TOO_MANY_PARENS, 0, JSEXN_INTERNALERR, "too many parentheses in regular expression")
-MSG_DEF(JSMSG_UNICODE_OVERFLOW, 0, JSEXN_SYNTAXERR, "unicode codepoint should not be greater than 0x10FFFF in regular expression")
+MSG_DEF(JSMSG_UNICODE_OVERFLOW, 1, JSEXN_SYNTAXERR, "Unicode codepoint must not be greater than 0x10FFFF in {0}")
MSG_DEF(JSMSG_UNMATCHED_RIGHT_PAREN, 0, JSEXN_SYNTAXERR, "unmatched ) in regular expression")
MSG_DEF(JSMSG_UNTERM_CLASS, 0, JSEXN_SYNTAXERR, "unterminated character class")
diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build
index 277a145b0..c176fbf0a 100644
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -37,6 +37,7 @@ UNIFIED_SOURCES += [
'testForOfIterator.cpp',
'testForwardSetProperty.cpp',
'testFreshGlobalEvalRedefinition.cpp',
+ 'testFunctionBinding.cpp',
'testFunctionProperties.cpp',
'testGCAllocator.cpp',
'testGCCellPtr.cpp',
diff --git a/js/src/jsapi-tests/testFunctionBinding.cpp b/js/src/jsapi-tests/testFunctionBinding.cpp
new file mode 100644
index 000000000..33632db14
--- /dev/null
+++ b/js/src/jsapi-tests/testFunctionBinding.cpp
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Test function name binding.
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "jsfriendapi.h"
+
+#include "jsapi-tests/tests.h"
+
+using namespace js;
+
+BEGIN_TEST(test_functionBinding)
+{
+ RootedFunction fun(cx);
+
+ JS::CompileOptions options(cx);
+ options.setFileAndLine(__FILE__, __LINE__);
+
+ // Named function shouldn't have it's binding.
+ const char s1chars[] = "return (typeof s1) == 'undefined';";
+ JS::AutoObjectVector emptyScopeChain(cx);
+ CHECK(JS::CompileFunction(cx, emptyScopeChain, options, "s1", 0, nullptr, s1chars,
+ strlen(s1chars), &fun));
+ CHECK(fun);
+
+ JS::AutoValueVector args(cx);
+ RootedValue rval(cx);
+ CHECK(JS::Call(cx, UndefinedHandleValue, fun, args, &rval));
+ CHECK(rval.isBoolean());
+ CHECK(rval.toBoolean());
+
+ // Named function shouldn't have `anonymous` binding.
+ const char s2chars[] = "return (typeof anonymous) == 'undefined';";
+ CHECK(JS::CompileFunction(cx, emptyScopeChain, options, "s2", 0, nullptr, s2chars,
+ strlen(s2chars), &fun));
+ CHECK(fun);
+
+ CHECK(JS::Call(cx, UndefinedHandleValue, fun, args, &rval));
+ CHECK(rval.isBoolean());
+ CHECK(rval.toBoolean());
+
+ // Anonymous function shouldn't have `anonymous` binding.
+ const char s3chars[] = "return (typeof anonymous) == 'undefined';";
+ CHECK(JS::CompileFunction(cx, emptyScopeChain, options, nullptr, 0, nullptr, s3chars,
+ strlen(s3chars), &fun));
+ CHECK(fun);
+
+ CHECK(JS::Call(cx, UndefinedHandleValue, fun, args, &rval));
+ CHECK(rval.isBoolean());
+ CHECK(rval.toBoolean());
+
+ return true;
+}
+END_TEST(test_functionBinding)
diff --git a/js/src/jsapi-tests/testUbiNode.cpp b/js/src/jsapi-tests/testUbiNode.cpp
index 075e777d6..00b1253e7 100644
--- a/js/src/jsapi-tests/testUbiNode.cpp
+++ b/js/src/jsapi-tests/testUbiNode.cpp
@@ -646,7 +646,7 @@ BEGIN_TEST(test_JS_ubi_Node_scriptFilename)
CHECK(obj->is<JSFunction>());
JS::RootedFunction func(cx, &obj->as<JSFunction>());
- JS::RootedScript script(cx, func->getOrCreateScript(cx));
+ JS::RootedScript script(cx, JSFunction::getOrCreateScript(cx, func));
CHECK(script);
CHECK(script->filename());
diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
index 9ee29ffe4..84a315587 100644
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -69,7 +69,6 @@
#include "js/Proxy.h"
#include "js/SliceBudget.h"
#include "js/StructuredClone.h"
-#include "js/UniquePtr.h"
#include "js/Utility.h"
#include "vm/AsyncFunction.h"
#include "vm/DateObject.h"
@@ -1022,7 +1021,7 @@ JS_ResolveStandardClass(JSContext* cx, HandleObject obj, HandleId id, bool* reso
CHECK_REQUEST(cx);
assertSameCompartment(cx, obj, id);
- Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
*resolved = false;
if (!JSID_IS_ATOM(id))
@@ -1066,10 +1065,7 @@ JS_ResolveStandardClass(JSContext* cx, HandleObject obj, HandleId id, bool* reso
// more way: its prototype chain is lazily initialized. That is,
// global->getProto() might be null right now because we haven't created
// Object.prototype yet. Force it now.
- if (!global->getOrCreateObjectPrototype(cx))
- return false;
-
- return true;
+ return GlobalObject::getOrCreateObjectPrototype(cx, global);
}
JS_PUBLIC_API(bool)
@@ -1102,8 +1098,7 @@ JS_EnumerateStandardClasses(JSContext* cx, HandleObject obj)
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, obj);
- MOZ_ASSERT(obj->is<GlobalObject>());
- Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
return GlobalObject::initStandardClasses(cx, global);
}
@@ -1159,7 +1154,8 @@ JS_GetObjectPrototype(JSContext* cx, HandleObject forObj)
{
CHECK_REQUEST(cx);
assertSameCompartment(cx, forObj);
- return forObj->global().getOrCreateObjectPrototype(cx);
+ Rooted<GlobalObject*> global(cx, &forObj->global());
+ return GlobalObject::getOrCreateObjectPrototype(cx, global);
}
JS_PUBLIC_API(JSObject*)
@@ -1167,7 +1163,8 @@ JS_GetFunctionPrototype(JSContext* cx, HandleObject forObj)
{
CHECK_REQUEST(cx);
assertSameCompartment(cx, forObj);
- return forObj->global().getOrCreateFunctionPrototype(cx);
+ Rooted<GlobalObject*> global(cx, &forObj->global());
+ return GlobalObject::getOrCreateFunctionPrototype(cx, global);
}
JS_PUBLIC_API(JSObject*)
@@ -3489,7 +3486,7 @@ CreateNonSyntacticEnvironmentChain(JSContext* cx, AutoObjectVector& envChain,
// declaration was qualified by "var". There is only sadness.
//
// See JSObject::isQualifiedVarObj.
- if (!env->setQualifiedVarObj(cx))
+ if (!JSObject::setQualifiedVarObj(cx, env))
return false;
// Also get a non-syntactic lexical environment to capture 'let' and
@@ -3549,7 +3546,7 @@ CloneFunctionObject(JSContext* cx, HandleObject funobj, HandleObject env, Handle
RootedFunction fun(cx, &funobj->as<JSFunction>());
if (fun->isInterpretedLazy()) {
AutoCompartment ac(cx, funobj);
- if (!fun->getOrCreateScript(cx))
+ if (!JSFunction::getOrCreateScript(cx, fun))
return nullptr;
}
@@ -3581,7 +3578,7 @@ CloneFunctionObject(JSContext* cx, HandleObject funobj, HandleObject env, Handle
// Fail here if we OOM during debug asserting.
// CloneFunctionReuseScript will delazify the script anyways, so we
// are not creating an extra failure condition for DEBUG builds.
- if (!fun->getOrCreateScript(cx))
+ if (!JSFunction::getOrCreateScript(cx, fun))
return nullptr;
MOZ_ASSERT(scope->as<GlobalScope>().isSyntactic() ||
fun->nonLazyScript()->hasNonSyntacticScope());
@@ -4234,7 +4231,7 @@ JS_GetFunctionScript(JSContext* cx, HandleFunction fun)
return nullptr;
if (fun->isInterpretedLazy()) {
AutoCompartment funCompartment(cx, fun);
- JSScript* script = fun->getOrCreateScript(cx);
+ JSScript* script = JSFunction::getOrCreateScript(cx, fun);
if (!script)
MOZ_CRASH();
return script;
@@ -4405,14 +4402,15 @@ JS_DecompileScript(JSContext* cx, HandleScript script, const char* name, unsigne
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
- script->ensureNonLazyCanonicalFunction(cx);
+ script->ensureNonLazyCanonicalFunction();
RootedFunction fun(cx, script->functionNonDelazifying());
if (fun)
return JS_DecompileFunction(cx, fun, indent);
bool haveSource = script->scriptSource()->hasSourceData();
if (!haveSource && !JSScript::loadSource(cx, script->scriptSource(), &haveSource))
return nullptr;
- return haveSource ? script->sourceData(cx) : NewStringCopyZ<CanGC>(cx, "[no source]");
+ return haveSource ? JSScript::sourceData(cx, script)
+ : NewStringCopyZ<CanGC>(cx, "[no source]");
}
JS_PUBLIC_API(JSString*)
@@ -4884,7 +4882,7 @@ JS::CallOriginalPromiseResolve(JSContext* cx, JS::HandleValue resolutionValue)
assertSameCompartment(cx, resolutionValue);
RootedObject promise(cx, PromiseObject::unforgeableResolve(cx, resolutionValue));
- MOZ_ASSERT_IF(promise, promise->is<PromiseObject>());
+ MOZ_ASSERT_IF(promise, CheckedUnwrap(promise)->is<PromiseObject>());
return promise;
}
@@ -4896,7 +4894,7 @@ JS::CallOriginalPromiseReject(JSContext* cx, JS::HandleValue rejectionValue)
assertSameCompartment(cx, rejectionValue);
RootedObject promise(cx, PromiseObject::unforgeableReject(cx, rejectionValue));
- MOZ_ASSERT_IF(promise, promise->is<PromiseObject>());
+ MOZ_ASSERT_IF(promise, CheckedUnwrap(promise)->is<PromiseObject>());
return promise;
}
@@ -4926,8 +4924,8 @@ ResolveOrRejectPromise(JSContext* cx, JS::HandleObject promiseObj, JS::HandleVal
}
return reject
- ? promise->reject(cx, resultOrReason)
- : promise->resolve(cx, resultOrReason);
+ ? PromiseObject::reject(cx, promise, resultOrReason)
+ : PromiseObject::resolve(cx, promise, resultOrReason);
}
JS_PUBLIC_API(bool)
@@ -6014,7 +6012,8 @@ JS_SetRegExpInput(JSContext* cx, HandleObject obj, HandleString input)
CHECK_REQUEST(cx);
assertSameCompartment(cx, input);
- RegExpStatics* res = obj->as<GlobalObject>().getRegExpStatics(cx);
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
+ RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global);
if (!res)
return false;
@@ -6029,7 +6028,8 @@ JS_ClearRegExpStatics(JSContext* cx, HandleObject obj)
CHECK_REQUEST(cx);
MOZ_ASSERT(obj);
- RegExpStatics* res = obj->as<GlobalObject>().getRegExpStatics(cx);
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
+ RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global);
if (!res)
return false;
@@ -6044,7 +6044,8 @@ JS_ExecuteRegExp(JSContext* cx, HandleObject obj, HandleObject reobj, char16_t*
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
- RegExpStatics* res = obj->as<GlobalObject>().getRegExpStatics(cx);
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
+ RegExpStatics* res = GlobalObject::getRegExpStatics(cx, global);
if (!res)
return false;
@@ -6052,7 +6053,7 @@ JS_ExecuteRegExp(JSContext* cx, HandleObject obj, HandleObject reobj, char16_t*
if (!input)
return false;
- return ExecuteRegExpLegacy(cx, res, reobj->as<RegExpObject>(), input, indexp, test, rval);
+ return ExecuteRegExpLegacy(cx, res, reobj.as<RegExpObject>(), input, indexp, test, rval);
}
JS_PUBLIC_API(bool)
@@ -6066,7 +6067,7 @@ JS_ExecuteRegExpNoStatics(JSContext* cx, HandleObject obj, char16_t* chars, size
if (!input)
return false;
- return ExecuteRegExpLegacy(cx, nullptr, obj->as<RegExpObject>(), input, indexp, test,
+ return ExecuteRegExpLegacy(cx, nullptr, obj.as<RegExpObject>(), input, indexp, test,
rval);
}
@@ -6298,7 +6299,7 @@ JSErrorReport::freeLinebuf()
}
JSString*
-JSErrorReport::newMessageString(JSContext* cx)
+JSErrorBase::newMessageString(JSContext* cx)
{
if (!message_)
return cx->runtime()->emptyString;
@@ -6307,7 +6308,7 @@ JSErrorReport::newMessageString(JSContext* cx)
}
void
-JSErrorReport::freeMessage()
+JSErrorBase::freeMessage()
{
if (ownsMessage_) {
js_free((void*)message_.get());
@@ -6316,6 +6317,132 @@ JSErrorReport::freeMessage()
message_ = JS::ConstUTF8CharsZ();
}
+JSErrorNotes::JSErrorNotes()
+ : notes_()
+{}
+
+JSErrorNotes::~JSErrorNotes()
+{
+}
+
+static UniquePtr<JSErrorNotes::Note>
+CreateErrorNoteVA(ExclusiveContext* cx,
+ const char* filename, unsigned lineno, unsigned column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber,
+ ErrorArgumentsType argumentsType, va_list ap)
+{
+ auto note = MakeUnique<JSErrorNotes::Note>();
+ if (!note)
+ return nullptr;
+
+ note->errorNumber = errorNumber;
+ note->filename = filename;
+ note->lineno = lineno;
+ note->column = column;
+
+ if (!ExpandErrorArgumentsVA(cx, errorCallback, userRef, errorNumber,
+ nullptr, argumentsType, note.get(), ap)) {
+ return nullptr;
+ }
+
+ return note;
+}
+
+bool
+JSErrorNotes::addNoteASCII(ExclusiveContext* cx,
+ const char* filename, unsigned lineno, unsigned column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...)
+{
+ va_list ap;
+ va_start(ap, errorNumber);
+ auto note = CreateErrorNoteVA(cx, filename, lineno, column, errorCallback, userRef,
+ errorNumber, ArgumentsAreASCII, ap);
+ va_end(ap);
+
+ if (!note)
+ return false;
+ if (!notes_.append(Move(note)))
+ return false;
+ return true;
+}
+
+bool
+JSErrorNotes::addNoteLatin1(ExclusiveContext* cx,
+ const char* filename, unsigned lineno, unsigned column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...)
+{
+ va_list ap;
+ va_start(ap, errorNumber);
+ auto note = CreateErrorNoteVA(cx, filename, lineno, column, errorCallback, userRef,
+ errorNumber, ArgumentsAreLatin1, ap);
+ va_end(ap);
+
+ if (!note)
+ return false;
+ if (!notes_.append(Move(note)))
+ return false;
+ return true;
+}
+
+bool
+JSErrorNotes::addNoteUTF8(ExclusiveContext* cx,
+ const char* filename, unsigned lineno, unsigned column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...)
+{
+ va_list ap;
+ va_start(ap, errorNumber);
+ auto note = CreateErrorNoteVA(cx, filename, lineno, column, errorCallback, userRef,
+ errorNumber, ArgumentsAreUTF8, ap);
+ va_end(ap);
+
+ if (!note)
+ return false;
+ if (!notes_.append(Move(note)))
+ return false;
+ return true;
+}
+
+size_t
+JSErrorNotes::length()
+{
+ return notes_.length();
+}
+
+UniquePtr<JSErrorNotes>
+JSErrorNotes::copy(JSContext* cx)
+{
+ auto copiedNotes = MakeUnique<JSErrorNotes>();
+ if (!copiedNotes)
+ return nullptr;
+
+ for (auto&& note : *this) {
+ js::UniquePtr<JSErrorNotes::Note> copied(CopyErrorNote(cx, note.get()));
+ if (!copied)
+ return nullptr;
+
+ if (!copiedNotes->notes_.append(Move(copied)))
+ return nullptr;
+ }
+
+ return copiedNotes;
+}
+
+JSErrorNotes::iterator
+JSErrorNotes::begin()
+{
+ return iterator(notes_.begin());
+}
+
+JSErrorNotes::iterator
+JSErrorNotes::end()
+{
+ return iterator(notes_.end());
+}
+
JS_PUBLIC_API(bool)
JS_ThrowStopIteration(JSContext* cx)
{
diff --git a/js/src/jsapi.h b/js/src/jsapi.h
index 005d2278e..67b3d4267 100644
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -18,6 +18,7 @@
#include "mozilla/RefPtr.h"
#include "mozilla/Variant.h"
+#include <iterator>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
@@ -36,6 +37,7 @@
#include "js/Realm.h"
#include "js/RootingAPI.h"
#include "js/TracingAPI.h"
+#include "js/UniquePtr.h"
#include "js/Utility.h"
#include "js/Value.h"
#include "js/Vector.h"
@@ -652,6 +654,7 @@ typedef enum JSExnType {
JSEXN_WASMRUNTIMEERROR,
JSEXN_ERROR_LIMIT,
JSEXN_WARN = JSEXN_ERROR_LIMIT,
+ JSEXN_NOTE,
JSEXN_LIMIT
} JSExnType;
@@ -1093,7 +1096,6 @@ class JS_PUBLIC_API(ContextOptions) {
wasmAlwaysBaseline_(false),
throwOnAsmJSValidationFailure_(false),
nativeRegExp_(true),
- unboxedArrays_(false),
asyncStack_(true),
throwOnDebuggeeWouldRun_(true),
dumpStackOnDebuggeeWouldRun_(false),
@@ -1170,12 +1172,6 @@ class JS_PUBLIC_API(ContextOptions) {
return *this;
}
- bool unboxedArrays() const { return unboxedArrays_; }
- ContextOptions& setUnboxedArrays(bool flag) {
- unboxedArrays_ = flag;
- return *this;
- }
-
bool asyncStack() const { return asyncStack_; }
ContextOptions& setAsyncStack(bool flag) {
asyncStack_ = flag;
@@ -1238,7 +1234,6 @@ class JS_PUBLIC_API(ContextOptions) {
bool wasmAlwaysBaseline_ : 1;
bool throwOnAsmJSValidationFailure_ : 1;
bool nativeRegExp_ : 1;
- bool unboxedArrays_ : 1;
bool asyncStack_ : 1;
bool throwOnDebuggeeWouldRun_ : 1;
bool dumpStackOnDebuggeeWouldRun_ : 1;
@@ -5370,14 +5365,130 @@ JS_ReportOutOfMemory(JSContext* cx);
extern JS_PUBLIC_API(void)
JS_ReportAllocationOverflow(JSContext* cx);
-class JSErrorReport
+/**
+ * Base class that implements parts shared by JSErrorReport and
+ * JSErrorNotes::Note.
+ */
+class JSErrorBase
{
// The (default) error message.
// If ownsMessage_ is true, the it is freed in destructor.
JS::ConstUTF8CharsZ message_;
+ public:
+ JSErrorBase()
+ : filename(nullptr), lineno(0), column(0),
+ errorNumber(0),
+ ownsMessage_(false)
+ {}
+
+ ~JSErrorBase() {
+ freeMessage();
+ }
+
+ // Source file name, URL, etc., or null.
+ const char* filename;
+
+ // Source line number.
+ unsigned lineno;
+
+ // Zero-based column index in line.
+ unsigned column;
+
+ // the error number, e.g. see js.msg.
+ unsigned errorNumber;
+
+ private:
+ bool ownsMessage_ : 1;
+
+ public:
+ const JS::ConstUTF8CharsZ message() const {
+ return message_;
+ }
+
+ void initOwnedMessage(const char* messageArg) {
+ initBorrowedMessage(messageArg);
+ ownsMessage_ = true;
+ }
+ void initBorrowedMessage(const char* messageArg) {
+ MOZ_ASSERT(!message_);
+ message_ = JS::ConstUTF8CharsZ(messageArg, strlen(messageArg));
+ }
+
+ JSString* newMessageString(JSContext* cx);
+
+ private:
+ void freeMessage();
+};
+
+/**
+ * Notes associated with JSErrorReport.
+ */
+class JSErrorNotes
+{
+ public:
+ class Note : public JSErrorBase
+ {};
+
+ private:
+ // Stores pointers to each note.
+ js::Vector<js::UniquePtr<Note>, 1, js::SystemAllocPolicy> notes_;
+
+ public:
+ JSErrorNotes();
+ ~JSErrorNotes();
+
+ // Add an note to the given position.
+ bool addNoteASCII(js::ExclusiveContext* cx,
+ const char* filename, unsigned lineno, unsigned column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...);
+ bool addNoteLatin1(js::ExclusiveContext* cx,
+ const char* filename, unsigned lineno, unsigned column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...);
+ bool addNoteUTF8(js::ExclusiveContext* cx,
+ const char* filename, unsigned lineno, unsigned column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...);
+
+ size_t length();
+
+ // Create a deep copy of notes.
+ js::UniquePtr<JSErrorNotes> copy(JSContext* cx);
+
+ class iterator : public std::iterator<std::input_iterator_tag, js::UniquePtr<Note>>
+ {
+ js::UniquePtr<Note>* note_;
+ public:
+ explicit iterator(js::UniquePtr<Note>* note = nullptr) : note_(note)
+ {}
+
+ bool operator==(iterator other) const {
+ return note_ == other.note_;
+ }
+ bool operator!=(iterator other) const {
+ return !(*this == other);
+ }
+ iterator& operator++() {
+ note_++;
+ return *this;
+ }
+ reference operator*() {
+ return *note_;
+ }
+ };
+ iterator begin();
+ iterator end();
+};
+
+/**
+ * Describes a single error or warning that occurs in the execution of script.
+ */
+class JSErrorReport : public JSErrorBase
+{
// Offending source line without final '\n'.
- // If ownsLinebuf__ is true, the buffer is freed in destructor.
+ // If ownsLinebuf_ is true, the buffer is freed in destructor.
const char16_t* linebuf_;
// Number of chars in linebuf_. Does not include trailing '\0'.
@@ -5389,28 +5500,29 @@ class JSErrorReport
public:
JSErrorReport()
: linebuf_(nullptr), linebufLength_(0), tokenOffset_(0),
- filename(nullptr), lineno(0), column(0),
- flags(0), errorNumber(0),
- exnType(0), isMuted(false),
- ownsLinebuf_(false), ownsMessage_(false)
+ notes(nullptr),
+ flags(0), exnType(0), isMuted(false),
+ ownsLinebuf_(false)
{}
~JSErrorReport() {
freeLinebuf();
- freeMessage();
}
- const char* filename; /* source file name, URL, etc., or null */
- unsigned lineno; /* source line number */
- unsigned column; /* zero-based column index in line */
- unsigned flags; /* error/warning, etc. */
- unsigned errorNumber; /* the error number, e.g. see js.msg */
- int16_t exnType; /* One of the JSExnType constants */
- bool isMuted : 1; /* See the comment in ReadOnlyCompileOptions. */
+ // Associated notes, or nullptr if there's no note.
+ js::UniquePtr<JSErrorNotes> notes;
+
+ // error/warning, etc.
+ unsigned flags;
+
+ // One of the JSExnType constants.
+ int16_t exnType;
+
+ // See the comment in ReadOnlyCompileOptions.
+ bool isMuted : 1;
private:
bool ownsLinebuf_ : 1;
- bool ownsMessage_ : 1;
public:
const char16_t* linebuf() const {
@@ -5422,29 +5534,16 @@ class JSErrorReport
size_t tokenOffset() const {
return tokenOffset_;
}
- void initOwnedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg, size_t tokenOffsetArg) {
+ void initOwnedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg,
+ size_t tokenOffsetArg) {
initBorrowedLinebuf(linebufArg, linebufLengthArg, tokenOffsetArg);
ownsLinebuf_ = true;
}
- void initBorrowedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg, size_t tokenOffsetArg);
- void freeLinebuf();
+ void initBorrowedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg,
+ size_t tokenOffsetArg);
- const JS::ConstUTF8CharsZ message() const {
- return message_;
- }
-
- void initOwnedMessage(const char* messageArg) {
- initBorrowedMessage(messageArg);
- ownsMessage_ = true;
- }
- void initBorrowedMessage(const char* messageArg) {
- MOZ_ASSERT(!message_);
- message_ = JS::ConstUTF8CharsZ(messageArg, strlen(messageArg));
- }
-
- JSString* newMessageString(JSContext* cx);
-
- void freeMessage();
+ private:
+ void freeLinebuf();
};
/*
diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp
index 3b4c957dc..e618c319f 100644
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -45,7 +45,6 @@
#include "vm/Caches-inl.h"
#include "vm/Interpreter-inl.h"
#include "vm/NativeObject-inl.h"
-#include "vm/UnboxedObject-inl.h"
using namespace js;
using namespace js::gc;
@@ -64,7 +63,7 @@ using JS::ToUint32;
bool
JS::IsArray(JSContext* cx, HandleObject obj, IsArrayAnswer* answer)
{
- if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) {
+ if (obj->is<ArrayObject>()) {
*answer = IsArrayAnswer::Array;
return true;
}
@@ -100,11 +99,6 @@ js::GetLengthProperty(JSContext* cx, HandleObject obj, uint32_t* lengthp)
return true;
}
- if (obj->is<UnboxedArrayObject>()) {
- *lengthp = obj->as<UnboxedArrayObject>().length();
- return true;
- }
-
if (obj->is<ArgumentsObject>()) {
ArgumentsObject& argsobj = obj->as<ArgumentsObject>();
if (!argsobj.hasOverriddenLength()) {
@@ -253,18 +247,20 @@ static bool
GetElement(JSContext* cx, HandleObject obj, HandleObject receiver,
uint32_t index, bool* hole, MutableHandleValue vp)
{
- AssertGreaterThanZero(index);
- if (index < GetAnyBoxedOrUnboxedInitializedLength(obj)) {
- vp.set(GetAnyBoxedOrUnboxedDenseElement(obj, uint32_t(index)));
- if (!vp.isMagic(JS_ELEMENTS_HOLE)) {
- *hole = false;
- return true;
+ if (obj->isNative()) {
+ NativeObject* nobj = &obj->as<NativeObject>();
+ if (index < nobj->getDenseInitializedLength()) {
+ vp.set(nobj->getDenseElement(size_t(index)));
+ if (!vp.isMagic(JS_ELEMENTS_HOLE)) {
+ *hole = false;
+ return true;
+ }
}
- }
- if (obj->is<ArgumentsObject>()) {
- if (obj->as<ArgumentsObject>().maybeGetElement(uint32_t(index), vp)) {
- *hole = false;
- return true;
+ if (nobj->is<ArgumentsObject>() && index <= UINT32_MAX) {
+ if (nobj->as<ArgumentsObject>().maybeGetElement(uint32_t(index), vp)) {
+ *hole = false;
+ return true;
+ }
}
}
@@ -283,8 +279,8 @@ ElementAdder::append(JSContext* cx, HandleValue v)
{
MOZ_ASSERT(index_ < length_);
if (resObj_) {
- DenseElementResult result =
- SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, resObj_, index_, v.address(), 1);
+ NativeObject* resObj = &resObj_->as<NativeObject>();
+ DenseElementResult result = resObj->setOrExtendDenseElements(cx, index_, v.address(), 1);
if (result == DenseElementResult::Failure)
return false;
if (result == DenseElementResult::Incomplete) {
@@ -336,37 +332,31 @@ js::GetElementsWithAdder(JSContext* cx, HandleObject obj, HandleObject receiver,
return true;
}
-template <JSValueType Type>
-DenseElementResult
-GetBoxedOrUnboxedDenseElements(JSObject* aobj, uint32_t length, Value* vp)
+static bool
+GetDenseElements(NativeObject* aobj, uint32_t length, Value* vp)
{
MOZ_ASSERT(!ObjectMayHaveExtraIndexedProperties(aobj));
- if (length > GetBoxedOrUnboxedInitializedLength<Type>(aobj))
- return DenseElementResult::Incomplete;
+ if (length > aobj->getDenseInitializedLength())
+ return false;
for (size_t i = 0; i < length; i++) {
- vp[i] = GetBoxedOrUnboxedDenseElement<Type>(aobj, i);
+ vp[i] = aobj->getDenseElement(i);
// No other indexed properties so hole => undefined.
if (vp[i].isMagic(JS_ELEMENTS_HOLE))
vp[i] = UndefinedValue();
}
- return DenseElementResult::Success;
+ return true;
}
-DefineBoxedOrUnboxedFunctor3(GetBoxedOrUnboxedDenseElements,
- JSObject*, uint32_t, Value*);
-
bool
js::GetElements(JSContext* cx, HandleObject aobj, uint32_t length, Value* vp)
{
if (!ObjectMayHaveExtraIndexedProperties(aobj)) {
- GetBoxedOrUnboxedDenseElementsFunctor functor(aobj, length, vp);
- DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, aobj);
- if (result != DenseElementResult::Incomplete)
- return result == DenseElementResult::Success;
+ if (GetDenseElements(&aobj->as<NativeObject>(), length, vp))
+ return true;
}
if (aobj->is<ArgumentsObject>()) {
@@ -398,9 +388,9 @@ SetArrayElement(JSContext* cx, HandleObject obj, double index, HandleValue v)
{
MOZ_ASSERT(index >= 0);
- if ((obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) && !obj->isIndexed() && index <= UINT32_MAX) {
- DenseElementResult result =
- SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, uint32_t(index), v.address(), 1);
+ if (obj->is<ArrayObject>() && !obj->isIndexed() && index <= UINT32_MAX) {
+ NativeObject* nobj = &obj->as<NativeObject>();
+ DenseElementResult result = nobj->setOrExtendDenseElements(cx, uint32_t(index), v.address(), 1);
if (result != DenseElementResult::Incomplete)
return result == DenseElementResult::Success;
}
@@ -520,24 +510,6 @@ struct ReverseIndexComparator
}
};
-bool
-js::CanonicalizeArrayLengthValue(JSContext* cx, HandleValue v, uint32_t* newLen)
-{
- double d;
-
- if (!ToUint32(cx, v, newLen))
- return false;
-
- if (!ToNumber(cx, v, &d))
- return false;
-
- if (d == *newLen)
- return true;
-
- JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
- return false;
-}
-
/* ES6 draft rev 34 (2015 Feb 20) 9.4.2.4 ArraySetLength */
bool
js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id,
@@ -559,12 +531,22 @@ js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id,
} else {
// Step 2 is irrelevant in our implementation.
- // Steps 3-7.
- MOZ_ASSERT_IF(attrs & JSPROP_IGNORE_VALUE, value.isUndefined());
- if (!CanonicalizeArrayLengthValue(cx, value, &newLen))
+ // Step 3.
+ if (!ToUint32(cx, value, &newLen))
return false;
- // Step 8 is irrelevant in our implementation.
+ // Step 4.
+ double d;
+ if (!ToNumber(cx, value, &d))
+ return false;
+
+ // Step 5.
+ if (d != newLen) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
+ return false;
+ }
+
+ // Steps 6-8 are irrelevant in our implementation.
}
// Steps 9-11.
@@ -620,7 +602,7 @@ js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id,
// for..in iteration over the array. Keys deleted before being reached
// during the iteration must not be visited, and suppressing them here
// would be too costly.
- ObjectGroup* arrGroup = arr->getGroup(cx);
+ ObjectGroup* arrGroup = JSObject::getGroup(cx, arr);
if (MOZ_UNLIKELY(!arrGroup))
return false;
if (!arr->isIndexed() && !MOZ_UNLIKELY(arrGroup->hasAllFlags(OBJECT_FLAG_ITERATED))) {
@@ -823,7 +805,7 @@ array_addProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
static inline bool
ObjectMayHaveExtraIndexedOwnProperties(JSObject* obj)
{
- return (!obj->isNative() && !obj->is<UnboxedArrayObject>()) ||
+ return !obj->isNative() ||
obj->isIndexed() ||
obj->is<TypedArrayObject>() ||
ClassMayResolveId(*obj->runtimeFromAnyThread()->commonNames,
@@ -854,7 +836,7 @@ js::ObjectMayHaveExtraIndexedProperties(JSObject* obj)
if (ObjectMayHaveExtraIndexedOwnProperties(obj))
return true;
- if (GetAnyBoxedOrUnboxedInitializedLength(obj) != 0)
+ if (obj->as<NativeObject>().getDenseInitializedLength() != 0)
return true;
} while (true);
}
@@ -1064,31 +1046,32 @@ struct StringSeparatorOp
}
};
-template <typename SeparatorOp, JSValueType Type>
-static DenseElementResult
-ArrayJoinDenseKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_t length,
+template <typename SeparatorOp>
+static bool
+ArrayJoinDenseKernel(JSContext* cx, SeparatorOp sepOp, HandleNativeObject obj, uint32_t length,
StringBuffer& sb, uint32_t* numProcessed)
{
// This loop handles all elements up to initializedLength. If
// length > initLength we rely on the second loop to add the
// other elements.
MOZ_ASSERT(*numProcessed == 0);
- uint32_t initLength = Min<uint32_t>(GetBoxedOrUnboxedInitializedLength<Type>(obj), length);
+ uint32_t initLength = Min<uint32_t>(obj->getDenseInitializedLength(),
+ length);
while (*numProcessed < initLength) {
if (!CheckForInterrupt(cx))
- return DenseElementResult::Failure;
+ return false;
- Value elem = GetBoxedOrUnboxedDenseElement<Type>(obj, *numProcessed);
+ Value elem = obj->as<NativeObject>().getDenseElement(*numProcessed);
if (elem.isString()) {
if (!sb.append(elem.toString()))
- return DenseElementResult::Failure;
+ return false;
} else if (elem.isNumber()) {
if (!NumberValueToStringBuffer(cx, elem, sb))
- return DenseElementResult::Failure;
+ return false;
} else if (elem.isBoolean()) {
if (!BooleanToStringBuffer(elem.toBoolean(), sb))
- return DenseElementResult::Failure;
+ return false;
} else if (elem.isObject() || elem.isSymbol()) {
/*
* Object stringifying could modify the initialized length or make
@@ -1104,33 +1087,13 @@ ArrayJoinDenseKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_
}
if (++(*numProcessed) != length && !sepOp(cx, sb))
- return DenseElementResult::Failure;
+ return false;
}
- return DenseElementResult::Incomplete;
+ return true;
}
template <typename SeparatorOp>
-struct ArrayJoinDenseKernelFunctor {
- JSContext* cx;
- SeparatorOp sepOp;
- HandleObject obj;
- uint32_t length;
- StringBuffer& sb;
- uint32_t* numProcessed;
-
- ArrayJoinDenseKernelFunctor(JSContext* cx, SeparatorOp sepOp, HandleObject obj,
- uint32_t length, StringBuffer& sb, uint32_t* numProcessed)
- : cx(cx), sepOp(sepOp), obj(obj), length(length), sb(sb), numProcessed(numProcessed)
- {}
-
- template <JSValueType Type>
- DenseElementResult operator()() {
- return ArrayJoinDenseKernel<SeparatorOp, Type>(cx, sepOp, obj, length, sb, numProcessed);
- }
-};
-
-template <typename SeparatorOp>
static bool
ArrayJoinKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_t length,
StringBuffer& sb)
@@ -1138,10 +1101,10 @@ ArrayJoinKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_t len
uint32_t i = 0;
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
- ArrayJoinDenseKernelFunctor<SeparatorOp> functor(cx, sepOp, obj, length, sb, &i);
- DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
- if (result == DenseElementResult::Failure)
+ if (!ArrayJoinDenseKernel<SeparatorOp>(cx, sepOp, obj.as<NativeObject>(), length, sb, &i))
+ {
return false;
+ }
}
if (i != length) {
@@ -1212,11 +1175,14 @@ js::array_join(JSContext* cx, unsigned argc, Value* vp)
// An optimized version of a special case of steps 7-11: when length==1 and
// the 0th element is a string, ToString() of that element is a no-op and
// so it can be immediately returned as the result.
- if (length == 1 && GetAnyBoxedOrUnboxedInitializedLength(obj) == 1) {
- Value elem0 = GetAnyBoxedOrUnboxedDenseElement(obj, 0);
- if (elem0.isString()) {
- args.rval().set(elem0);
- return true;
+ if (length == 1 && obj->isNative()) {
+ NativeObject* nobj = &obj->as<NativeObject>();
+ if (nobj->getDenseInitializedLength() == 1) {
+ Value elem0 = nobj->getDenseElement(0);
+ if (elem0.isString()) {
+ args.rval().set(elem0);
+ return true;
+ }
}
}
@@ -1259,7 +1225,7 @@ js::array_join(JSContext* cx, unsigned argc, Value* vp)
}
// Step 11
- JSString *str = sb.finishString();
+ JSString* str = sb.finishString();
if (!str)
return false;
@@ -1288,10 +1254,6 @@ array_toLocaleString(JSContext* cx, unsigned argc, Value* vp)
args.rval().setString(cx->names().empty);
return true;
}
- if (obj->is<UnboxedArrayObject>() && obj->as<UnboxedArrayObject>().length() == 0) {
- args.rval().setString(cx->names().empty);
- return true;
- }
AutoCycleDetector detector(cx, obj);
if (!detector.init())
@@ -1323,13 +1285,14 @@ InitArrayElements(JSContext* cx, HandleObject obj, uint32_t start,
if (count == 0)
return true;
- ObjectGroup* group = obj->getGroup(cx);
+ ObjectGroup* group = JSObject::getGroup(cx, obj);
if (!group)
return false;
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
- DenseElementResult result =
- SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, start, vector, count, updateTypes);
+ NativeObject* nobj = &obj->as<NativeObject>();
+ DenseElementResult result = nobj->setOrExtendDenseElements(cx, uint32_t(start), vector,
+ count, updateTypes);
if (result != DenseElementResult::Incomplete)
return result == DenseElementResult::Success;
}
@@ -1363,54 +1326,45 @@ InitArrayElements(JSContext* cx, HandleObject obj, uint32_t start,
return true;
}
-template <JSValueType Type>
-DenseElementResult
-ArrayReverseDenseKernel(JSContext* cx, HandleObject obj, uint32_t length)
+static DenseElementResult
+ArrayReverseDenseKernel(JSContext* cx, HandleNativeObject obj, uint32_t length)
{
/* An empty array or an array with no elements is already reversed. */
- if (length == 0 || GetBoxedOrUnboxedInitializedLength<Type>(obj) == 0)
+ if (length == 0 || obj->getDenseInitializedLength() == 0)
return DenseElementResult::Success;
- if (Type == JSVAL_TYPE_MAGIC) {
- if (obj->as<NativeObject>().denseElementsAreFrozen())
- return DenseElementResult::Incomplete;
+ if (obj->denseElementsAreFrozen())
+ return DenseElementResult::Incomplete;
- /*
- * It's actually surprisingly complicated to reverse an array due to the
- * orthogonality of array length and array capacity while handling
- * leading and trailing holes correctly. Reversing seems less likely to
- * be a common operation than other array mass-mutation methods, so for
- * now just take a probably-small memory hit (in the absence of too many
- * holes in the array at its start) and ensure that the capacity is
- * sufficient to hold all the elements in the array if it were full.
- */
- DenseElementResult result = obj->as<NativeObject>().ensureDenseElements(cx, length, 0);
- if (result != DenseElementResult::Success)
- return result;
+ /*
+ * It's actually surprisingly complicated to reverse an array due to the
+ * orthogonality of array length and array capacity while handling
+ * leading and trailing holes correctly. Reversing seems less likely to
+ * be a common operation than other array mass-mutation methods, so for
+ * now just take a probably-small memory hit (in the absence of too many
+ * holes in the array at its start) and ensure that the capacity is
+ * sufficient to hold all the elements in the array if it were full.
+ */
+ DenseElementResult result = obj->ensureDenseElements(cx, length, 0);
+ if (result != DenseElementResult::Success)
+ return result;
- /* Fill out the array's initialized length to its proper length. */
- obj->as<NativeObject>().ensureDenseInitializedLength(cx, length, 0);
- } else {
- // Unboxed arrays can only be reversed here if their initialized length
- // matches their actual length. Otherwise the reversal will place holes
- // at the beginning of the array, which we don't support.
- if (length != obj->as<UnboxedArrayObject>().initializedLength())
- return DenseElementResult::Incomplete;
- }
+ /* Fill out the array's initialized length to its proper length. */
+ obj->ensureDenseInitializedLength(cx, length, 0);
RootedValue origlo(cx), orighi(cx);
uint32_t lo = 0, hi = length - 1;
for (; lo < hi; lo++, hi--) {
- origlo = GetBoxedOrUnboxedDenseElement<Type>(obj, lo);
- orighi = GetBoxedOrUnboxedDenseElement<Type>(obj, hi);
- SetBoxedOrUnboxedDenseElementNoTypeChange<Type>(obj, lo, orighi);
+ origlo = obj->getDenseElement(lo);
+ orighi = obj->getDenseElement(hi);
+ obj->setDenseElement(lo, orighi);
if (orighi.isMagic(JS_ELEMENTS_HOLE) &&
!SuppressDeletedProperty(cx, obj, INT_TO_JSID(lo)))
{
return DenseElementResult::Failure;
}
- SetBoxedOrUnboxedDenseElementNoTypeChange<Type>(obj, hi, origlo);
+ obj->setDenseElement(hi, origlo);
if (origlo.isMagic(JS_ELEMENTS_HOLE) &&
!SuppressDeletedProperty(cx, obj, INT_TO_JSID(hi)))
{
@@ -1421,9 +1375,6 @@ ArrayReverseDenseKernel(JSContext* cx, HandleObject obj, uint32_t length)
return DenseElementResult::Success;
}
-DefineBoxedOrUnboxedFunctor3(ArrayReverseDenseKernel,
- JSContext*, HandleObject, uint32_t);
-
bool
js::array_reverse(JSContext* cx, unsigned argc, Value* vp)
{
@@ -1438,8 +1389,8 @@ js::array_reverse(JSContext* cx, unsigned argc, Value* vp)
return false;
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
- ArrayReverseDenseKernelFunctor functor(cx, obj, len);
- DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
+ DenseElementResult result =
+ ArrayReverseDenseKernel(cx, obj.as<NativeObject>(), uint32_t(len));
if (result != DenseElementResult::Incomplete) {
/*
* Per ECMA-262, don't update the length of the array, even if the new
@@ -1711,11 +1662,11 @@ MatchNumericComparator(JSContext* cx, const Value& v)
if (!obj.is<JSFunction>())
return Match_None;
- JSFunction* fun = &obj.as<JSFunction>();
+ RootedFunction fun(cx, &obj.as<JSFunction>());
if (!fun->isInterpreted() || fun->isClassConstructor())
return Match_None;
- JSScript* script = fun->getOrCreateScript(cx);
+ JSScript* script = JSFunction::getOrCreateScript(cx, fun);
if (!script)
return Match_Failure;
@@ -2082,8 +2033,8 @@ js::array_push(JSContext* cx, unsigned argc, Value* vp)
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
DenseElementResult result =
- SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, length,
- args.array(), args.length());
+ obj->as<NativeObject>().setOrExtendDenseElements(cx, uint32_t(length),
+ args.array(), args.length());
if (result != DenseElementResult::Incomplete) {
if (result == DenseElementResult::Failure)
return false;
@@ -2091,14 +2042,8 @@ js::array_push(JSContext* cx, unsigned argc, Value* vp)
uint32_t newlength = length + args.length();
args.rval().setNumber(newlength);
- // SetOrExtendAnyBoxedOrUnboxedDenseElements takes care of updating the
- // length for boxed and unboxed arrays. Handle updates to the length of
- // non-arrays here.
- bool isArray;
- if (!IsArray(cx, obj, &isArray))
- return false;
-
- if (!isArray)
+ // Handle updates to the length of non-arrays here.
+ if (!obj->is<ArrayObject>())
return SetLengthProperty(cx, obj, newlength);
return true;
@@ -2154,73 +2099,74 @@ js::array_pop(JSContext* cx, unsigned argc, Value* vp)
return SetLengthProperty(cx, obj, index);
}
-template <JSValueType Type>
-static inline DenseElementResult
-ShiftMoveBoxedOrUnboxedDenseElements(JSObject* obj)
+void
+js::ArrayShiftMoveElements(NativeObject* obj)
{
- MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(obj));
+ MOZ_ASSERT_IF(obj->is<ArrayObject>(), obj->as<ArrayObject>().lengthIsWritable());
+
+ size_t initlen = obj->getDenseInitializedLength();
+ MOZ_ASSERT(initlen > 0);
/*
* At this point the length and initialized length have already been
* decremented and the result fetched, so just shift the array elements
* themselves.
*/
- size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
- if (Type == JSVAL_TYPE_MAGIC) {
- obj->as<NativeObject>().moveDenseElementsNoPreBarrier(0, 1, initlen);
- } else {
- uint8_t* data = obj->as<UnboxedArrayObject>().elements();
- size_t elementSize = UnboxedTypeSize(Type);
- memmove(data, data + elementSize, initlen * elementSize);
- }
-
- return DenseElementResult::Success;
+ obj->moveDenseElementsNoPreBarrier(0, 1, initlen);
}
-DefineBoxedOrUnboxedFunctor1(ShiftMoveBoxedOrUnboxedDenseElements, JSObject*);
+static inline void
+SetInitializedLength(JSContext* cx, NativeObject* obj, size_t initlen)
+{
+ size_t oldInitlen = obj->getDenseInitializedLength();
+ obj->setDenseInitializedLength(initlen);
+ if (initlen < oldInitlen)
+ obj->shrinkElements(cx, initlen);
+}
-void
-js::ArrayShiftMoveElements(JSObject* obj)
+static DenseElementResult
+MoveDenseElements(JSContext* cx, NativeObject* obj, uint32_t dstStart, uint32_t srcStart,
+ uint32_t length)
{
- MOZ_ASSERT_IF(obj->is<ArrayObject>(), obj->as<ArrayObject>().lengthIsWritable());
+ if (obj->denseElementsAreFrozen())
+ return DenseElementResult::Incomplete;
+
+ if (!obj->maybeCopyElementsForWrite(cx))
+ return DenseElementResult::Failure;
+ obj->moveDenseElements(dstStart, srcStart, length);
- ShiftMoveBoxedOrUnboxedDenseElementsFunctor functor(obj);
- JS_ALWAYS_TRUE(CallBoxedOrUnboxedSpecialization(functor, obj) == DenseElementResult::Success);
+ return DenseElementResult::Success;
}
-template <JSValueType Type>
-DenseElementResult
+static DenseElementResult
ArrayShiftDenseKernel(JSContext* cx, HandleObject obj, MutableHandleValue rval)
{
if (ObjectMayHaveExtraIndexedProperties(obj))
return DenseElementResult::Incomplete;
- RootedObjectGroup group(cx, obj->getGroup(cx));
+ RootedObjectGroup group(cx, JSObject::getGroup(cx, obj));
if (MOZ_UNLIKELY(!group))
return DenseElementResult::Failure;
if (MOZ_UNLIKELY(group->hasAllFlags(OBJECT_FLAG_ITERATED)))
return DenseElementResult::Incomplete;
- size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
+ size_t initlen = obj->as<NativeObject>().getDenseInitializedLength();
if (initlen == 0)
return DenseElementResult::Incomplete;
- rval.set(GetBoxedOrUnboxedDenseElement<Type>(obj, 0));
+ rval.set(obj->as<NativeObject>().getDenseElement(0));
if (rval.isMagic(JS_ELEMENTS_HOLE))
rval.setUndefined();
- DenseElementResult result = MoveBoxedOrUnboxedDenseElements<Type>(cx, obj, 0, 1, initlen - 1);
+ DenseElementResult result = MoveDenseElements(cx, &obj->as<NativeObject>(), 0, 1, initlen - 1);
if (result != DenseElementResult::Success)
return result;
- SetBoxedOrUnboxedInitializedLength<Type>(cx, obj, initlen - 1);
+ SetInitializedLength(cx, obj.as<NativeObject>(), initlen - 1);
return DenseElementResult::Success;
}
-DefineBoxedOrUnboxedFunctor3(ArrayShiftDenseKernel,
- JSContext*, HandleObject, MutableHandleValue);
-
/* ES5 15.4.4.9 */
bool
js::array_shift(JSContext* cx, unsigned argc, Value* vp)
@@ -2252,8 +2198,7 @@ js::array_shift(JSContext* cx, unsigned argc, Value* vp)
uint32_t newlen = len - 1;
/* Fast paths. */
- ArrayShiftDenseKernelFunctor functor(cx, obj, args.rval());
- DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
+ DenseElementResult result = ArrayShiftDenseKernel(cx, obj, args.rval());
if (result != DenseElementResult::Incomplete) {
if (result == DenseElementResult::Failure)
return false;
@@ -2307,9 +2252,6 @@ js::array_unshift(JSContext* cx, unsigned argc, Value* vp)
if (args.length() > 0) {
/* Slide up the array to make room for all args at the bottom. */
if (length > 0) {
- // Only include a fast path for boxed arrays. Unboxed arrays can'nt
- // be optimized here because unshifting temporarily places holes at
- // the start of the array.
bool optimized = false;
do {
if (!obj->is<ArrayObject>())
@@ -2369,10 +2311,10 @@ js::array_unshift(JSContext* cx, unsigned argc, Value* vp)
}
/*
- * Returns true if this is a dense or unboxed array whose |count| properties
- * starting from |startingIndex| may be accessed (get, set, delete) directly
- * through its contiguous vector of elements without fear of getters, setters,
- * etc. along the prototype chain, or of enumerators requiring notification of
+ * Returns true if this is a dense array whose properties ending at |endIndex|
+ * (exclusive) may be accessed (get, set, delete) directly through its
+ * contiguous vector of elements without fear of getters, setters, etc. along
+ * the prototype chain, or of enumerators requiring notification of
* modifications.
*/
static inline bool
@@ -2383,11 +2325,11 @@ CanOptimizeForDenseStorage(HandleObject arr, uint32_t startingIndex, uint32_t co
return false;
/* There's no optimizing possible if it's not an array. */
- if (!arr->is<ArrayObject>() && !arr->is<UnboxedArrayObject>())
+ if (!arr->is<ArrayObject>())
return false;
/* If it's a frozen array, always pick the slow path */
- if (arr->is<ArrayObject>() && arr->as<ArrayObject>().denseElementsAreFrozen())
+ if (arr->as<ArrayObject>().denseElementsAreFrozen())
return false;
/*
@@ -2398,7 +2340,7 @@ CanOptimizeForDenseStorage(HandleObject arr, uint32_t startingIndex, uint32_t co
* deleted if a hole is moved from one location to another location not yet
* visited. See bug 690622.
*/
- ObjectGroup* arrGroup = arr->getGroup(cx);
+ ObjectGroup* arrGroup = JSObject::getGroup(cx, arr);
if (!arrGroup) {
cx->recoverFromOutOfMemory();
return false;
@@ -2419,14 +2361,23 @@ CanOptimizeForDenseStorage(HandleObject arr, uint32_t startingIndex, uint32_t co
* is subsumed by the initializedLength comparison.)
*/
return !ObjectMayHaveExtraIndexedProperties(arr) &&
- startingIndex + count <= GetAnyBoxedOrUnboxedInitializedLength(arr);
+ startingIndex + count <= arr->as<NativeObject>().getDenseInitializedLength();
}
-/* ES 2016 draft Mar 25, 2016 22.1.3.26. */
-bool
-js::array_splice(JSContext* cx, unsigned argc, Value* vp)
+static inline DenseElementResult
+CopyDenseElements(JSContext* cx, NativeObject* dst, NativeObject* src,
+ uint32_t dstStart, uint32_t srcStart, uint32_t length)
{
- return array_splice_impl(cx, argc, vp, true);
+ MOZ_ASSERT(dst->getDenseInitializedLength() == dstStart);
+ MOZ_ASSERT(src->getDenseInitializedLength() >= srcStart + length);
+ MOZ_ASSERT(dst->getDenseCapacity() >= dstStart + length);
+
+ dst->setDenseInitializedLength(dstStart + length);
+
+ const Value* vp = src->getDenseElements() + srcStart;
+ dst->initDenseElements(dstStart, vp, length);
+
+ return DenseElementResult::Success;
}
static inline bool
@@ -2458,8 +2409,8 @@ ArraySpliceCopy(JSContext* cx, HandleObject arr, HandleObject obj,
return SetLengthProperty(cx, arr, actualDeleteCount);
}
-bool
-js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueIsUsed)
+static bool
+array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueIsUsed)
{
AutoSPSEntry pseudoFrame(cx->runtime(), "Array.prototype.splice");
CallArgs args = CallArgsFromVp(argc, vp);
@@ -2520,7 +2471,9 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI
/* Steps 10-11. */
DebugOnly<DenseElementResult> result =
- CopyAnyBoxedOrUnboxedDenseElements(cx, arr, obj, 0, actualStart, actualDeleteCount);
+ CopyDenseElements(cx, &arr->as<NativeObject>(),
+ &obj->as<NativeObject>(), 0,
+ actualStart, actualDeleteCount);
MOZ_ASSERT(result.value == DenseElementResult::Success);
/* Step 12 (implicit). */
@@ -2557,14 +2510,13 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI
if (CanOptimizeForDenseStorage(obj, 0, len, cx)) {
/* Steps 15.a-b. */
DenseElementResult result =
- MoveAnyBoxedOrUnboxedDenseElements(cx, obj, targetIndex, sourceIndex,
- len - sourceIndex);
+ MoveDenseElements(cx, &obj->as<NativeObject>(), targetIndex, sourceIndex, len - sourceIndex);
MOZ_ASSERT(result != DenseElementResult::Incomplete);
if (result == DenseElementResult::Failure)
return false;
/* Steps 15.c-d. */
- SetAnyBoxedOrUnboxedInitializedLength(cx, obj, finalLength);
+ SetInitializedLength(cx, obj.as<NativeObject>(), finalLength);
} else {
/*
* This is all very slow if the length is very large. We don't yet
@@ -2644,15 +2596,15 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI
if (CanOptimizeForDenseStorage(obj, len, itemCount - actualDeleteCount, cx)) {
DenseElementResult result =
- MoveAnyBoxedOrUnboxedDenseElements(cx, obj, actualStart + itemCount,
- actualStart + actualDeleteCount,
- len - (actualStart + actualDeleteCount));
+ MoveDenseElements(cx, &obj->as<NativeObject>(), actualStart + itemCount,
+ actualStart + actualDeleteCount,
+ len - (actualStart + actualDeleteCount));
MOZ_ASSERT(result != DenseElementResult::Incomplete);
if (result == DenseElementResult::Failure)
return false;
/* Steps 16.a-b. */
- SetAnyBoxedOrUnboxedInitializedLength(cx, obj, len + itemCount - actualDeleteCount);
+ SetInitializedLength(cx, obj.as<NativeObject>(), len + itemCount - actualDeleteCount);
} else {
RootedValue fromValue(cx);
for (double k = len - actualDeleteCount; k > actualStart; k--) {
@@ -2708,6 +2660,19 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI
return true;
}
+/* ES 2016 draft Mar 25, 2016 22.1.3.26. */
+bool
+js::array_splice(JSContext* cx, unsigned argc, Value* vp)
+{
+ return array_splice_impl(cx, argc, vp, true);
+}
+
+static bool
+array_splice_noRetVal(JSContext* cx, unsigned argc, Value* vp)
+{
+ return array_splice_impl(cx, argc, vp, false);
+}
+
struct SortComparatorIndexes
{
bool operator()(uint32_t a, uint32_t b, bool* lessOrEqualp) {
@@ -2824,7 +2789,7 @@ SliceSlowly(JSContext* cx, HandleObject obj, HandleObject receiver,
}
static bool
-SliceSparse(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end, HandleObject result)
+SliceSparse(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end, HandleArrayObject result)
{
MOZ_ASSERT(begin <= end);
@@ -2874,26 +2839,28 @@ ArraySliceOrdinary(JSContext* cx, HandleObject obj, uint32_t length, uint32_t be
begin = end;
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
- size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(obj);
+ size_t initlen = obj->as<NativeObject>().getDenseInitializedLength();
size_t count = 0;
if (initlen > begin)
count = Min<size_t>(initlen - begin, end - begin);
- RootedObject narr(cx, NewFullyAllocatedArrayTryReuseGroup(cx, obj, count));
+ RootedArrayObject narr(cx, NewFullyAllocatedArrayTryReuseGroup(cx, obj, count));
if (!narr)
return false;
- SetAnyBoxedOrUnboxedArrayLength(cx, narr, end - begin);
+
+ MOZ_ASSERT(count >= narr->as<ArrayObject>().length());
+ narr->as<ArrayObject>().setLength(cx, count);
if (count) {
DebugOnly<DenseElementResult> result =
- CopyAnyBoxedOrUnboxedDenseElements(cx, narr, obj, 0, begin, count);
+ CopyDenseElements(cx, &narr->as<NativeObject>(), &obj->as<NativeObject>(), 0, begin, count);
MOZ_ASSERT(result.value == DenseElementResult::Success);
}
arr.set(narr);
return true;
}
- RootedObject narr(cx, NewPartlyAllocatedArrayTryReuseGroup(cx, obj, end - begin));
+ RootedArrayObject narr(cx, NewPartlyAllocatedArrayTryReuseGroup(cx, obj, end - begin));
if (!narr)
return false;
@@ -3010,11 +2977,10 @@ js::array_slice(JSContext* cx, unsigned argc, Value* vp)
return true;
}
-template <JSValueType Type>
-DenseElementResult
-ArraySliceDenseKernel(JSContext* cx, JSObject* obj, int32_t beginArg, int32_t endArg, JSObject* result)
+static bool
+ArraySliceDenseKernel(JSContext* cx, ArrayObject* arr, int32_t beginArg, int32_t endArg, ArrayObject* result)
{
- int32_t length = GetAnyBoxedOrUnboxedArrayLength(obj);
+ int32_t length = arr->length();
uint32_t begin = NormalizeSliceTerm(beginArg, length);
uint32_t end = NormalizeSliceTerm(endArg, length);
@@ -3022,33 +2988,33 @@ ArraySliceDenseKernel(JSContext* cx, JSObject* obj, int32_t beginArg, int32_t en
if (begin > end)
begin = end;
- size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
+ size_t initlen = arr->getDenseInitializedLength();
+ size_t count = Min<size_t>(initlen - begin, end - begin);
if (initlen > begin) {
- size_t count = Min<size_t>(initlen - begin, end - begin);
if (count) {
- DenseElementResult rv = EnsureBoxedOrUnboxedDenseElements<Type>(cx, result, count);
- if (rv != DenseElementResult::Success)
- return rv;
- CopyBoxedOrUnboxedDenseElements<Type, Type>(cx, result, obj, 0, begin, count);
+ if (!result->ensureElements(cx, count))
+ return false;
+ CopyDenseElements(cx, &result->as<NativeObject>(), &arr->as<NativeObject>(), 0, begin, count);
}
}
- SetAnyBoxedOrUnboxedArrayLength(cx, result, end - begin);
- return DenseElementResult::Success;
-}
+ MOZ_ASSERT(count >= result->length());
+ result->setLength(cx, count);
-DefineBoxedOrUnboxedFunctor5(ArraySliceDenseKernel,
- JSContext*, JSObject*, int32_t, int32_t, JSObject*);
+ return true;
+}
JSObject*
js::array_slice_dense(JSContext* cx, HandleObject obj, int32_t begin, int32_t end,
HandleObject result)
{
if (result && IsArraySpecies(cx, obj)) {
- ArraySliceDenseKernelFunctor functor(cx, obj, begin, end, result);
- DenseElementResult rv = CallBoxedOrUnboxedSpecialization(functor, result);
- MOZ_ASSERT(rv != DenseElementResult::Incomplete);
- return rv == DenseElementResult::Success ? result : nullptr;
+ if (!ArraySliceDenseKernel(cx, &obj->as<ArrayObject>(), begin, end,
+ &result->as<ArrayObject>()))
+ {
+ return nullptr;
+ }
+ return result;
}
// Slower path if the JIT wasn't able to allocate an object inline.
@@ -3079,7 +3045,7 @@ array_isArray(JSContext* cx, unsigned argc, Value* vp)
static bool
ArrayFromCallArgs(JSContext* cx, CallArgs& args, HandleObject proto = nullptr)
{
- JSObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length(), proto);
+ ArrayObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length(), proto);
if (!obj)
return false;
@@ -3124,6 +3090,15 @@ array_of(JSContext* cx, unsigned argc, Value* vp)
return true;
}
+const JSJitInfo js::array_splice_info = {
+ { (JSJitGetterOp)array_splice_noRetVal },
+ { 0 }, /* unused */
+ { 0 }, /* unused */
+ JSJitInfo::IgnoresReturnValueNative,
+ JSJitInfo::AliasEverything,
+ JSVAL_TYPE_UNDEFINED,
+};
+
static const JSFunctionSpec array_methods[] = {
#if JS_HAS_TOSOURCE
JS_FN(js_toSource_str, array_toSource, 0,0),
@@ -3139,7 +3114,7 @@ static const JSFunctionSpec array_methods[] = {
JS_INLINABLE_FN("pop", array_pop, 0,0, ArrayPop),
JS_INLINABLE_FN("shift", array_shift, 0,0, ArrayShift),
JS_FN("unshift", array_unshift, 1,0),
- JS_INLINABLE_FN("splice", array_splice, 2,0, ArraySplice),
+ JS_FNINFO("splice", array_splice, &array_splice_info, 2,0),
/* Pythonic sequence methods. */
JS_SELF_HOSTED_FN("concat", "ArrayConcat", 1,0),
@@ -3244,7 +3219,7 @@ ArrayConstructorImpl(JSContext* cx, CallArgs& args, bool isConstructor)
}
}
- JSObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length, proto);
+ ArrayObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length, proto);
if (!obj)
return false;
@@ -3270,7 +3245,7 @@ js::array_construct(JSContext* cx, unsigned argc, Value* vp)
return ArrayConstructorImpl(cx, args, /* isConstructor = */ false);
}
-JSObject*
+ArrayObject*
js::ArrayConstructorOneArg(JSContext* cx, HandleObjectGroup group, int32_t lengthInt)
{
if (lengthInt < 0) {
@@ -3286,7 +3261,7 @@ static JSObject*
CreateArrayPrototype(JSContext* cx, JSProtoKey key)
{
MOZ_ASSERT(key == JSProto_Array);
- RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx));
+ RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, cx->global()));
if (!proto)
return nullptr;
@@ -3306,7 +3281,7 @@ CreateArrayPrototype(JSContext* cx, JSProtoKey key)
metadata));
if (!arrayProto ||
!JSObject::setSingleton(cx, arrayProto) ||
- !arrayProto->setDelegate(cx) ||
+ !JSObject::setDelegate(cx, arrayProto) ||
!AddLengthProperty(cx, arrayProto))
{
return nullptr;
@@ -3565,7 +3540,7 @@ js::NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSOb
return arr;
}
-JSObject*
+ArrayObject*
js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::InitialHeap heap)
{
MOZ_ASSERT(!gc::IsInsideNursery(templateObject));
@@ -3578,30 +3553,21 @@ js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc
return arr;
}
-// Return a new boxed or unboxed array with the specified length and allocated
-// capacity (up to maxLength), using the specified group if possible. If the
-// specified group cannot be used, ensure that the created array at least has
-// the given [[Prototype]].
+// Return a new array with the specified length and allocated capacity (up to
+// maxLength), using the specified group if possible. If the specified group
+// cannot be used, ensure that the created array at least has the given
+// [[Prototype]].
template <uint32_t maxLength>
-static inline JSObject*
+static inline ArrayObject*
NewArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length,
NewObjectKind newKind = GenericObject)
{
MOZ_ASSERT(newKind != SingletonObject);
- if (group->maybePreliminaryObjects())
- group->maybePreliminaryObjects()->maybeAnalyze(cx, group);
-
- if (group->shouldPreTenure() || group->maybePreliminaryObjects())
+ if (group->shouldPreTenure())
newKind = TenuredObject;
RootedObject proto(cx, group->proto().toObject());
- if (group->maybeUnboxedLayout()) {
- if (length > UnboxedArrayObject::MaximumCapacity)
- return NewArray<maxLength>(cx, length, proto, newKind);
- return UnboxedArrayObject::create(cx, group, length, newKind, maxLength);
- }
-
ArrayObject* res = NewArray<maxLength>(cx, length, proto, newKind);
if (!res)
return nullptr;
@@ -3613,20 +3579,17 @@ NewArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length
if (res->length() > INT32_MAX)
res->setLength(cx, res->length());
- if (PreliminaryObjectArray* preliminaryObjects = group->maybePreliminaryObjects())
- preliminaryObjects->registerNewObject(res);
-
return res;
}
-JSObject*
+ArrayObject*
js::NewFullyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length,
NewObjectKind newKind)
{
return NewArrayTryUseGroup<UINT32_MAX>(cx, group, length, newKind);
}
-JSObject*
+ArrayObject*
js::NewPartlyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length)
{
return NewArrayTryUseGroup<ArrayObject::EagerAllocationMaxLength>(cx, group, length);
@@ -3635,42 +3598,39 @@ js::NewPartlyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup g
// Return a new array with the default prototype and specified allocated
// capacity and length. If possible, try to reuse the group of the input
// object. The resulting array will either reuse the input object's group or
-// will have unknown property types. Additionally, the result will have the
-// same boxed/unboxed elements representation as the input object, unless
-// |length| is larger than the input object's initialized length (in which case
-// UnboxedArrayObject::MaximumCapacity might be exceeded).
+// will have unknown property types.
template <uint32_t maxLength>
-static inline JSObject*
-NewArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
+static inline ArrayObject*
+NewArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length,
NewObjectKind newKind = GenericObject)
{
- if (!obj->is<ArrayObject>() && !obj->is<UnboxedArrayObject>())
+ if (!obj->is<ArrayObject>())
return NewArray<maxLength>(cx, length, nullptr, newKind);
if (obj->staticPrototype() != cx->global()->maybeGetArrayPrototype())
return NewArray<maxLength>(cx, length, nullptr, newKind);
- RootedObjectGroup group(cx, obj->getGroup(cx));
+ RootedObjectGroup group(cx, JSObject::getGroup(cx, obj));
if (!group)
return nullptr;
return NewArrayTryUseGroup<maxLength>(cx, group, length, newKind);
}
-JSObject*
-js::NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
+ArrayObject*
+js::NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length,
NewObjectKind newKind)
{
return NewArrayTryReuseGroup<UINT32_MAX>(cx, obj, length, newKind);
}
-JSObject*
-js::NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length)
+ArrayObject*
+js::NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length)
{
return NewArrayTryReuseGroup<ArrayObject::EagerAllocationMaxLength>(cx, obj, length);
}
-JSObject*
+ArrayObject*
js::NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length,
NewObjectKind newKind)
{
@@ -3680,7 +3640,7 @@ js::NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length,
return NewArrayTryUseGroup<UINT32_MAX>(cx, group, length, newKind);
}
-JSObject*
+ArrayObject*
js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, HandleObject proto)
{
RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array, proto));
@@ -3689,68 +3649,23 @@ js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length
return NewArrayTryUseGroup<ArrayObject::EagerAllocationMaxLength>(cx, group, length);
}
-bool
-js::MaybeAnalyzeBeforeCreatingLargeArray(ExclusiveContext* cx, HandleObjectGroup group,
- const Value* vp, size_t length)
-{
- static const size_t EagerPreliminaryObjectAnalysisThreshold = 800;
-
- // Force analysis to see if an unboxed array can be used when making a
- // sufficiently large array, to avoid excessive analysis and copying later
- // on. If this is the first array of its group that is being created, first
- // make a dummy array with the initial elements of the array we are about
- // to make, so there is some basis for the unboxed array analysis.
- if (length > EagerPreliminaryObjectAnalysisThreshold) {
- if (PreliminaryObjectArrayWithTemplate* objects = group->maybePreliminaryObjects()) {
- if (objects->empty()) {
- size_t nlength = Min<size_t>(length, 100);
- JSObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, nlength);
- if (!obj)
- return false;
- DebugOnly<DenseElementResult> result =
- SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, nlength,
- ShouldUpdateTypes::Update);
- MOZ_ASSERT(result.value == DenseElementResult::Success);
- }
- objects->maybeAnalyze(cx, group, /* forceAnalyze = */ true);
- }
- }
- return true;
-}
-
-JSObject*
+ArrayObject*
js::NewCopiedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group,
const Value* vp, size_t length, NewObjectKind newKind,
ShouldUpdateTypes updateTypes)
{
- if (!MaybeAnalyzeBeforeCreatingLargeArray(cx, group, vp, length))
- return nullptr;
-
- JSObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, length, newKind);
+ ArrayObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, length, newKind);
if (!obj)
return nullptr;
- DenseElementResult result =
- SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, length, updateTypes);
+ DenseElementResult result = obj->setOrExtendDenseElements(cx, 0, vp, length, updateTypes);
if (result == DenseElementResult::Failure)
return nullptr;
- if (result == DenseElementResult::Success)
- return obj;
-
- MOZ_ASSERT(obj->is<UnboxedArrayObject>());
- if (!UnboxedArrayObject::convertToNative(cx->asJSContext(), obj))
- return nullptr;
-
- result = SetOrExtendBoxedOrUnboxedDenseElements<JSVAL_TYPE_MAGIC>(cx, obj, 0, vp, length,
- updateTypes);
- MOZ_ASSERT(result != DenseElementResult::Incomplete);
- if (result == DenseElementResult::Failure)
- return nullptr;
-
+ MOZ_ASSERT(result == DenseElementResult::Success);
return obj;
}
-JSObject*
+ArrayObject*
js::NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length,
HandleObject proto /* = nullptr */)
{
diff --git a/js/src/jsarray.h b/js/src/jsarray.h
index e22cde881..d0084731f 100644
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -72,49 +72,37 @@ extern ArrayObject*
NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSObject* templateObject);
/* Create a dense array with the same copy-on-write elements as another object. */
-extern JSObject*
+extern ArrayObject*
NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::InitialHeap heap);
-// The methods below can create either boxed or unboxed arrays.
-
-extern JSObject*
+extern ArrayObject*
NewFullyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length,
NewObjectKind newKind = GenericObject);
-extern JSObject*
+extern ArrayObject*
NewPartlyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length);
-extern JSObject*
-NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
+extern ArrayObject*
+NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length,
NewObjectKind newKind = GenericObject);
-extern JSObject*
-NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length);
+extern ArrayObject*
+NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length);
-extern JSObject*
+extern ArrayObject*
NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length,
NewObjectKind newKind = GenericObject);
-extern JSObject*
+extern ArrayObject*
NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, HandleObject proto);
-enum class ShouldUpdateTypes
-{
- Update,
- DontUpdate
-};
-
-extern bool
-MaybeAnalyzeBeforeCreatingLargeArray(ExclusiveContext* cx, HandleObjectGroup group,
- const Value* vp, size_t length);
-
-extern JSObject*
+extern ArrayObject*
NewCopiedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group,
const Value* vp, size_t length,
NewObjectKind newKind = GenericObject,
ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update);
-extern JSObject*
+extern ArrayObject*
NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length,
HandleObject proto = nullptr);
@@ -129,13 +117,6 @@ NewValuePair(JSContext* cx, const Value& val1, const Value& val2, MutableHandleV
extern bool
WouldDefinePastNonwritableLength(HandleNativeObject obj, uint32_t index);
-/*
- * Canonicalize |vp| to a uint32_t value potentially suitable for use as an
- * array length.
- */
-extern bool
-CanonicalizeArrayLengthValue(JSContext* cx, HandleValue v, uint32_t* canonicalized);
-
extern bool
GetLengthProperty(JSContext* cx, HandleObject obj, uint32_t* lengthp);
@@ -166,13 +147,10 @@ extern bool
array_pop(JSContext* cx, unsigned argc, js::Value* vp);
extern bool
-array_splice_impl(JSContext* cx, unsigned argc, js::Value* vp, bool pop);
-
-extern bool
array_join(JSContext* cx, unsigned argc, js::Value* vp);
extern void
-ArrayShiftMoveElements(JSObject* obj);
+ArrayShiftMoveElements(NativeObject* obj);
extern bool
array_shift(JSContext* cx, unsigned argc, js::Value* vp);
@@ -192,6 +170,8 @@ array_reverse(JSContext* cx, unsigned argc, js::Value* vp);
extern bool
array_splice(JSContext* cx, unsigned argc, js::Value* vp);
+extern const JSJitInfo array_splice_info;
+
/*
* Append the given (non-hole) value to the end of an array. The array must be
* a newborn array -- that is, one which has not been exposed to script for
@@ -202,7 +182,7 @@ array_splice(JSContext* cx, unsigned argc, js::Value* vp);
extern bool
NewbornArrayPush(JSContext* cx, HandleObject obj, const Value& v);
-extern JSObject*
+extern ArrayObject*
ArrayConstructorOneArg(JSContext* cx, HandleObjectGroup group, int32_t lengthInt);
#ifdef DEBUG
diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp
index 2a3c58638..280ec3e9c 100644
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -54,41 +54,9 @@ FOR_EACH_COMMON_PROPERTYNAME(CONST_CHAR_STR)
#undef CONST_CHAR_STR
/* Constant strings that are not atomized. */
-const char js_break_str[] = "break";
-const char js_case_str[] = "case";
-const char js_catch_str[] = "catch";
-const char js_class_str[] = "class";
-const char js_const_str[] = "const";
-const char js_continue_str[] = "continue";
-const char js_debugger_str[] = "debugger";
-const char js_default_str[] = "default";
-const char js_do_str[] = "do";
-const char js_else_str[] = "else";
-const char js_enum_str[] = "enum";
-const char js_export_str[] = "export";
-const char js_extends_str[] = "extends";
-const char js_finally_str[] = "finally";
-const char js_for_str[] = "for";
const char js_getter_str[] = "getter";
-const char js_if_str[] = "if";
-const char js_implements_str[] = "implements";
-const char js_import_str[] = "import";
-const char js_in_str[] = "in";
-const char js_instanceof_str[] = "instanceof";
-const char js_interface_str[] = "interface";
-const char js_package_str[] = "package";
-const char js_private_str[] = "private";
-const char js_protected_str[] = "protected";
-const char js_public_str[] = "public";
const char js_send_str[] = "send";
const char js_setter_str[] = "setter";
-const char js_switch_str[] = "switch";
-const char js_this_str[] = "this";
-const char js_try_str[] = "try";
-const char js_typeof_str[] = "typeof";
-const char js_void_str[] = "void";
-const char js_while_str[] = "while";
-const char js_with_str[] = "with";
// Use a low initial capacity for atom hash tables to avoid penalizing runtimes
// which create a small number of atoms.
diff --git a/js/src/jsatom.h b/js/src/jsatom.h
index 496dcbb4c..0a5fd3c14 100644
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -142,44 +142,9 @@ FOR_EACH_COMMON_PROPERTYNAME(DECLARE_CONST_CHAR_STR)
#undef DECLARE_CONST_CHAR_STR
/* Constant strings that are not atomized. */
-extern const char js_break_str[];
-extern const char js_case_str[];
-extern const char js_catch_str[];
-extern const char js_class_str[];
-extern const char js_close_str[];
-extern const char js_const_str[];
-extern const char js_continue_str[];
-extern const char js_debugger_str[];
-extern const char js_default_str[];
-extern const char js_do_str[];
-extern const char js_else_str[];
-extern const char js_enum_str[];
-extern const char js_export_str[];
-extern const char js_extends_str[];
-extern const char js_finally_str[];
-extern const char js_for_str[];
extern const char js_getter_str[];
-extern const char js_if_str[];
-extern const char js_implements_str[];
-extern const char js_import_str[];
-extern const char js_in_str[];
-extern const char js_instanceof_str[];
-extern const char js_interface_str[];
-extern const char js_package_str[];
-extern const char js_private_str[];
-extern const char js_protected_str[];
-extern const char js_public_str[];
extern const char js_send_str[];
extern const char js_setter_str[];
-extern const char js_static_str[];
-extern const char js_super_str[];
-extern const char js_switch_str[];
-extern const char js_this_str[];
-extern const char js_try_str[];
-extern const char js_typeof_str[];
-extern const char js_void_str[];
-extern const char js_while_str[];
-extern const char js_with_str[];
namespace js {
diff --git a/js/src/jsbool.cpp b/js/src/jsbool.cpp
index c8109b02c..b2d07c628 100644
--- a/js/src/jsbool.cpp
+++ b/js/src/jsbool.cpp
@@ -137,14 +137,14 @@ js::InitBooleanClass(JSContext* cx, HandleObject obj)
{
MOZ_ASSERT(obj->isNative());
- Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
- Rooted<BooleanObject*> booleanProto(cx, global->createBlankPrototype<BooleanObject>(cx));
+ Rooted<BooleanObject*> booleanProto(cx, GlobalObject::createBlankPrototype<BooleanObject>(cx, global));
if (!booleanProto)
return nullptr;
booleanProto->setFixedSlot(BooleanObject::PRIMITIVE_VALUE_SLOT, BooleanValue(false));
- RootedFunction ctor(cx, global->createConstructor(cx, Boolean, cx->names().Boolean, 1));
+ RootedFunction ctor(cx, GlobalObject::createConstructor(cx, Boolean, cx->names().Boolean, 1));
if (!ctor)
return nullptr;
diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp
index 31d62332d..e505a4b34 100644
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -381,49 +381,16 @@ js::ReportUsageErrorASCII(JSContext* cx, HandleObject callee, const char* msg)
}
}
-bool
-js::PrintError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult,
- JSErrorReport* report, bool reportWarnings)
-{
- MOZ_ASSERT(report);
-
- /* Conditionally ignore reported warnings. */
- if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
- return false;
-
- char* prefix = nullptr;
- if (report->filename)
- prefix = JS_smprintf("%s:", report->filename);
- if (report->lineno) {
- char* tmp = prefix;
- prefix = JS_smprintf("%s%u:%u ", tmp ? tmp : "", report->lineno, report->column);
- JS_free(cx, tmp);
- }
- if (JSREPORT_IS_WARNING(report->flags)) {
- char* tmp = prefix;
- prefix = JS_smprintf("%s%swarning: ",
- tmp ? tmp : "",
- JSREPORT_IS_STRICT(report->flags) ? "strict " : "");
- JS_free(cx, tmp);
- }
-
- const char* message = toStringResult ? toStringResult.c_str() : report->message().c_str();
-
- /* embedded newlines -- argh! */
- const char* ctmp;
- while ((ctmp = strchr(message, '\n')) != 0) {
- ctmp++;
- if (prefix)
- fputs(prefix, file);
- fwrite(message, 1, ctmp - message, file);
- message = ctmp;
- }
-
- /* If there were no filename or lineno, the prefix might be empty */
- if (prefix)
- fputs(prefix, file);
- fputs(message, file);
+enum class PrintErrorKind {
+ Error,
+ Warning,
+ StrictWarning,
+ Note
+};
+static void
+PrintErrorLine(JSContext* cx, FILE* file, const char* prefix, JSErrorReport* report)
+{
if (const char16_t* linebuf = report->linebuf()) {
size_t n = report->linebufLength();
@@ -453,9 +420,96 @@ js::PrintError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult,
}
fputc('^', file);
}
+}
+
+static void
+PrintErrorLine(JSContext* cx, FILE* file, const char* prefix, JSErrorNotes::Note* note)
+{
+}
+
+template <typename T>
+static bool
+PrintSingleError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult,
+ T* report, PrintErrorKind kind)
+{
+ UniquePtr<char> prefix;
+ if (report->filename)
+ prefix.reset(JS_smprintf("%s:", report->filename));
+
+ if (report->lineno) {
+ UniquePtr<char> tmp(JS_smprintf("%s%u:%u ", prefix ? prefix.get() : "", report->lineno,
+ report->column));
+ prefix = Move(tmp);
+ }
+
+ if (kind != PrintErrorKind::Error) {
+ const char* kindPrefix = nullptr;
+ switch (kind) {
+ case PrintErrorKind::Error:
+ break;
+ case PrintErrorKind::Warning:
+ kindPrefix = "warning";
+ break;
+ case PrintErrorKind::StrictWarning:
+ kindPrefix = "strict warning";
+ break;
+ case PrintErrorKind::Note:
+ kindPrefix = "note";
+ break;
+ }
+
+ UniquePtr<char> tmp(JS_smprintf("%s%s: ", prefix ? prefix.get() : "", kindPrefix));
+ prefix = Move(tmp);
+ }
+
+ const char* message = toStringResult ? toStringResult.c_str() : report->message().c_str();
+
+ /* embedded newlines -- argh! */
+ const char* ctmp;
+ while ((ctmp = strchr(message, '\n')) != 0) {
+ ctmp++;
+ if (prefix)
+ fputs(prefix.get(), file);
+ fwrite(message, 1, ctmp - message, file);
+ message = ctmp;
+ }
+
+ /* If there were no filename or lineno, the prefix might be empty */
+ if (prefix)
+ fputs(prefix.get(), file);
+ fputs(message, file);
+
+ PrintErrorLine(cx, file, prefix.get(), report);
fputc('\n', file);
+
fflush(file);
- JS_free(cx, prefix);
+ return true;
+}
+
+bool
+js::PrintError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult,
+ JSErrorReport* report, bool reportWarnings)
+{
+ MOZ_ASSERT(report);
+
+ /* Conditionally ignore reported warnings. */
+ if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
+ return false;
+
+ PrintErrorKind kind = PrintErrorKind::Error;
+ if (JSREPORT_IS_WARNING(report->flags)) {
+ if (JSREPORT_IS_STRICT(report->flags))
+ kind = PrintErrorKind::StrictWarning;
+ else
+ kind = PrintErrorKind::Warning;
+ }
+ PrintSingleError(cx, file, toStringResult, report, kind);
+
+ if (report->notes) {
+ for (auto&& note : *report->notes)
+ PrintSingleError(cx, file, JS::ConstUTF8CharsZ(), note.get(), PrintErrorKind::Note);
+ }
+
return true;
}
@@ -557,6 +611,18 @@ class MOZ_RAII AutoMessageArgs
}
};
+static void
+SetExnType(JSErrorReport* reportp, int16_t exnType)
+{
+ reportp->exnType = exnType;
+}
+
+static void
+SetExnType(JSErrorNotes::Note* notep, int16_t exnType)
+{
+ // Do nothing for JSErrorNotes::Note.
+}
+
/*
* The arguments from ap need to be packaged up into an array and stored
* into the report struct.
@@ -568,12 +634,13 @@ class MOZ_RAII AutoMessageArgs
*
* Returns true if the expansion succeeds (can fail if out of memory).
*/
+template <typename T>
bool
-js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback,
+ExpandErrorArgumentsHelper(ExclusiveContext* cx, JSErrorCallback callback,
void* userRef, const unsigned errorNumber,
const char16_t** messageArgs,
ErrorArgumentsType argumentsType,
- JSErrorReport* reportp, va_list ap)
+ T* reportp, va_list ap)
{
const JSErrorFormatString* efs;
@@ -586,7 +653,7 @@ js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback,
}
if (efs) {
- reportp->exnType = efs->exnType;
+ SetExnType(reportp, efs->exnType);
MOZ_ASSERT_IF(argumentsType == ArgumentsAreASCII, JS::StringIsASCII(efs->format));
@@ -670,6 +737,28 @@ js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback,
}
bool
+js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback,
+ void* userRef, const unsigned errorNumber,
+ const char16_t** messageArgs,
+ ErrorArgumentsType argumentsType,
+ JSErrorReport* reportp, va_list ap)
+{
+ return ExpandErrorArgumentsHelper(cx, callback, userRef, errorNumber,
+ messageArgs, argumentsType, reportp, ap);
+}
+
+bool
+js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback,
+ void* userRef, const unsigned errorNumber,
+ const char16_t** messageArgs,
+ ErrorArgumentsType argumentsType,
+ JSErrorNotes::Note* notep, va_list ap)
+{
+ return ExpandErrorArgumentsHelper(cx, callback, userRef, errorNumber,
+ messageArgs, argumentsType, notep, ap);
+}
+
+bool
js::ReportErrorNumberVA(JSContext* cx, unsigned flags, JSErrorCallback callback,
void* userRef, const unsigned errorNumber,
ErrorArgumentsType argumentsType, va_list ap)
@@ -832,6 +921,52 @@ js::ReportValueErrorFlags(JSContext* cx, unsigned flags, const unsigned errorNum
return ok;
}
+JSObject*
+js::CreateErrorNotesArray(JSContext* cx, JSErrorReport* report)
+{
+ RootedArrayObject notesArray(cx, NewDenseEmptyArray(cx));
+ if (!notesArray)
+ return nullptr;
+
+ if (!report->notes)
+ return notesArray;
+
+ for (auto&& note : *report->notes) {
+ RootedPlainObject noteObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
+ if (!noteObj)
+ return nullptr;
+
+ RootedString messageStr(cx, note->newMessageString(cx));
+ if (!messageStr)
+ return nullptr;
+ RootedValue messageVal(cx, StringValue(messageStr));
+ if (!DefineProperty(cx, noteObj, cx->names().message, messageVal))
+ return nullptr;
+
+ RootedValue filenameVal(cx);
+ if (note->filename) {
+ RootedString filenameStr(cx, NewStringCopyZ<CanGC>(cx, note->filename));
+ if (!filenameStr)
+ return nullptr;
+ filenameVal = StringValue(filenameStr);
+ }
+ if (!DefineProperty(cx, noteObj, cx->names().fileName, filenameVal))
+ return nullptr;
+
+ RootedValue linenoVal(cx, Int32Value(note->lineno));
+ if (!DefineProperty(cx, noteObj, cx->names().lineNumber, linenoVal))
+ return nullptr;
+ RootedValue columnVal(cx, Int32Value(note->column));
+ if (!DefineProperty(cx, noteObj, cx->names().columnNumber, columnVal))
+ return nullptr;
+
+ if (!NewbornArrayPush(cx, notesArray, ObjectValue(*noteObj)))
+ return nullptr;
+ }
+
+ return notesArray;
+}
+
const JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
#define MSG_DEF(name, count, exception, format) \
{ #name, format, count, exception } ,
diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h
index 0a2841242..8be3376cf 100644
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -621,6 +621,13 @@ ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback,
ErrorArgumentsType argumentsType,
JSErrorReport* reportp, va_list ap);
+extern bool
+ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback,
+ void* userRef, const unsigned errorNumber,
+ const char16_t** messageArgs,
+ ErrorArgumentsType argumentsType,
+ JSErrorNotes::Note* notep, va_list ap);
+
/* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
extern void
ReportUsageErrorASCII(JSContext* cx, HandleObject callee, const char* msg);
@@ -678,6 +685,9 @@ ReportValueErrorFlags(JSContext* cx, unsigned flags, const unsigned errorNumber,
((void)ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber, \
spindex, v, fallback, arg1, arg2))
+JSObject*
+CreateErrorNotesArray(JSContext* cx, JSErrorReport* report);
+
} /* namespace js */
extern const JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp
index a48bb0ffe..d0caeb558 100644
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -112,13 +112,6 @@ JSCompartment::~JSCompartment()
js_delete(nonSyntacticLexicalEnvironments_),
js_free(enumerators);
-#ifdef DEBUG
- // Avoid assertion destroying the unboxed layouts list if the embedding
- // leaked GC things.
- if (!rt->gc.shutdownCollectedEverything())
- unboxedLayouts.clear();
-#endif
-
runtime_->numCompartments--;
}
@@ -1082,18 +1075,18 @@ CreateLazyScriptsForCompartment(JSContext* cx)
// Create scripts for each lazy function, updating the list of functions to
// process with any newly exposed inner functions in created scripts.
// A function cannot be delazified until its outer script exists.
+ RootedFunction fun(cx);
for (size_t i = 0; i < lazyFunctions.length(); i++) {
- JSFunction* fun = &lazyFunctions[i]->as<JSFunction>();
+ fun = &lazyFunctions[i]->as<JSFunction>();
// lazyFunctions may have been populated with multiple functions for
// a lazy script.
if (!fun->isInterpretedLazy())
continue;
- LazyScript* lazy = fun->lazyScript();
- bool lazyScriptHadNoScript = !lazy->maybeScript();
+ bool lazyScriptHadNoScript = !fun->lazyScript()->maybeScript();
- JSScript* script = fun->getOrCreateScript(cx);
+ JSScript* script = JSFunction::getOrCreateScript(cx, fun);
if (!script)
return false;
if (lazyScriptHadNoScript && !AddInnerLazyFunctionsFromScript(script, lazyFunctions))
diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h
index 98c8fe200..83c15da3b 100644
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -529,9 +529,6 @@ struct JSCompartment
// table manages references from such typed objects to their buffers.
js::ObjectWeakMap* lazyArrayBuffers;
- // All unboxed layouts in the compartment.
- mozilla::LinkedList<js::UnboxedLayout> unboxedLayouts;
-
// WebAssembly state for the compartment.
js::wasm::Compartment wasm;
diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp
index 52294a5df..c6a369e2d 100755
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -3163,7 +3163,7 @@ js::DateConstructor(JSContext* cx, unsigned argc, Value* vp)
static JSObject*
CreateDatePrototype(JSContext* cx, JSProtoKey key)
{
- return cx->global()->createBlankPrototype(cx, &DateObject::protoClass_);
+ return GlobalObject::createBlankPrototype(cx, cx->global(), &DateObject::protoClass_);
}
static bool
diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp
index 1e70a3890..65cc81a1a 100644
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -201,28 +201,77 @@ ErrorObject::classes[JSEXN_ERROR_LIMIT] = {
IMPLEMENT_ERROR_CLASS(RuntimeError)
};
-JSErrorReport*
-js::CopyErrorReport(JSContext* cx, JSErrorReport* report)
+size_t
+ExtraMallocSize(JSErrorReport* report)
+{
+ if (report->linebuf())
+ return (report->linebufLength() + 1) * sizeof(char16_t);
+
+ return 0;
+}
+
+size_t
+ExtraMallocSize(JSErrorNotes::Note* note)
+{
+ return 0;
+}
+
+bool
+CopyExtraData(JSContext* cx, uint8_t** cursor, JSErrorReport* copy, JSErrorReport* report)
+{
+ if (report->linebuf()) {
+ size_t linebufSize = (report->linebufLength() + 1) * sizeof(char16_t);
+ const char16_t* linebufCopy = (const char16_t*)(*cursor);
+ js_memcpy(*cursor, report->linebuf(), linebufSize);
+ *cursor += linebufSize;
+ copy->initBorrowedLinebuf(linebufCopy, report->linebufLength(), report->tokenOffset());
+ }
+
+ /* Copy non-pointer members. */
+ copy->isMuted = report->isMuted;
+ copy->exnType = report->exnType;
+
+ /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */
+ copy->flags = report->flags;
+
+ /* Deep copy notes. */
+ if (report->notes) {
+ auto copiedNotes = report->notes->copy(cx);
+ if (!copiedNotes)
+ return false;
+ copy->notes = Move(copiedNotes);
+ } else {
+ copy->notes.reset(nullptr);
+ }
+
+ return true;
+}
+
+bool
+CopyExtraData(JSContext* cx, uint8_t** cursor, JSErrorNotes::Note* copy, JSErrorNotes::Note* report)
+{
+ return true;
+}
+
+template <typename T>
+static T*
+CopyErrorHelper(JSContext* cx, T* report)
{
/*
- * We use a single malloc block to make a deep copy of JSErrorReport with
+ * We use a single malloc block to make a deep copy of JSErrorReport or
+ * JSErrorNotes::Note, except JSErrorNotes linked from JSErrorReport with
* the following layout:
- * JSErrorReport
+ * JSErrorReport or JSErrorNotes::Note
* char array with characters for message_
- * char16_t array with characters for linebuf
* char array with characters for filename
+ * char16_t array with characters for linebuf (only for JSErrorReport)
* Such layout together with the properties enforced by the following
* asserts does not need any extra alignment padding.
*/
- JS_STATIC_ASSERT(sizeof(JSErrorReport) % sizeof(const char*) == 0);
+ JS_STATIC_ASSERT(sizeof(T) % sizeof(const char*) == 0);
JS_STATIC_ASSERT(sizeof(const char*) % sizeof(char16_t) == 0);
-#define JS_CHARS_SIZE(chars) ((js_strlen(chars) + 1) * sizeof(char16_t))
-
size_t filenameSize = report->filename ? strlen(report->filename) + 1 : 0;
- size_t linebufSize = 0;
- if (report->linebuf())
- linebufSize = (report->linebufLength() + 1) * sizeof(char16_t);
size_t messageSize = 0;
if (report->message())
messageSize = strlen(report->message().c_str()) + 1;
@@ -231,13 +280,13 @@ js::CopyErrorReport(JSContext* cx, JSErrorReport* report)
* The mallocSize can not overflow since it represents the sum of the
* sizes of already allocated objects.
*/
- size_t mallocSize = sizeof(JSErrorReport) + messageSize + linebufSize + filenameSize;
+ size_t mallocSize = sizeof(T) + messageSize + filenameSize + ExtraMallocSize(report);
uint8_t* cursor = cx->pod_calloc<uint8_t>(mallocSize);
if (!cursor)
return nullptr;
- JSErrorReport* copy = (JSErrorReport*)cursor;
- cursor += sizeof(JSErrorReport);
+ T* copy = new (cursor) T();
+ cursor += sizeof(T);
if (report->message()) {
copy->initBorrowedMessage((const char*)cursor);
@@ -245,33 +294,40 @@ js::CopyErrorReport(JSContext* cx, JSErrorReport* report)
cursor += messageSize;
}
- if (report->linebuf()) {
- const char16_t* linebufCopy = (const char16_t*)cursor;
- js_memcpy(cursor, report->linebuf(), linebufSize);
- cursor += linebufSize;
- copy->initBorrowedLinebuf(linebufCopy, report->linebufLength(), report->tokenOffset());
- }
-
if (report->filename) {
copy->filename = (const char*)cursor;
js_memcpy(cursor, report->filename, filenameSize);
+ cursor += filenameSize;
}
- MOZ_ASSERT(cursor + filenameSize == (uint8_t*)copy + mallocSize);
+
+ if (!CopyExtraData(cx, &cursor, copy, report)) {
+ /* js_delete calls destructor for T and js_free for pod_calloc. */
+ js_delete(copy);
+ return nullptr;
+ }
+
+ MOZ_ASSERT(cursor == (uint8_t*)copy + mallocSize);
/* Copy non-pointer members. */
- copy->isMuted = report->isMuted;
copy->lineno = report->lineno;
copy->column = report->column;
copy->errorNumber = report->errorNumber;
- copy->exnType = report->exnType;
-
- /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */
- copy->flags = report->flags;
-#undef JS_CHARS_SIZE
return copy;
}
+JSErrorNotes::Note*
+js::CopyErrorNote(JSContext* cx, JSErrorNotes::Note* note)
+{
+ return CopyErrorHelper(cx, note);
+}
+
+JSErrorReport*
+js::CopyErrorReport(JSContext* cx, JSErrorReport* report)
+{
+ return CopyErrorHelper(cx, report);
+}
+
struct SuppressErrorsGuard
{
JSContext* cx;
@@ -322,7 +378,7 @@ exn_finalize(FreeOp* fop, JSObject* obj)
{
MOZ_ASSERT(fop->maybeOffMainThread());
if (JSErrorReport* report = obj->as<ErrorObject>().getErrorReport())
- fop->free_(report);
+ fop->delete_(report);
}
JSErrorReport*
@@ -512,14 +568,17 @@ ErrorObject::createProto(JSContext* cx, JSProtoKey key)
{
JSExnType type = ExnTypeFromProtoKey(key);
- if (type == JSEXN_ERR)
- return cx->global()->createBlankPrototype(cx, &ErrorObject::protoClasses[JSEXN_ERR]);
+ if (type == JSEXN_ERR) {
+ return GlobalObject::createBlankPrototype(cx, cx->global(),
+ &ErrorObject::protoClasses[JSEXN_ERR]);
+ }
RootedObject protoProto(cx, GlobalObject::getOrCreateErrorPrototype(cx, cx->global()));
if (!protoProto)
return nullptr;
- return cx->global()->createBlankPrototypeInheriting(cx, &ErrorObject::protoClasses[type],
+ return GlobalObject::createBlankPrototypeInheriting(cx, cx->global(),
+ &ErrorObject::protoClasses[type],
protoProto);
}
@@ -586,6 +645,7 @@ js::ErrorToException(JSContext* cx, JSErrorReport* reportp,
const JSErrorFormatString* errorString = callback(userRef, errorNumber);
JSExnType exnType = errorString ? static_cast<JSExnType>(errorString->exnType) : JSEXN_ERR;
MOZ_ASSERT(exnType < JSEXN_LIMIT);
+ MOZ_ASSERT(exnType != JSEXN_NOTE);
if (exnType == JSEXN_WARN) {
// werror must be enabled, so we use JSEXN_ERR.
@@ -669,7 +729,7 @@ ErrorReportToString(JSContext* cx, JSErrorReport* reportp)
*/
JSExnType type = static_cast<JSExnType>(reportp->exnType);
RootedString str(cx);
- if (type != JSEXN_WARN)
+ if (type != JSEXN_WARN && type != JSEXN_NOTE)
str = ClassName(GetExceptionProtoKey(type), cx);
/*
diff --git a/js/src/jsexn.h b/js/src/jsexn.h
index ae6335209..00120d89c 100644
--- a/js/src/jsexn.h
+++ b/js/src/jsexn.h
@@ -18,6 +18,9 @@
namespace js {
class ErrorObject;
+JSErrorNotes::Note*
+CopyErrorNote(JSContext* cx, JSErrorNotes::Note* note);
+
JSErrorReport*
CopyErrorReport(JSContext* cx, JSErrorReport* report);
@@ -67,7 +70,8 @@ static_assert(JSEXN_ERR == 0 &&
JSProto_Error + JSEXN_WASMCOMPILEERROR == JSProto_CompileError &&
JSProto_Error + JSEXN_WASMRUNTIMEERROR == JSProto_RuntimeError &&
JSEXN_WASMRUNTIMEERROR + 1 == JSEXN_WARN &&
- JSEXN_WARN + 1 == JSEXN_LIMIT,
+ JSEXN_WARN + 1 == JSEXN_NOTE &&
+ JSEXN_NOTE + 1 == JSEXN_LIMIT,
"GetExceptionProtoKey and ExnTypeFromProtoKey require that "
"each corresponding JSExnType and JSProtoKey value be separated "
"by the same constant value");
diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp
index f5cd56a9b..f7622cb44 100644
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -113,7 +113,7 @@ JS_SplicePrototype(JSContext* cx, HandleObject obj, HandleObject proto)
}
Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
- return obj->splicePrototype(cx, obj->getClass(), tagged);
+ return JSObject::splicePrototype(cx, obj, obj->getClass(), tagged);
}
JS_FRIEND_API(JSObject*)
@@ -269,9 +269,9 @@ js::GetBuiltinClass(JSContext* cx, HandleObject obj, ESClass* cls)
if (MOZ_UNLIKELY(obj->is<ProxyObject>()))
return Proxy::getBuiltinClass(cx, obj, cls);
- if (obj->is<PlainObject>() || obj->is<UnboxedPlainObject>())
+ if (obj->is<PlainObject>())
*cls = ESClass::Object;
- else if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>())
+ else if (obj->is<ArrayObject>())
*cls = ESClass::Array;
else if (obj->is<NumberObject>())
*cls = ESClass::Number;
diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h
index d29285483..351667fb3 100644
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -2254,6 +2254,7 @@ struct JSJitInfo {
Method,
StaticMethod,
InlinableNative,
+ IgnoresReturnValueNative,
// Must be last
OpTypeCount
};
@@ -2345,8 +2346,13 @@ struct JSJitInfo {
JSJitMethodOp method;
/** A DOM static method, used for Promise wrappers */
JSNative staticMethod;
+ JSNative ignoresReturnValueMethod;
};
+ static unsigned offsetOfIgnoresReturnValueNative() {
+ return offsetof(JSJitInfo, ignoresReturnValueMethod);
+ }
+
union {
uint16_t protoID;
js::jit::InlinableNative inlinableNative;
diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp
index e624aa415..9edf238ef 100644
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -129,6 +129,11 @@ IsFunctionInStrictMode(JSFunction* fun)
return IsAsmJSStrictModeModuleOrFunction(fun);
}
+static bool
+IsNewerTypeFunction(JSFunction* fun) {
+ return fun->isArrow() || fun->isGenerator() || fun->isAsync() || fun->isMethod();
+}
+
// Beware: this function can be invoked on *any* function! That includes
// natives, strict mode functions, bound functions, arrow functions,
// self-hosted functions and constructors, asm.js functions, functions with
@@ -142,7 +147,9 @@ ArgumentsRestrictions(JSContext* cx, HandleFunction fun)
// a strict mode function, or a bound function.
// TODO (bug 1057208): ensure semantics are correct for all possible
// pairings of callee/caller.
- if (fun->isBuiltin() || IsFunctionInStrictMode(fun) || fun->isBoundFunction()) {
+ if (fun->isBuiltin() || IsFunctionInStrictMode(fun) ||
+ fun->isBoundFunction() || IsNewerTypeFunction(fun))
+ {
ThrowTypeErrorBehavior(cx);
return false;
}
@@ -229,7 +236,9 @@ CallerRestrictions(JSContext* cx, HandleFunction fun)
// a strict mode function, or a bound function.
// TODO (bug 1057208): ensure semantics are correct for all possible
// pairings of callee/caller.
- if (fun->isBuiltin() || IsFunctionInStrictMode(fun) || fun->isBoundFunction()) {
+ if (fun->isBuiltin() || IsFunctionInStrictMode(fun) ||
+ fun->isBoundFunction() || IsNewerTypeFunction(fun))
+ {
ThrowTypeErrorBehavior(cx);
return false;
}
@@ -275,6 +284,8 @@ CallerGetterImpl(JSContext* cx, const CallArgs& args)
}
RootedObject caller(cx, iter.callee(cx));
+ if (caller->is<JSFunction>() && caller->as<JSFunction>().isAsync())
+ caller = GetWrappedAsyncFunction(&caller->as<JSFunction>());
if (!cx->compartment()->wrap(cx, &caller))
return false;
@@ -295,6 +306,8 @@ CallerGetterImpl(JSContext* cx, const CallArgs& args)
}
JSFunction* callerFun = &callerObj->as<JSFunction>();
+ if (IsWrappedAsyncFunction(callerFun))
+ callerFun = GetUnwrappedAsyncFunction(callerFun);
MOZ_ASSERT(!callerFun->isBuiltin(), "non-builtin iterator returned a builtin?");
if (callerFun->strict()) {
@@ -366,7 +379,7 @@ ResolveInterpretedFunctionPrototype(JSContext* cx, HandleFunction fun, HandleId
if (isStarGenerator)
objProto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global);
else
- objProto = fun->global().getOrCreateObjectPrototype(cx);
+ objProto = GlobalObject::getOrCreateObjectPrototype(cx, global);
if (!objProto)
return false;
@@ -463,7 +476,7 @@ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
if (fun->hasResolvedLength())
return true;
- if (!fun->getUnresolvedLength(cx, &v))
+ if (!JSFunction::getUnresolvedLength(cx, fun, &v))
return false;
} else {
if (fun->hasResolvedName())
@@ -808,12 +821,13 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
sourceObject,
begin,
ss->length(),
- 0));
+ 0,
+ ss->length()));
if (!script || !JSScript::initFunctionPrototype(cx, script, functionProto))
return nullptr;
functionProto->initScript(script);
- ObjectGroup* protoGroup = functionProto->getGroup(cx);
+ ObjectGroup* protoGroup = JSObject::getGroup(cx, functionProto);
if (!protoGroup)
return nullptr;
@@ -848,6 +862,28 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
if (!throwTypeError || !PreventExtensions(cx, throwTypeError))
return nullptr;
+ // The "length" property of %ThrowTypeError% is non-configurable, adjust
+ // the default property attributes accordingly.
+ Rooted<PropertyDescriptor> nonConfigurableDesc(cx);
+ nonConfigurableDesc.setAttributes(JSPROP_PERMANENT | JSPROP_IGNORE_READONLY |
+ JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_VALUE);
+
+ RootedId lengthId(cx, NameToId(cx->names().length));
+ ObjectOpResult lengthResult;
+ if (!NativeDefineProperty(cx, throwTypeError, lengthId, nonConfigurableDesc, lengthResult))
+ return nullptr;
+ MOZ_ASSERT(lengthResult);
+
+ // Non-standard: Also change "name" to non-configurable. ECMAScript defines
+ // %ThrowTypeError% as an anonymous function, i.e. it shouldn't actually
+ // get an own "name" property. To be consistent with other built-in,
+ // anonymous functions, we don't delete %ThrowTypeError%'s "name" property.
+ RootedId nameId(cx, NameToId(cx->names().name));
+ ObjectOpResult nameResult;
+ if (!NativeDefineProperty(cx, throwTypeError, nameId, nonConfigurableDesc, nameResult))
+ return nullptr;
+ MOZ_ASSERT(nameResult);
+
self->setThrowTypeError(throwTypeError);
return functionProto;
@@ -886,80 +922,10 @@ const Class JSFunction::class_ = {
const Class* const js::FunctionClassPtr = &JSFunction::class_;
-/* Find the body of a function (not including braces). */
-bool
-js::FindBody(JSContext* cx, HandleFunction fun, HandleLinearString src, size_t* bodyStart,
- size_t* bodyEnd)
-{
- // We don't need principals, since those are only used for error reporting.
- CompileOptions options(cx);
- options.setFileAndLine("internal-findBody", 0);
-
- // For asm.js/wasm modules, there's no script.
- if (fun->hasScript())
- options.setVersion(fun->nonLazyScript()->getVersion());
-
- AutoKeepAtoms keepAtoms(cx->perThreadData);
-
- AutoStableStringChars stableChars(cx);
- if (!stableChars.initTwoByte(cx, src))
- return false;
-
- const mozilla::Range<const char16_t> srcChars = stableChars.twoByteRange();
- TokenStream ts(cx, options, srcChars.begin().get(), srcChars.length(), nullptr);
- int nest = 0;
- bool onward = true;
- // Skip arguments list.
- do {
- TokenKind tt;
- if (!ts.getToken(&tt))
- return false;
- switch (tt) {
- case TOK_NAME:
- case TOK_YIELD:
- if (nest == 0)
- onward = false;
- break;
- case TOK_LP:
- nest++;
- break;
- case TOK_RP:
- if (--nest == 0)
- onward = false;
- break;
- default:
- break;
- }
- } while (onward);
- TokenKind tt;
- if (!ts.getToken(&tt))
- return false;
- if (tt == TOK_ARROW) {
- if (!ts.getToken(&tt))
- return false;
- }
- bool braced = tt == TOK_LC;
- MOZ_ASSERT_IF(fun->isExprBody(), !braced);
- *bodyStart = ts.currentToken().pos.begin;
- if (braced)
- *bodyStart += 1;
- mozilla::RangedPtr<const char16_t> end = srcChars.end();
- if (end[-1] == '}') {
- end--;
- } else {
- MOZ_ASSERT(!braced);
- for (; unicode::IsSpaceOrBOM2(end[-1]); end--)
- ;
- }
- *bodyEnd = end - srcChars.begin();
- MOZ_ASSERT(*bodyStart <= *bodyEnd);
- return true;
-}
-
JSString*
js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
{
- if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx))
+ if (fun->isInterpretedLazy() && !JSFunction::getOrCreateScript(cx, fun))
return nullptr;
if (IsAsmJSModule(fun))
@@ -989,7 +955,13 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
}
bool funIsNonArrowLambda = fun->isLambda() && !fun->isArrow();
- bool haveSource = fun->isInterpreted() && !fun->isSelfHostedBuiltin();
+
+ // Default class constructors are self-hosted, but have their source
+ // objects overridden to refer to the span of the class statement or
+ // expression. Non-default class constructors are never self-hosted. So,
+ // all class constructors always have source.
+ bool haveSource = fun->isInterpreted() && (fun->isClassConstructor() ||
+ !fun->isSelfHostedBuiltin());
// If we're not in pretty mode, put parentheses around lambda functions
// so that eval returns lambda, not function statement.
@@ -1030,7 +1002,7 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
};
if (haveSource) {
- Rooted<JSFlatString*> src(cx, script->sourceDataWithPrelude(cx));
+ Rooted<JSFlatString*> src(cx, JSScript::sourceDataForToString(cx, script));
if (!src)
return nullptr;
@@ -1041,7 +1013,20 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
if (!out.append(")"))
return nullptr;
}
- } else if (fun->isInterpreted() && !fun->isSelfHostedBuiltin()) {
+ } else if (fun->isInterpreted() &&
+ (!fun->isSelfHostedBuiltin() ||
+ fun->infallibleIsDefaultClassConstructor(cx)))
+ {
+ // Default class constructors should always haveSource except;
+ //
+ // 1. Source has been discarded for the whole compartment.
+ //
+ // 2. The source is marked as "lazy", i.e., retrieved on demand, and
+ // the embedding has not provided a hook to retrieve sources.
+ MOZ_ASSERT_IF(fun->infallibleIsDefaultClassConstructor(cx),
+ !cx->runtime()->sourceHook ||
+ !script->scriptSource()->sourceRetrievable() ||
+ fun->compartment()->behaviors().discardSource());
if (!AppendPrelude() ||
!out.append("() {\n ") ||
!out.append("[sourceless code]") ||
@@ -1050,29 +1035,16 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
return nullptr;
}
} else {
- MOZ_ASSERT(!fun->isExprBody());
- bool derived = fun->infallibleIsDefaultClassConstructor(cx);
- if (derived && fun->isDerivedClassConstructor()) {
- if (!AppendPrelude() ||
- !out.append("(...args) {\n ") ||
- !out.append("super(...args);\n}"))
- {
- return nullptr;
- }
- } else {
- if (!AppendPrelude() ||
- !out.append("() {\n "))
- return nullptr;
+ if (!AppendPrelude() ||
+ !out.append("() {\n "))
+ return nullptr;
- if (!derived) {
- if (!out.append("[native code]"))
- return nullptr;
- }
+ if (!out.append("[native code]"))
+ return nullptr;
- if (!out.append("\n}"))
- return nullptr;
- }
+ if (!out.append("\n}"))
+ return nullptr;
}
return out.finishString();
}
@@ -1301,34 +1273,33 @@ JSFunction::isDerivedClassConstructor()
return derived;
}
-bool
-JSFunction::getLength(JSContext* cx, uint16_t* length)
+/* static */ bool
+JSFunction::getLength(JSContext* cx, HandleFunction fun, uint16_t* length)
{
- JS::RootedFunction self(cx, this);
- MOZ_ASSERT(!self->isBoundFunction());
- if (self->isInterpretedLazy() && !self->getOrCreateScript(cx))
+ MOZ_ASSERT(!fun->isBoundFunction());
+ if (fun->isInterpretedLazy() && !getOrCreateScript(cx, fun))
return false;
- *length = self->isNative() ? self->nargs() : self->nonLazyScript()->funLength();
+ *length = fun->isNative() ? fun->nargs() : fun->nonLazyScript()->funLength();
return true;
}
-bool
-JSFunction::getUnresolvedLength(JSContext* cx, MutableHandleValue v)
+/* static */ bool
+JSFunction::getUnresolvedLength(JSContext* cx, HandleFunction fun, MutableHandleValue v)
{
- MOZ_ASSERT(!IsInternalFunctionObject(*this));
- MOZ_ASSERT(!hasResolvedLength());
+ MOZ_ASSERT(!IsInternalFunctionObject(*fun));
+ MOZ_ASSERT(!fun->hasResolvedLength());
// Bound functions' length can have values up to MAX_SAFE_INTEGER, so
// they're handled differently from other functions.
- if (isBoundFunction()) {
- MOZ_ASSERT(getExtendedSlot(BOUND_FUN_LENGTH_SLOT).isNumber());
- v.set(getExtendedSlot(BOUND_FUN_LENGTH_SLOT));
+ if (fun->isBoundFunction()) {
+ MOZ_ASSERT(fun->getExtendedSlot(BOUND_FUN_LENGTH_SLOT).isNumber());
+ v.set(fun->getExtendedSlot(BOUND_FUN_LENGTH_SLOT));
return true;
}
uint16_t length;
- if (!getLength(cx, &length))
+ if (!JSFunction::getLength(cx, fun, &length))
return false;
v.setInt32(length);
@@ -1385,13 +1356,11 @@ GetBoundFunctionArguments(const JSFunction* boundFun)
}
const js::Value&
-JSFunction::getBoundFunctionArgument(JSContext* cx, unsigned which) const
+JSFunction::getBoundFunctionArgument(unsigned which) const
{
MOZ_ASSERT(which < getBoundFunctionArgumentCount());
- RootedArrayObject boundArgs(cx, GetBoundFunctionArguments(this));
- RootedValue res(cx);
- return boundArgs->getDenseElement(which);
+ return GetBoundFunctionArguments(this)->getDenseElement(which);
}
size_t
@@ -1428,7 +1397,7 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext* cx, HandleFuncti
}
if (fun != lazy->functionNonDelazifying()) {
- if (!lazy->functionDelazifying(cx))
+ if (!LazyScript::functionDelazifying(cx, lazy))
return false;
script = lazy->functionNonDelazifying()->nonLazyScript();
if (!script)
diff --git a/js/src/jsfun.h b/js/src/jsfun.h
index 1c7da57ec..234169507 100644
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -58,8 +58,6 @@ class JSFunction : public js::NativeObject
CONSTRUCTOR = 0x0002, /* function that can be called as a constructor */
EXTENDED = 0x0004, /* structure is FunctionExtended */
BOUND_FUN = 0x0008, /* function was created with Function.prototype.bind. */
- EXPR_BODY = 0x0010, /* arrow function with expression body or
- * expression closure: function(x) x*x */
HAS_GUESSED_ATOM = 0x0020, /* function had no explicit name, but a
name was guessed for it anyway */
LAMBDA = 0x0040, /* function comes from a FunctionExpression, ArrowFunction, or
@@ -102,7 +100,7 @@ class JSFunction : public js::NativeObject
INTERPRETED_GENERATOR = INTERPRETED,
NO_XDR_FLAGS = RESOLVED_LENGTH | RESOLVED_NAME,
- STABLE_ACROSS_CLONES = CONSTRUCTOR | EXPR_BODY | HAS_GUESSED_ATOM | LAMBDA |
+ STABLE_ACROSS_CLONES = CONSTRUCTOR | HAS_GUESSED_ATOM | LAMBDA |
SELF_HOSTED | HAS_COMPILE_TIME_NAME | FUNCTION_KIND_MASK
};
@@ -187,7 +185,6 @@ class JSFunction : public js::NativeObject
bool isAsmJSNative() const { return kind() == AsmJS; }
/* Possible attributes of an interpreted function: */
- bool isExprBody() const { return flags() & EXPR_BODY; }
bool hasCompileTimeName() const { return flags() & HAS_COMPILE_TIME_NAME; }
bool hasGuessedAtom() const { return flags() & HAS_GUESSED_ATOM; }
bool isLambda() const { return flags() & LAMBDA; }
@@ -290,11 +287,6 @@ class JSFunction : public js::NativeObject
flags_ |= SELF_HOSTED;
}
- // Can be called multiple times by the parser.
- void setIsExprBody() {
- flags_ |= EXPR_BODY;
- }
-
void setArrow() {
setKind(Arrow);
}
@@ -314,7 +306,8 @@ class JSFunction : public js::NativeObject
nonLazyScript()->setAsyncKind(asyncKind);
}
- bool getUnresolvedLength(JSContext* cx, js::MutableHandleValue v);
+ static bool getUnresolvedLength(JSContext* cx, js::HandleFunction fun,
+ js::MutableHandleValue v);
JSAtom* getUnresolvedName(JSContext* cx);
@@ -419,16 +412,15 @@ class JSFunction : public js::NativeObject
//
// - For functions known to have a JSScript, nonLazyScript() will get it.
- JSScript* getOrCreateScript(JSContext* cx) {
- MOZ_ASSERT(isInterpreted());
+ static JSScript* getOrCreateScript(JSContext* cx, js::HandleFunction fun) {
+ MOZ_ASSERT(fun->isInterpreted());
MOZ_ASSERT(cx);
- if (isInterpretedLazy()) {
- JS::RootedFunction self(cx, this);
- if (!createScriptForLazilyInterpretedFunction(cx, self))
+ if (fun->isInterpretedLazy()) {
+ if (!createScriptForLazilyInterpretedFunction(cx, fun))
return nullptr;
- return self->nonLazyScript();
+ return fun->nonLazyScript();
}
- return nonLazyScript();
+ return fun->nonLazyScript();
}
JSScript* existingScriptNonDelazifying() const {
@@ -486,7 +478,7 @@ class JSFunction : public js::NativeObject
return u.i.s.script_;
}
- bool getLength(JSContext* cx, uint16_t* length);
+ static bool getLength(JSContext* cx, js::HandleFunction fun, uint16_t* length);
js::LazyScript* lazyScript() const {
MOZ_ASSERT(isInterpretedLazy() && u.i.s.lazy_);
@@ -593,13 +585,17 @@ class JSFunction : public js::NativeObject
return offsetof(JSFunction, u.nativeOrScript);
}
+ static unsigned offsetOfJitInfo() {
+ return offsetof(JSFunction, u.n.jitinfo);
+ }
+
inline void trace(JSTracer* trc);
/* Bound function accessors. */
JSObject* getBoundFunctionTarget() const;
const js::Value& getBoundFunctionThis() const;
- const js::Value& getBoundFunctionArgument(JSContext* cx, unsigned which) const;
+ const js::Value& getBoundFunctionArgument(unsigned which) const;
size_t getBoundFunctionArgumentCount() const;
private:
@@ -802,10 +798,6 @@ CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject parent,
gc::AllocKind kind = gc::AllocKind::FUNCTION,
HandleObject proto = nullptr);
-extern bool
-FindBody(JSContext* cx, HandleFunction fun, HandleLinearString src, size_t* bodyStart,
- size_t* bodyEnd);
-
} // namespace js
inline js::FunctionExtended*
diff --git a/js/src/jsfuninlines.h b/js/src/jsfuninlines.h
index e134def61..13fe51e26 100644
--- a/js/src/jsfuninlines.h
+++ b/js/src/jsfuninlines.h
@@ -88,7 +88,7 @@ CloneFunctionObjectIfNotSingleton(JSContext* cx, HandleFunction fun, HandleObjec
if (CanReuseScriptForClone(cx->compartment(), fun, parent))
return CloneFunctionReuseScript(cx, fun, parent, kind, newKind, proto);
- RootedScript script(cx, fun->getOrCreateScript(cx));
+ RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
if (!script)
return nullptr;
RootedScope enclosingScope(cx, script->enclosingScope());
diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp
index 004c7fc0d..749e15d27 100644
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -654,7 +654,7 @@ VectorToKeyIterator(JSContext* cx, HandleObject obj, unsigned flags, AutoIdVecto
{
MOZ_ASSERT(!(flags & JSITER_FOREACH));
- if (obj->isSingleton() && !obj->setIteratedSingleton(cx))
+ if (obj->isSingleton() && !JSObject::setIteratedSingleton(cx, obj))
return false;
MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_ITERATED);
@@ -698,7 +698,7 @@ VectorToValueIterator(JSContext* cx, HandleObject obj, unsigned flags, AutoIdVec
{
MOZ_ASSERT(flags & JSITER_FOREACH);
- if (obj->isSingleton() && !obj->setIteratedSingleton(cx))
+ if (obj->isSingleton() && !JSObject::setIteratedSingleton(cx, obj))
return false;
MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_ITERATED);
@@ -921,7 +921,7 @@ js::CreateItrResultObject(JSContext* cx, HandleValue value, bool done)
// FIXME: We can cache the iterator result object shape somewhere.
AssertHeapIsIdle(cx);
- RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx));
+ RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, cx->global()));
if (!proto)
return nullptr;
@@ -1497,7 +1497,7 @@ GlobalObject::initIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
if (global->getReservedSlot(ITERATOR_PROTO).isObject())
return true;
- RootedObject proto(cx, global->createBlankPrototype<PlainObject>(cx));
+ RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, iterator_proto_methods))
return false;
@@ -1516,7 +1516,8 @@ GlobalObject::initArrayIteratorProto(JSContext* cx, Handle<GlobalObject*> global
return false;
const Class* cls = &ArrayIteratorPrototypeClass;
- RootedObject proto(cx, global->createBlankPrototypeInheriting(cx, cls, iteratorProto));
+ RootedObject proto(cx, GlobalObject::createBlankPrototypeInheriting(cx, global, cls,
+ iteratorProto));
if (!proto ||
!DefinePropertiesAndFunctions(cx, proto, nullptr, array_iterator_methods) ||
!DefineToStringTag(cx, proto, cx->names().ArrayIterator))
@@ -1539,7 +1540,8 @@ GlobalObject::initStringIteratorProto(JSContext* cx, Handle<GlobalObject*> globa
return false;
const Class* cls = &StringIteratorPrototypeClass;
- RootedObject proto(cx, global->createBlankPrototypeInheriting(cx, cls, iteratorProto));
+ RootedObject proto(cx, GlobalObject::createBlankPrototypeInheriting(cx, global, cls,
+ iteratorProto));
if (!proto ||
!DefinePropertiesAndFunctions(cx, proto, nullptr, string_iterator_methods) ||
!DefineToStringTag(cx, proto, cx->names().StringIterator))
@@ -1560,7 +1562,8 @@ js::InitLegacyIteratorClass(JSContext* cx, HandleObject obj)
return &global->getPrototype(JSProto_Iterator).toObject();
RootedObject iteratorProto(cx);
- iteratorProto = global->createBlankPrototype(cx, &PropertyIteratorObject::class_);
+ iteratorProto = GlobalObject::createBlankPrototype(cx, global,
+ &PropertyIteratorObject::class_);
if (!iteratorProto)
return nullptr;
@@ -1572,7 +1575,7 @@ js::InitLegacyIteratorClass(JSContext* cx, HandleObject obj)
ni->init(nullptr, nullptr, 0 /* flags */, 0, 0);
Rooted<JSFunction*> ctor(cx);
- ctor = global->createConstructor(cx, IteratorConstructor, cx->names().Iterator, 2);
+ ctor = GlobalObject::createConstructor(cx, IteratorConstructor, cx->names().Iterator, 2);
if (!ctor)
return nullptr;
if (!LinkConstructorAndPrototype(cx, ctor, iteratorProto))
@@ -1593,7 +1596,8 @@ js::InitStopIterationClass(JSContext* cx, HandleObject obj)
{
Handle<GlobalObject*> global = obj.as<GlobalObject>();
if (!global->getPrototype(JSProto_StopIteration).isObject()) {
- RootedObject proto(cx, global->createBlankPrototype(cx, &StopIterationObject::class_));
+ RootedObject proto(cx, GlobalObject::createBlankPrototype(cx, global,
+ &StopIterationObject::class_));
if (!proto || !FreezeObject(cx, proto))
return nullptr;
diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp
index 08fbe048c..78a231003 100644
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -1417,7 +1417,8 @@ static const JSFunctionSpec math_static_methods[] = {
JSObject*
js::InitMathClass(JSContext* cx, HandleObject obj)
{
- RootedObject proto(cx, obj->as<GlobalObject>().getOrCreateObjectPrototype(cx));
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
+ RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
if (!proto)
return nullptr;
RootedObject Math(cx, NewObjectWithGivenProto(cx, &MathClass, proto, SingletonObject));
diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp
index 28ed15159..bde1f918e 100644
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -1005,15 +1005,16 @@ js::InitNumberClass(JSContext* cx, HandleObject obj)
/* XXX must do at least once per new thread, so do it per JSContext... */
FIX_FPU();
- Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
- RootedObject numberProto(cx, global->createBlankPrototype(cx, &NumberObject::class_));
+ RootedObject numberProto(cx, GlobalObject::createBlankPrototype(cx, global,
+ &NumberObject::class_));
if (!numberProto)
return nullptr;
numberProto->as<NumberObject>().setPrimitiveValue(0);
RootedFunction ctor(cx);
- ctor = global->createConstructor(cx, Number, cx->names().Number, 1);
+ ctor = GlobalObject::createConstructor(cx, Number, cx->names().Number, 1);
if (!ctor)
return nullptr;
diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp
index f22ecb445..6f9596924 100644
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -43,6 +43,7 @@
#include "frontend/BytecodeCompiler.h"
#include "gc/Marking.h"
#include "gc/Policy.h"
+#include "gc/StoreBuffer-inl.h"
#include "jit/BaselineJIT.h"
#include "js/MemoryMetrics.h"
#include "js/Proxy.h"
@@ -54,7 +55,6 @@
#include "vm/RegExpStaticsObject.h"
#include "vm/Shape.h"
#include "vm/TypedArrayCommon.h"
-#include "vm/UnboxedObject-inl.h"
#include "jsatominlines.h"
#include "jsboolinlines.h"
@@ -471,7 +471,7 @@ js::SetIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level)
// Steps 8-9, loosely interpreted.
if (obj->isNative() && !obj->as<NativeObject>().inDictionaryMode() &&
- !obj->is<TypedArrayObject>())
+ !obj->is<TypedArrayObject>() && !obj->is<MappedArgumentsObject>())
{
HandleNativeObject nobj = obj.as<NativeObject>();
@@ -884,7 +884,7 @@ CreateThisForFunctionWithGroup(JSContext* cx, HandleObjectGroup group,
if (newKind == SingletonObject) {
Rooted<TaggedProto> proto(cx, TaggedProto(templateObject->staticPrototype()));
- if (!res->splicePrototype(cx, &PlainObject::class_, proto))
+ if (!JSObject::splicePrototype(cx, res, &PlainObject::class_, proto))
return nullptr;
} else {
res->setGroup(group);
@@ -952,7 +952,7 @@ js::CreateThisForFunctionWithProto(JSContext* cx, HandleObject callee, HandleObj
}
if (res) {
- JSScript* script = callee->as<JSFunction>().getOrCreateScript(cx);
+ JSScript* script = JSFunction::getOrCreateScript(cx, callee.as<JSFunction>());
if (!script)
return nullptr;
TypeScript::SetThis(cx, script, TypeSet::ObjectType(res));
@@ -1143,19 +1143,18 @@ js::CloneObject(JSContext* cx, HandleObject obj, Handle<js::TaggedProto> proto)
}
static bool
-GetScriptArrayObjectElements(JSContext* cx, HandleObject obj, MutableHandle<GCVector<Value>> values)
+GetScriptArrayObjectElements(JSContext* cx, HandleArrayObject arr, MutableHandle<GCVector<Value>> values)
{
- MOZ_ASSERT(!obj->isSingleton());
- MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
- MOZ_ASSERT(!obj->isIndexed());
+ MOZ_ASSERT(!arr->isSingleton());
+ MOZ_ASSERT(!arr->isIndexed());
- size_t length = GetAnyBoxedOrUnboxedArrayLength(obj);
+ size_t length = arr->length();
if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), length))
return false;
- size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(obj);
+ size_t initlen = arr->getDenseInitializedLength();
for (size_t i = 0; i < initlen; i++)
- values[i].set(GetAnyBoxedOrUnboxedDenseElement(obj, i));
+ values[i].set(arr->getDenseElement(i));
return true;
}
@@ -1207,13 +1206,12 @@ js::DeepCloneObjectLiteral(JSContext* cx, HandleObject obj, NewObjectKind newKin
MOZ_ASSERT_IF(obj->isSingleton(),
cx->compartment()->behaviors().getSingletonsAsTemplates());
MOZ_ASSERT(obj->is<PlainObject>() ||
- obj->is<ArrayObject>() ||
- obj->is<UnboxedArrayObject>());
+ obj->is<ArrayObject>());
MOZ_ASSERT(newKind != SingletonObject);
- if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) {
+ if (obj->is<ArrayObject>()) {
Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
- if (!GetScriptArrayObjectElements(cx, obj, &values))
+ if (!GetScriptArrayObjectElements(cx, obj.as<ArrayObject>(), &values))
return nullptr;
// Deep clone any elements.
@@ -1327,9 +1325,8 @@ js::XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj)
{
if (mode == XDR_ENCODE) {
MOZ_ASSERT(obj->is<PlainObject>() ||
- obj->is<ArrayObject>() ||
- obj->is<UnboxedArrayObject>());
- isArray = (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) ? 1 : 0;
+ obj->is<ArrayObject>());
+ isArray = obj->is<ArrayObject>() ? 1 : 0;
}
if (!xdr->codeUint32(&isArray))
@@ -1341,8 +1338,11 @@ js::XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj)
if (isArray) {
Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
- if (mode == XDR_ENCODE && !GetScriptArrayObjectElements(cx, obj, &values))
- return false;
+ if (mode == XDR_ENCODE) {
+ RootedArrayObject arr(cx, &obj->as<ArrayObject>());
+ if (!GetScriptArrayObjectElements(cx, arr, &values))
+ return false;
+ }
uint32_t initialized;
if (mode == XDR_ENCODE)
@@ -1430,40 +1430,41 @@ js::XDRObjectLiteral(XDRState<XDR_ENCODE>* xdr, MutableHandleObject obj);
template bool
js::XDRObjectLiteral(XDRState<XDR_DECODE>* xdr, MutableHandleObject obj);
-bool
-NativeObject::fillInAfterSwap(JSContext* cx, const Vector<Value>& values, void* priv)
+/* static */ bool
+NativeObject::fillInAfterSwap(JSContext* cx, HandleNativeObject obj,
+ const Vector<Value>& values, void* priv)
{
// This object has just been swapped with some other object, and its shape
// no longer reflects its allocated size. Correct this information and
// fill the slots in with the specified values.
- MOZ_ASSERT(slotSpan() == values.length());
+ MOZ_ASSERT(obj->slotSpan() == values.length());
// Make sure the shape's numFixedSlots() is correct.
- size_t nfixed = gc::GetGCKindSlots(asTenured().getAllocKind(), getClass());
- if (nfixed != shape_->numFixedSlots()) {
- if (!generateOwnShape(cx))
+ size_t nfixed = gc::GetGCKindSlots(obj->asTenured().getAllocKind(), obj->getClass());
+ if (nfixed != obj->shape_->numFixedSlots()) {
+ if (!NativeObject::generateOwnShape(cx, obj))
return false;
- shape_->setNumFixedSlots(nfixed);
+ obj->shape_->setNumFixedSlots(nfixed);
}
- if (hasPrivate())
- setPrivate(priv);
+ if (obj->hasPrivate())
+ obj->setPrivate(priv);
else
MOZ_ASSERT(!priv);
- if (slots_) {
- js_free(slots_);
- slots_ = nullptr;
+ if (obj->slots_) {
+ js_free(obj->slots_);
+ obj->slots_ = nullptr;
}
- if (size_t ndynamic = dynamicSlotsCount(nfixed, values.length(), getClass())) {
- slots_ = cx->zone()->pod_malloc<HeapSlot>(ndynamic);
- if (!slots_)
+ if (size_t ndynamic = dynamicSlotsCount(nfixed, values.length(), obj->getClass())) {
+ obj->slots_ = cx->zone()->pod_malloc<HeapSlot>(ndynamic);
+ if (!obj->slots_)
return false;
- Debug_SetSlotRangeToCrashOnTouch(slots_, ndynamic);
+ Debug_SetSlotRangeToCrashOnTouch(obj->slots_, ndynamic);
}
- initSlotRange(0, values.begin(), values.length());
+ obj->initSlotRange(0, values.begin(), values.length());
return true;
}
@@ -1489,9 +1490,9 @@ JSObject::swap(JSContext* cx, HandleObject a, HandleObject b)
AutoCompartment ac(cx, a);
- if (!a->getGroup(cx))
+ if (!JSObject::getGroup(cx, a))
oomUnsafe.crash("JSObject::swap");
- if (!b->getGroup(cx))
+ if (!JSObject::getGroup(cx, b))
oomUnsafe.crash("JSObject::swap");
/*
@@ -1573,10 +1574,14 @@ JSObject::swap(JSContext* cx, HandleObject a, HandleObject b)
a->fixDictionaryShapeAfterSwap();
b->fixDictionaryShapeAfterSwap();
- if (na && !b->as<NativeObject>().fillInAfterSwap(cx, avals, apriv))
- oomUnsafe.crash("fillInAfterSwap");
- if (nb && !a->as<NativeObject>().fillInAfterSwap(cx, bvals, bpriv))
- oomUnsafe.crash("fillInAfterSwap");
+ if (na) {
+ if (!NativeObject::fillInAfterSwap(cx, b.as<NativeObject>(), avals, apriv))
+ oomUnsafe.crash("fillInAfterSwap");
+ }
+ if (nb) {
+ if (!NativeObject::fillInAfterSwap(cx, a.as<NativeObject>(), bvals, bpriv))
+ oomUnsafe.crash("fillInAfterSwap");
+ }
}
// Swapping the contents of two objects invalidates type sets which contain
@@ -1722,7 +1727,7 @@ DefineConstructorAndPrototype(JSContext* cx, HandleObject obj, JSProtoKey key, H
/* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
- if (ctor->getClass() == clasp && !ctor->splicePrototype(cx, clasp, tagged))
+ if (ctor->getClass() == clasp && !JSObject::splicePrototype(cx, ctor, clasp, tagged))
goto bad;
}
@@ -1839,10 +1844,10 @@ js::SetClassAndProto(JSContext* cx, HandleObject obj,
// We always generate a new shape if the object is a singleton,
// regardless of the uncacheable-proto flag. ICs may rely on
// this.
- if (!oldproto->as<NativeObject>().generateOwnShape(cx))
+ if (!NativeObject::generateOwnShape(cx, oldproto.as<NativeObject>()))
return false;
} else {
- if (!oldproto->setUncacheableProto(cx))
+ if (!JSObject::setUncacheableProto(cx, oldproto))
return false;
}
if (!obj->isDelegate()) {
@@ -1854,15 +1859,18 @@ js::SetClassAndProto(JSContext* cx, HandleObject obj,
oldproto = oldproto->staticPrototype();
}
- if (proto.isObject() && !proto.toObject()->setDelegate(cx))
- return false;
+ if (proto.isObject()) {
+ RootedObject protoObj(cx, proto.toObject());
+ if (!JSObject::setDelegate(cx, protoObj))
+ return false;
+ }
if (obj->isSingleton()) {
/*
* Just splice the prototype, but mark the properties as unknown for
* consistent behavior.
*/
- if (!obj->splicePrototype(cx, clasp, proto))
+ if (!JSObject::splicePrototype(cx, obj, clasp, proto))
return false;
MarkObjectGroupUnknownProperties(cx, obj->group());
return true;
@@ -1982,7 +1990,8 @@ js::GetObjectFromIncumbentGlobal(JSContext* cx, MutableHandleObject obj)
{
AutoCompartment ac(cx, globalObj);
- obj.set(globalObj->as<GlobalObject>().getOrCreateObjectPrototype(cx));
+ Handle<GlobalObject*> global = globalObj.as<GlobalObject>();
+ obj.set(GlobalObject::getOrCreateObjectPrototype(cx, global));
if (!obj)
return false;
}
@@ -2195,7 +2204,8 @@ js::LookupNameUnqualified(JSContext* cx, HandlePropertyName name, HandleObject e
// environments.
if (env->is<DebugEnvironmentProxy>()) {
RootedValue v(cx);
- if (!env->as<DebugEnvironmentProxy>().getMaybeSentinelValue(cx, id, &v))
+ Rooted<DebugEnvironmentProxy*> envProxy(cx, &env->as<DebugEnvironmentProxy>());
+ if (!DebugEnvironmentProxy::getMaybeSentinelValue(cx, envProxy, id, &v))
return false;
isTDZ = IsUninitializedLexical(v);
} else {
@@ -2312,11 +2322,6 @@ js::LookupOwnPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Shape**
// us the resolve hook won't define a property with this id.
if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj))
return false;
- } else if (obj->is<UnboxedArrayObject>()) {
- if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
- MarkNonNativePropertyFound<NoGC>(propp);
- return true;
- }
} else if (obj->is<TypedObject>()) {
if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
MarkNonNativePropertyFound<NoGC>(propp);
@@ -2331,9 +2336,18 @@ js::LookupOwnPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Shape**
}
static inline bool
-NativeGetPureInline(NativeObject* pobj, Shape* shape, Value* vp)
+NativeGetPureInline(NativeObject* pobj, jsid id, Shape* shape, Value* vp)
{
- /* Fail if we have a custom getter. */
+ if (IsImplicitDenseOrTypedArrayElement(shape)) {
+ // For simplicity we ignore the TypedArray with string index case.
+ if (!JSID_IS_INT(id))
+ return false;
+
+ *vp = pobj->getDenseOrTypedArrayElement(JSID_TO_INT(id));
+ return true;
+ }
+
+ // Fail if we have a custom getter.
if (!shape->hasDefaultGetter())
return false;
@@ -2360,13 +2374,13 @@ js::GetPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Value* vp)
return true;
}
- return pobj->isNative() && NativeGetPureInline(&pobj->as<NativeObject>(), shape, vp);
+ return pobj->isNative() && NativeGetPureInline(&pobj->as<NativeObject>(), id, shape, vp);
}
static inline bool
NativeGetGetterPureInline(Shape* shape, JSFunction** fp)
{
- if (shape->hasGetterObject()) {
+ if (!IsImplicitDenseOrTypedArrayElement(shape) && shape->hasGetterObject()) {
if (shape->getterObject()->is<JSFunction>()) {
*fp = &shape->getterObject()->as<JSFunction>();
return true;
@@ -2447,7 +2461,7 @@ js::HasOwnDataPropertyPure(JSContext* cx, JSObject* obj, jsid id, bool* result)
return true;
}
-bool
+/* static */ bool
JSObject::reportReadOnly(JSContext* cx, jsid id, unsigned report)
{
RootedValue val(cx, IdToValue(id));
@@ -2456,7 +2470,7 @@ JSObject::reportReadOnly(JSContext* cx, jsid id, unsigned report)
nullptr, nullptr);
}
-bool
+/* static */ bool
JSObject::reportNotConfigurable(JSContext* cx, jsid id, unsigned report)
{
RootedValue val(cx, IdToValue(id));
@@ -2465,10 +2479,10 @@ JSObject::reportNotConfigurable(JSContext* cx, jsid id, unsigned report)
nullptr, nullptr);
}
-bool
-JSObject::reportNotExtensible(JSContext* cx, unsigned report)
+/* static */ bool
+JSObject::reportNotExtensible(JSContext* cx, HandleObject obj, unsigned report)
{
- RootedValue val(cx, ObjectValue(*this));
+ RootedValue val(cx, ObjectValue(*obj));
return ReportValueErrorFlags(cx, report, JSMSG_OBJECT_NOT_EXTENSIBLE,
JSDVG_IGNORE_STACK, val, nullptr,
nullptr, nullptr);
@@ -2540,7 +2554,7 @@ js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto, JS::Object
// [[Prototype]] chain is always properly immutable, even in the presence
// of lazy standard classes.
if (obj->is<GlobalObject>()) {
- Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
if (!GlobalObject::ensureConstructor(cx, global, JSProto_Object))
return false;
}
@@ -2607,7 +2621,7 @@ js::PreventExtensions(JSContext* cx, HandleObject obj, ObjectOpResult& result, I
}
}
- if (!obj->setFlags(cx, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE)) {
+ if (!JSObject::setFlags(cx, obj, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE)) {
// We failed to mark the object non-extensible, so reset the frozen
// flag on the elements.
MOZ_ASSERT(obj->nonProxyIsExtensible());
@@ -2747,7 +2761,7 @@ js::SetImmutablePrototype(ExclusiveContext* cx, HandleObject obj, bool* succeede
return Proxy::setImmutablePrototype(cx->asJSContext(), obj, succeeded);
}
- if (!obj->setFlags(cx, BaseShape::IMMUTABLE_PROTOTYPE))
+ if (!JSObject::setFlags(cx, obj, BaseShape::IMMUTABLE_PROTOTYPE))
return false;
*succeeded = true;
return true;
@@ -2857,24 +2871,6 @@ js::GetObjectClassName(JSContext* cx, HandleObject obj)
/* * */
-bool
-js::HasDataProperty(JSContext* cx, NativeObject* obj, jsid id, Value* vp)
-{
- if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
- *vp = obj->getDenseElement(JSID_TO_INT(id));
- return true;
- }
-
- if (Shape* shape = obj->lookup(cx, id)) {
- if (shape->hasDefaultGetter() && shape->hasSlot()) {
- *vp = obj->getSlot(shape->slot());
- return true;
- }
- }
-
- return false;
-}
-
extern bool
PropertySpecNameToId(JSContext* cx, const char* name, MutableHandleId id,
js::PinningBehavior pin = js::DoNotPinAtom);
@@ -2990,7 +2986,7 @@ JS::OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHan
/* Optimize (new String(...)).toString(). */
if (clasp == &StringObject::class_) {
StringObject* nobj = &obj->as<StringObject>();
- if (ClassMethodIsNative(cx, nobj, &StringObject::class_, id, str_toString)) {
+ if (HasNativeMethodPure(nobj, cx->names().toString, str_toString, cx)) {
vp.setString(nobj->unbox());
return true;
}
@@ -3012,7 +3008,7 @@ JS::OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHan
/* Optimize new String(...).valueOf(). */
if (clasp == &StringObject::class_) {
StringObject* nobj = &obj->as<StringObject>();
- if (ClassMethodIsNative(cx, nobj, &StringObject::class_, id, str_toString)) {
+ if (HasNativeMethodPure(nobj, cx->names().valueOf, str_toString, cx)) {
vp.setString(nobj->unbox());
return true;
}
@@ -3021,7 +3017,7 @@ JS::OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHan
/* Optimize new Number(...).valueOf(). */
if (clasp == &NumberObject::class_) {
NumberObject* nobj = &obj->as<NumberObject>();
- if (ClassMethodIsNative(cx, nobj, &NumberObject::class_, id, num_valueOf)) {
+ if (HasNativeMethodPure(nobj, cx->names().valueOf, num_valueOf, cx)) {
vp.setNumber(nobj->unbox());
return true;
}
@@ -3680,16 +3676,6 @@ JSObject::allocKindForTenure(const js::Nursery& nursery) const
if (IsProxy(this))
return as<ProxyObject>().allocKindForTenure();
- // Unboxed arrays use inline data if their size is small enough.
- if (is<UnboxedArrayObject>()) {
- const UnboxedArrayObject* nobj = &as<UnboxedArrayObject>();
- size_t nbytes = UnboxedArrayObject::offsetOfInlineElements() +
- nobj->capacity() * nobj->elementSize();
- if (nbytes <= JSObject::MAX_BYTE_SIZE)
- return GetGCObjectKindForBytes(nbytes);
- return AllocKind::OBJECT0;
- }
-
// Inlined typed objects are followed by their data, so make sure we copy
// it all over to the new object.
if (is<InlineTypedObject>()) {
@@ -3873,10 +3859,10 @@ displayAtomFromObjectGroup(ObjectGroup& group)
return script->function()->displayAtom();
}
-bool
-JSObject::constructorDisplayAtom(JSContext* cx, js::MutableHandleAtom name)
+/* static */ bool
+JSObject::constructorDisplayAtom(JSContext* cx, js::HandleObject obj, js::MutableHandleAtom name)
{
- ObjectGroup *g = getGroup(cx);
+ ObjectGroup *g = JSObject::getGroup(cx, obj);
if (!g)
return false;
diff --git a/js/src/jsobj.h b/js/src/jsobj.h
index af79131af..db2c22b76 100644
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -200,8 +200,8 @@ class JSObject : public js::gc::Cell
GENERATE_SHAPE
};
- bool setFlags(js::ExclusiveContext* cx, js::BaseShape::Flag flags,
- GenerateShape generateShape = GENERATE_NONE);
+ static bool setFlags(js::ExclusiveContext* cx, JS::HandleObject obj, js::BaseShape::Flag flags,
+ GenerateShape generateShape = GENERATE_NONE);
inline bool hasAllFlags(js::BaseShape::Flag flags) const;
/*
@@ -214,16 +214,16 @@ class JSObject : public js::gc::Cell
* (see Purge{Scope,Proto}Chain in jsobj.cpp).
*/
inline bool isDelegate() const;
- bool setDelegate(js::ExclusiveContext* cx) {
- return setFlags(cx, js::BaseShape::DELEGATE, GENERATE_SHAPE);
+ static bool setDelegate(js::ExclusiveContext* cx, JS::HandleObject obj) {
+ return setFlags(cx, obj, js::BaseShape::DELEGATE, GENERATE_SHAPE);
}
inline bool isBoundFunction() const;
inline bool hasSpecialEquality() const;
inline bool watched() const;
- bool setWatched(js::ExclusiveContext* cx) {
- return setFlags(cx, js::BaseShape::WATCHED, GENERATE_SHAPE);
+ static bool setWatched(js::ExclusiveContext* cx, JS::HandleObject obj) {
+ return setFlags(cx, obj, js::BaseShape::WATCHED, GENERATE_SHAPE);
}
// A "qualified" varobj is the object on which "qualified" variable
@@ -247,8 +247,8 @@ class JSObject : public js::gc::Cell
// (e.g., Gecko and XPConnect), as they often wish to run scripts under a
// scope that captures var bindings.
inline bool isQualifiedVarObj() const;
- bool setQualifiedVarObj(js::ExclusiveContext* cx) {
- return setFlags(cx, js::BaseShape::QUALIFIED_VAROBJ);
+ static bool setQualifiedVarObj(js::ExclusiveContext* cx, JS::HandleObject obj) {
+ return setFlags(cx, obj, js::BaseShape::QUALIFIED_VAROBJ);
}
// An "unqualified" varobj is the object on which "unqualified"
@@ -262,11 +262,11 @@ class JSObject : public js::gc::Cell
// generate a new shape when their prototype changes, regardless of this
// hasUncacheableProto flag.
inline bool hasUncacheableProto() const;
- bool setUncacheableProto(js::ExclusiveContext* cx) {
- MOZ_ASSERT(hasStaticPrototype(),
+ static bool setUncacheableProto(js::ExclusiveContext* cx, JS::HandleObject obj) {
+ MOZ_ASSERT(obj->hasStaticPrototype(),
"uncacheability as a concept is only applicable to static "
"(not dynamically-computed) prototypes");
- return setFlags(cx, js::BaseShape::UNCACHEABLE_PROTO, GENERATE_SHAPE);
+ return setFlags(cx, obj, js::BaseShape::UNCACHEABLE_PROTO, GENERATE_SHAPE);
}
/*
@@ -274,8 +274,8 @@ class JSObject : public js::gc::Cell
* PropertyTree::MAX_HEIGHT.
*/
inline bool hadElementsAccess() const;
- bool setHadElementsAccess(js::ExclusiveContext* cx) {
- return setFlags(cx, js::BaseShape::HAD_ELEMENTS_ACCESS);
+ static bool setHadElementsAccess(js::ExclusiveContext* cx, JS::HandleObject obj) {
+ return setFlags(cx, obj, js::BaseShape::HAD_ELEMENTS_ACCESS);
}
/*
@@ -288,7 +288,8 @@ class JSObject : public js::gc::Cell
* If this object was instantiated with `new Ctor`, return the constructor's
* display atom. Otherwise, return nullptr.
*/
- bool constructorDisplayAtom(JSContext* cx, js::MutableHandleAtom name);
+ static bool constructorDisplayAtom(JSContext* cx, js::HandleObject obj,
+ js::MutableHandleAtom name);
/*
* The same as constructorDisplayAtom above, however if this object has a
@@ -348,7 +349,7 @@ class JSObject : public js::gc::Cell
// Change an existing object to have a singleton group.
static bool changeToSingleton(JSContext* cx, js::HandleObject obj);
- inline js::ObjectGroup* getGroup(JSContext* cx);
+ static inline js::ObjectGroup* getGroup(JSContext* cx, js::HandleObject obj);
const js::GCPtrObjectGroup& groupFromGC() const {
/* Direct field access for use by GC. */
@@ -420,8 +421,8 @@ class JSObject : public js::gc::Cell
* is purged on GC.
*/
inline bool isIteratedSingleton() const;
- bool setIteratedSingleton(js::ExclusiveContext* cx) {
- return setFlags(cx, js::BaseShape::ITERATED_SINGLETON);
+ static bool setIteratedSingleton(js::ExclusiveContext* cx, JS::HandleObject obj) {
+ return setFlags(cx, obj, js::BaseShape::ITERATED_SINGLETON);
}
/*
@@ -433,18 +434,19 @@ class JSObject : public js::gc::Cell
// Mark an object as having its 'new' script information cleared.
inline bool wasNewScriptCleared() const;
- bool setNewScriptCleared(js::ExclusiveContext* cx) {
- return setFlags(cx, js::BaseShape::NEW_SCRIPT_CLEARED);
+ static bool setNewScriptCleared(js::ExclusiveContext* cx, JS::HandleObject obj) {
+ return setFlags(cx, obj, js::BaseShape::NEW_SCRIPT_CLEARED);
}
/* Set a new prototype for an object with a singleton type. */
- bool splicePrototype(JSContext* cx, const js::Class* clasp, js::Handle<js::TaggedProto> proto);
+ static bool splicePrototype(JSContext* cx, js::HandleObject obj, const js::Class* clasp,
+ js::Handle<js::TaggedProto> proto);
/*
* For bootstrapping, whether to splice a prototype for Function.prototype
* or the global object.
*/
- bool shouldSplicePrototype(JSContext* cx);
+ bool shouldSplicePrototype();
/*
* Environment chains.
@@ -518,8 +520,9 @@ class JSObject : public js::gc::Cell
public:
static bool reportReadOnly(JSContext* cx, jsid id, unsigned report = JSREPORT_ERROR);
- bool reportNotConfigurable(JSContext* cx, jsid id, unsigned report = JSREPORT_ERROR);
- bool reportNotExtensible(JSContext* cx, unsigned report = JSREPORT_ERROR);
+ static bool reportNotConfigurable(JSContext* cx, jsid id, unsigned report = JSREPORT_ERROR);
+ static bool reportNotExtensible(JSContext* cx, js::HandleObject obj,
+ unsigned report = JSREPORT_ERROR);
static bool nonNativeSetProperty(JSContext* cx, js::HandleObject obj, js::HandleId id,
js::HandleValue v, js::HandleValue receiver,
diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h
index 6be4d0d28..c132ee6b2 100644
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -32,21 +32,6 @@
#include "vm/ShapedObject-inl.h"
#include "vm/TypeInference-inl.h"
-namespace js {
-
-// This is needed here for ensureShape() below.
-inline bool
-MaybeConvertUnboxedObjectToNative(ExclusiveContext* cx, JSObject* obj)
-{
- if (obj->is<UnboxedPlainObject>())
- return UnboxedPlainObject::convertToNative(cx->asJSContext(), obj);
- if (obj->is<UnboxedArrayObject>())
- return UnboxedArrayObject::convertToNative(cx->asJSContext(), obj);
- return true;
-}
-
-} // namespace js
-
inline js::Shape*
JSObject::maybeShape() const
{
@@ -59,8 +44,6 @@ JSObject::maybeShape() const
inline js::Shape*
JSObject::ensureShape(js::ExclusiveContext* cx)
{
- if (!js::MaybeConvertUnboxedObjectToNative(cx, this))
- return nullptr;
js::Shape* shape = maybeShape();
MOZ_ASSERT(shape);
return shape;
@@ -134,17 +117,16 @@ JSObject::setSingleton(js::ExclusiveContext* cx, js::HandleObject obj)
return true;
}
-inline js::ObjectGroup*
-JSObject::getGroup(JSContext* cx)
+/* static */ inline js::ObjectGroup*
+JSObject::getGroup(JSContext* cx, js::HandleObject obj)
{
- MOZ_ASSERT(cx->compartment() == compartment());
- if (hasLazyGroup()) {
- JS::RootedObject self(cx, this);
- if (cx->compartment() != compartment())
+ MOZ_ASSERT(cx->compartment() == obj->compartment());
+ if (obj->hasLazyGroup()) {
+ if (cx->compartment() != obj->compartment())
MOZ_CRASH();
- return makeLazyGroup(cx, self);
+ return makeLazyGroup(cx, obj);
}
- return group_;
+ return obj->group_;
}
inline void
@@ -574,48 +556,30 @@ IsNativeFunction(const js::Value& v, JSNative native)
return IsFunctionObject(v, &fun) && fun->maybeNative() == native;
}
-/*
- * When we have an object of a builtin class, we don't quite know what its
- * valueOf/toString methods are, since these methods may have been overwritten
- * or shadowed. However, we can still do better than the general case by
- * hard-coding the necessary properties for us to find the native we expect.
- *
- * TODO: a per-thread shape-based cache would be faster and simpler.
- */
+// Return whether looking up a method on 'obj' definitely resolves to the
+// original specified native function. The method may conservatively return
+// 'false' in the case of proxies or other non-native objects.
static MOZ_ALWAYS_INLINE bool
-ClassMethodIsNative(JSContext* cx, NativeObject* obj, const Class* clasp, jsid methodid, JSNative native)
+HasNativeMethodPure(JSObject* obj, PropertyName* name, JSNative native, JSContext* cx)
{
- MOZ_ASSERT(obj->getClass() == clasp);
-
Value v;
- if (!HasDataProperty(cx, obj, methodid, &v)) {
- JSObject* proto = obj->staticPrototype();
- if (!proto || proto->getClass() != clasp || !HasDataProperty(cx, &proto->as<NativeObject>(), methodid, &v))
- return false;
- }
+ if (!GetPropertyPure(cx, obj, NameToId(name), &v))
+ return false;
return IsNativeFunction(v, native);
}
-// Return whether looking up 'valueOf' on 'obj' definitely resolves to the
-// original Object.prototype.valueOf. The method may conservatively return
-// 'false' in the case of proxies or other non-native objects.
+// Return whether 'obj' definitely has no @@toPrimitive method.
static MOZ_ALWAYS_INLINE bool
-HasObjectValueOf(JSObject* obj, JSContext* cx)
+HasNoToPrimitiveMethodPure(JSObject* obj, JSContext* cx)
{
- if (obj->is<ProxyObject>() || !obj->isNative())
+ jsid id = SYMBOL_TO_JSID(cx->wellKnownSymbols().toPrimitive);
+ JSObject* pobj;
+ Shape* shape;
+ if (!LookupPropertyPure(cx, obj, id, &pobj, &shape))
return false;
- jsid valueOf = NameToId(cx->names().valueOf);
-
- Value v;
- while (!HasDataProperty(cx, &obj->as<NativeObject>(), valueOf, &v)) {
- obj = obj->staticPrototype();
- if (!obj || obj->is<ProxyObject>() || !obj->isNative())
- return false;
- }
-
- return IsNativeFunction(v, obj_valueOf);
+ return !shape;
}
/* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */
diff --git a/js/src/json.cpp b/js/src/json.cpp
index 425a2f117..08382b97b 100644
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -971,9 +971,9 @@ static const JSFunctionSpec json_static_methods[] = {
JSObject*
js::InitJSONClass(JSContext* cx, HandleObject obj)
{
- Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
- RootedObject proto(cx, global->getOrCreateObjectPrototype(cx));
+ RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
if (!proto)
return nullptr;
RootedObject JSON(cx, NewObjectWithGivenProto(cx, &JSONClass, proto, SingletonObject));
diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp
index 6adb5401e..d1ae3cd5e 100644
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -132,7 +132,8 @@ js::StackUses(JSScript* script, jsbytecode* pc)
return 2 + GET_ARGC(pc) + 1;
default:
/* stack: fun, this, [argc arguments] */
- MOZ_ASSERT(op == JSOP_CALL || op == JSOP_EVAL || op == JSOP_CALLITER ||
+ MOZ_ASSERT(op == JSOP_CALL || op == JSOP_CALL_IGNORES_RV || op == JSOP_EVAL ||
+ op == JSOP_CALLITER ||
op == JSOP_STRICTEVAL || op == JSOP_FUNCALL || op == JSOP_FUNAPPLY);
return 2 + GET_ARGC(pc);
}
@@ -1363,6 +1364,7 @@ ExpressionDecompiler::decompilePC(jsbytecode* pc)
case JSOP_NEWTARGET:
return write("new.target");
case JSOP_CALL:
+ case JSOP_CALL_IGNORES_RV:
case JSOP_CALLITER:
case JSOP_FUNCALL:
return decompilePCForStackOperand(pc, -int32_t(GET_ARGC(pc) + 2)) &&
@@ -1662,7 +1664,7 @@ DecompileArgumentFromStack(JSContext* cx, int formalIndex, char** res)
/* Don't handle getters, setters or calls from fun.call/fun.apply. */
JSOp op = JSOp(*current);
- if (op != JSOP_CALL && op != JSOP_NEW)
+ if (op != JSOP_CALL && op != JSOP_CALL_IGNORES_RV && op != JSOP_NEW)
return true;
if (static_cast<unsigned>(formalIndex) >= GET_ARGC(current))
@@ -1725,6 +1727,8 @@ js::CallResultEscapes(jsbytecode* pc)
if (*pc == JSOP_CALL)
pc += JSOP_CALL_LENGTH;
+ else if (*pc == JSOP_CALL_IGNORES_RV)
+ pc += JSOP_CALL_IGNORES_RV_LENGTH;
else if (*pc == JSOP_SPREADCALL)
pc += JSOP_SPREADCALL_LENGTH;
else
@@ -2214,6 +2218,7 @@ GenerateLcovInfo(JSContext* cx, JSCompartment* comp, GenericPrinter& out)
return false;
RootedScript script(cx);
+ RootedFunction fun(cx);
do {
script = queue.popCopy();
compCover.collectCodeCoverageInfo(comp, script->sourceObject(), script);
@@ -2231,15 +2236,15 @@ GenerateLcovInfo(JSContext* cx, JSCompartment* comp, GenericPrinter& out)
// Only continue on JSFunction objects.
if (!obj->is<JSFunction>())
continue;
- JSFunction& fun = obj->as<JSFunction>();
+ fun = &obj->as<JSFunction>();
// Let's skip wasm for now.
- if (!fun.isInterpreted())
+ if (!fun->isInterpreted())
continue;
// Queue the script in the list of script associated to the
// current source.
- JSScript* childScript = fun.getOrCreateScript(cx);
+ JSScript* childScript = JSFunction::getOrCreateScript(cx, fun);
if (!childScript || !queue.append(childScript))
return false;
}
diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp
index 33ae56d6f..fc7438e3b 100644
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -235,7 +235,8 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
{
uint32_t begin = script->sourceStart();
uint32_t end = script->sourceEnd();
- uint32_t preludeStart = script->preludeStart();
+ uint32_t toStringStart = script->toStringStart();
+ uint32_t toStringEnd = script->toStringEnd();
uint32_t lineno = script->lineno();
uint32_t column = script->column();
@@ -243,7 +244,8 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
packedFields = lazy->packedFields();
MOZ_ASSERT(begin == lazy->begin());
MOZ_ASSERT(end == lazy->end());
- MOZ_ASSERT(preludeStart == lazy->preludeStart());
+ MOZ_ASSERT(toStringStart == lazy->toStringStart());
+ MOZ_ASSERT(toStringEnd == lazy->toStringEnd());
MOZ_ASSERT(lineno == lazy->lineno());
MOZ_ASSERT(column == lazy->column());
// We can assert we have no inner functions because we don't
@@ -257,7 +259,12 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
if (mode == XDR_DECODE) {
lazy.set(LazyScript::Create(cx, fun, script, enclosingScope, script,
- packedFields, begin, end, preludeStart, lineno, column));
+ packedFields, begin, end, toStringStart, lineno, column));
+
+ if (!lazy)
+ return false;
+
+ lazy->setToStringEnd(toStringEnd);
// As opposed to XDRLazyScript, we need to restore the runtime bits
// of the script, as we are trying to match the fact this function
@@ -319,6 +326,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
IsStarGenerator,
IsAsync,
HasRest,
+ IsExprBody,
OwnSource,
ExplicitUseStrict,
SelfHosted,
@@ -436,6 +444,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
scriptBits |= (1 << IsAsync);
if (script->hasRest())
scriptBits |= (1 << HasRest);
+ if (script->isExprBody())
+ scriptBits |= (1 << IsExprBody);
if (script->hasSingletons())
scriptBits |= (1 << HasSingleton);
if (script->treatAsRunOnce())
@@ -519,7 +529,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
sourceObject = &enclosingScript->sourceObject()->as<ScriptSourceObject>();
}
- script = JSScript::Create(cx, options, sourceObject, 0, 0, 0);
+ script = JSScript::Create(cx, options, sourceObject, 0, 0, 0, 0);
if (!script)
return false;
@@ -589,6 +599,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
script->setAsyncKind(AsyncFunction);
if (scriptBits & (1 << HasRest))
script->setHasRest();
+ if (scriptBits & (1 << IsExprBody))
+ script->setIsExprBody();
}
JS_STATIC_ASSERT(sizeof(jsbytecode) == 1);
@@ -602,7 +614,9 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
return false;
if (!xdr->codeUint32(&script->sourceEnd_))
return false;
- if (!xdr->codeUint32(&script->preludeStart_))
+ if (!xdr->codeUint32(&script->toStringStart_))
+ return false;
+ if (!xdr->codeUint32(&script->toStringEnd_))
return false;
if (!xdr->codeUint32(&lineno) ||
@@ -934,7 +948,8 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript
{
uint32_t begin;
uint32_t end;
- uint32_t preludeStart;
+ uint32_t toStringStart;
+ uint32_t toStringEnd;
uint32_t lineno;
uint32_t column;
uint64_t packedFields;
@@ -948,14 +963,16 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript
begin = lazy->begin();
end = lazy->end();
- preludeStart = lazy->preludeStart();
+ toStringStart = lazy->toStringStart();
+ toStringEnd = lazy->toStringEnd();
lineno = lazy->lineno();
column = lazy->column();
packedFields = lazy->packedFields();
}
if (!xdr->codeUint32(&begin) || !xdr->codeUint32(&end) ||
- !xdr->codeUint32(&preludeStart) ||
+ !xdr->codeUint32(&toStringStart) ||
+ !xdr->codeUint32(&toStringEnd) ||
!xdr->codeUint32(&lineno) || !xdr->codeUint32(&column) ||
!xdr->codeUint64(&packedFields))
{
@@ -964,9 +981,10 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript
if (mode == XDR_DECODE) {
lazy.set(LazyScript::Create(cx, fun, nullptr, enclosingScope, enclosingScript,
- packedFields, begin, end, preludeStart, lineno, column));
+ packedFields, begin, end, toStringStart, lineno, column));
if (!lazy)
return false;
+ lazy->setToStringEnd(toStringEnd);
fun->initLazyScript(lazy);
}
}
@@ -1010,6 +1028,15 @@ JSScript::setSourceObject(JSObject* object)
sourceObject_ = object;
}
+void
+JSScript::setDefaultClassConstructorSpan(JSObject* sourceObject, uint32_t start, uint32_t end)
+{
+ MOZ_ASSERT(isDefaultClassConstructor());
+ setSourceObject(sourceObject);
+ toStringStart_ = start;
+ toStringEnd_ = end;
+}
+
js::ScriptSourceObject&
JSScript::scriptSourceUnwrap() const {
return UncheckedUnwrap(sourceObject())->as<ScriptSourceObject>();
@@ -1430,18 +1457,18 @@ JSScript::loadSource(JSContext* cx, ScriptSource* ss, bool* worked)
return true;
}
-JSFlatString*
-JSScript::sourceData(JSContext* cx)
+/* static */ JSFlatString*
+JSScript::sourceData(JSContext* cx, HandleScript script)
{
- MOZ_ASSERT(scriptSource()->hasSourceData());
- return scriptSource()->substring(cx, sourceStart(), sourceEnd());
+ MOZ_ASSERT(script->scriptSource()->hasSourceData());
+ return script->scriptSource()->substring(cx, script->sourceStart(), script->sourceEnd());
}
-JSFlatString*
-JSScript::sourceDataWithPrelude(JSContext* cx)
+/* static */ JSFlatString*
+JSScript::sourceDataForToString(JSContext* cx, HandleScript script)
{
- MOZ_ASSERT(scriptSource()->hasSourceData());
- return scriptSource()->substring(cx, preludeStart(), sourceEnd());
+ MOZ_ASSERT(script->scriptSource()->hasSourceData());
+ return script->scriptSource()->substring(cx, script->toStringStart(), script->toStringEnd());
}
UncompressedSourceCache::AutoHoldEntry::AutoHoldEntry()
@@ -2443,9 +2470,15 @@ JSScript::initCompartment(ExclusiveContext* cx)
/* static */ JSScript*
JSScript::Create(ExclusiveContext* cx, const ReadOnlyCompileOptions& options,
HandleObject sourceObject, uint32_t bufStart, uint32_t bufEnd,
- uint32_t preludeStart)
+ uint32_t toStringStart, uint32_t toStringEnd)
{
+ // bufStart and bufEnd specify the range of characters parsed by the
+ // Parser to produce this script. toStringStart and toStringEnd specify
+ // the range of characters to be returned for Function.prototype.toString.
MOZ_ASSERT(bufStart <= bufEnd);
+ MOZ_ASSERT(toStringStart <= toStringEnd);
+ MOZ_ASSERT(toStringStart <= bufStart);
+ MOZ_ASSERT(toStringEnd >= bufEnd);
RootedScript script(cx, Allocate<JSScript>(cx));
if (!script)
@@ -2465,7 +2498,8 @@ JSScript::Create(ExclusiveContext* cx, const ReadOnlyCompileOptions& options,
script->setSourceObject(sourceObject);
script->sourceStart_ = bufStart;
script->sourceEnd_ = bufEnd;
- script->preludeStart_ = preludeStart;
+ script->toStringStart_ = toStringStart;
+ script->toStringEnd_ = toStringEnd;
return script;
}
@@ -2660,6 +2694,8 @@ JSScript::initFromFunctionBox(ExclusiveContext* cx, HandleScript script,
script->setAsyncKind(funbox->asyncKind());
if (funbox->hasRest())
script->setHasRest();
+ if (funbox->isExprBody())
+ script->setIsExprBody();
PositionalFormalParameterIter fi(script);
while (fi && !fi.closedOver())
@@ -3261,7 +3297,7 @@ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
} else {
if (innerFun->isInterpretedLazy()) {
AutoCompartment ac(cx, innerFun);
- if (!innerFun->getOrCreateScript(cx))
+ if (!JSFunction::getOrCreateScript(cx, innerFun))
return false;
}
@@ -3320,6 +3356,7 @@ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
dst->isDefaultClassConstructor_ = src->isDefaultClassConstructor();
dst->isAsync_ = src->asyncKind() == AsyncFunction;
dst->hasRest_ = src->hasRest_;
+ dst->isExprBody_ = src->isExprBody_;
if (nconsts != 0) {
GCPtrValue* vector = Rebase<GCPtrValue>(dst, src, src->consts()->vector);
@@ -3399,7 +3436,7 @@ CreateEmptyScriptForClone(JSContext* cx, HandleScript src)
.setVersion(src->getVersion());
return JSScript::Create(cx, options, sourceObject, src->sourceStart(), src->sourceEnd(),
- src->preludeStart());
+ src->toStringStart(), src->toStringEnd());
}
JSScript*
@@ -3950,7 +3987,7 @@ JSScript::formalLivesInArgumentsObject(unsigned argSlot)
LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
uint32_t begin, uint32_t end,
- uint32_t preludeStart, uint32_t lineno, uint32_t column)
+ uint32_t toStringStart, uint32_t lineno, uint32_t column)
: script_(nullptr),
function_(fun),
enclosingScope_(nullptr),
@@ -3959,11 +3996,13 @@ LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
packedFields_(packedFields),
begin_(begin),
end_(end),
- preludeStart_(preludeStart),
+ toStringStart_(toStringStart),
+ toStringEnd_(end),
lineno_(lineno),
column_(column)
{
MOZ_ASSERT(begin <= end);
+ MOZ_ASSERT(toStringStart <= begin);
}
void
@@ -4009,7 +4048,7 @@ LazyScript::maybeForwardedScriptSource() const
/* static */ LazyScript*
LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun,
uint64_t packedFields, uint32_t begin, uint32_t end,
- uint32_t preludeStart, uint32_t lineno, uint32_t column)
+ uint32_t toStringStart, uint32_t lineno, uint32_t column)
{
union {
PackedView p;
@@ -4038,7 +4077,7 @@ LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun,
cx->compartment()->scheduleDelazificationForDebugger();
return new (res) LazyScript(fun, table.forget(), packed, begin, end,
- preludeStart, lineno, column);
+ toStringStart, lineno, column);
}
/* static */ LazyScript*
@@ -4047,7 +4086,7 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
Handle<GCVector<JSFunction*, 8>> innerFunctions,
JSVersion version,
uint32_t begin, uint32_t end,
- uint32_t preludeStart, uint32_t lineno, uint32_t column)
+ uint32_t toStringStart, uint32_t lineno, uint32_t column)
{
union {
PackedView p;
@@ -4059,6 +4098,7 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
p.hasThisBinding = false;
p.isAsync = false;
p.hasRest = false;
+ p.isExprBody = false;
p.numClosedOverBindings = closedOverBindings.length();
p.numInnerFunctions = innerFunctions.length();
p.generatorKindBits = GeneratorKindAsBits(NotGenerator);
@@ -4070,7 +4110,7 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
p.isDerivedClassConstructor = false;
p.needsHomeObject = false;
- LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, preludeStart,
+ LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, toStringStart,
lineno, column);
if (!res)
return nullptr;
@@ -4092,7 +4132,7 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
HandleScript script, HandleScope enclosingScope,
HandleScript enclosingScript,
uint64_t packedFields, uint32_t begin, uint32_t end,
- uint32_t preludeStart, uint32_t lineno, uint32_t column)
+ uint32_t toStringStart, uint32_t lineno, uint32_t column)
{
// Dummy atom which is not a valid property name.
RootedAtom dummyAtom(cx, cx->names().comma);
@@ -4101,7 +4141,7 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
// holding this lazy script.
HandleFunction dummyFun = fun;
- LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, preludeStart,
+ LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, toStringStart,
lineno, column);
if (!res)
return nullptr;
@@ -4287,7 +4327,7 @@ JSScript::AutoDelazify::holdScript(JS::HandleFunction fun)
script_ = fun->nonLazyScript();
} else {
JSAutoCompartment ac(cx_, fun);
- script_ = fun->getOrCreateScript(cx_);
+ script_ = JSFunction::getOrCreateScript(cx_, fun);
if (script_) {
oldDoNotRelazify_ = script_->doNotRelazify_;
script_->setDoNotRelazify(true);
diff --git a/js/src/jsscript.h b/js/src/jsscript.h
index bb8635581..85eb2938d 100644
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -853,19 +853,36 @@ class JSScript : public js::gc::TenuredCell
uint32_t bodyScopeIndex_; /* index into the scopes array of the body scope */
- // Range of characters in scriptSource which contains this script's source.
- // each field points the following location.
+ // Range of characters in scriptSource which contains this script's
+ // source, that is, the range used by the Parser to produce this script.
+ //
+ // Most scripted functions have sourceStart_ == toStringStart_ and
+ // sourceEnd_ == toStringEnd_. However, for functions with extra
+ // qualifiers (e.g. generators, async) and for class constructors (which
+ // need to return the entire class source), their values differ.
+ //
+ // Each field points the following locations.
//
// function * f(a, b) { return a + b; }
// ^ ^ ^
// | | |
// | sourceStart_ sourceEnd_
- // |
- // preludeStart_
+ // | |
+ // toStringStart_ toStringEnd_
+ //
+ // And, in the case of class constructors, an additional toStringEnd
+ // offset is used.
//
+ // class C { constructor() { this.field = 42; } }
+ // ^ ^ ^ ^
+ // | | | `---------`
+ // | sourceStart_ sourceEnd_ |
+ // | |
+ // toStringStart_ toStringEnd_
uint32_t sourceStart_;
uint32_t sourceEnd_;
- uint32_t preludeStart_;
+ uint32_t toStringStart_;
+ uint32_t toStringEnd_;
// Number of times the script has been called or has had backedges taken.
// When running in ion, also increased for any inlined scripts. Reset if
@@ -1021,12 +1038,13 @@ class JSScript : public js::gc::TenuredCell
bool isAsync_:1;
bool hasRest_:1;
+ bool isExprBody_:1;
// Add padding so JSScript is gc::Cell aligned. Make padding protected
// instead of private to suppress -Wunused-private-field compiler warnings.
protected:
#if JS_BITS_PER_WORD == 32
- uint32_t padding;
+ // Currently no padding is needed.
#endif
//
@@ -1036,8 +1054,9 @@ class JSScript : public js::gc::TenuredCell
public:
static JSScript* Create(js::ExclusiveContext* cx,
const JS::ReadOnlyCompileOptions& options,
- js::HandleObject sourceObject, uint32_t sourceStart,
- uint32_t sourceEnd, uint32_t preludeStart);
+ js::HandleObject sourceObject,
+ uint32_t sourceStart, uint32_t sourceEnd,
+ uint32_t toStringStart, uint32_t toStringEnd);
void initCompartment(js::ExclusiveContext* cx);
@@ -1184,8 +1203,12 @@ class JSScript : public js::gc::TenuredCell
return sourceEnd_;
}
- size_t preludeStart() const {
- return preludeStart_;
+ uint32_t toStringStart() const {
+ return toStringStart_;
+ }
+
+ uint32_t toStringEnd() const {
+ return toStringEnd_;
}
bool noScriptRval() const {
@@ -1329,6 +1352,13 @@ class JSScript : public js::gc::TenuredCell
hasRest_ = true;
}
+ bool isExprBody() const {
+ return isExprBody_;
+ }
+ void setIsExprBody() {
+ isExprBody_ = true;
+ }
+
void setNeedsHomeObject() {
needsHomeObject_ = true;
}
@@ -1464,6 +1494,7 @@ class JSScript : public js::gc::TenuredCell
bool isRelazifiable() const {
return (selfHosted() || lazyScript) && !hasInnerFunctions_ && !types_ &&
!isGenerator() && !hasBaselineScript() && !hasAnyIonScript() &&
+ !isDefaultClassConstructor() &&
!doNotRelazify_;
}
void setLazyScript(js::LazyScript* lazy) {
@@ -1491,7 +1522,7 @@ class JSScript : public js::gc::TenuredCell
* De-lazifies the canonical function. Must be called before entering code
* that expects the function to be non-lazy.
*/
- inline void ensureNonLazyCanonicalFunction(JSContext* cx);
+ inline void ensureNonLazyCanonicalFunction();
js::ModuleObject* module() const {
if (bodyScope()->is<js::ModuleScope>())
@@ -1510,8 +1541,8 @@ class JSScript : public js::gc::TenuredCell
// directly, via lazy arguments or a rest parameter.
bool mayReadFrameArgsDirectly();
- JSFlatString* sourceData(JSContext* cx);
- JSFlatString* sourceDataWithPrelude(JSContext* cx);
+ static JSFlatString* sourceData(JSContext* cx, JS::HandleScript script);
+ static JSFlatString* sourceDataForToString(JSContext* cx, JS::HandleScript script);
static bool loadSource(JSContext* cx, js::ScriptSource* ss, bool* worked);
@@ -1526,6 +1557,8 @@ class JSScript : public js::gc::TenuredCell
const char* filename() const { return scriptSource()->filename(); }
const char* maybeForwardedFilename() const { return maybeForwardedScriptSource()->filename(); }
+ void setDefaultClassConstructorSpan(JSObject* sourceObject, uint32_t start, uint32_t end);
+
public:
/* Return whether this script was compiled for 'eval' */
@@ -1931,12 +1964,11 @@ class LazyScript : public gc::TenuredCell
// instead of private to suppress -Wunused-private-field compiler warnings.
protected:
#if JS_BITS_PER_WORD == 32
- // uint32_t padding;
- // Currently no padding is needed.
+ uint32_t padding;
#endif
private:
- static const uint32_t NumClosedOverBindingsBits = 21;
+ static const uint32_t NumClosedOverBindingsBits = 20;
static const uint32_t NumInnerFunctionsBits = 20;
struct PackedView {
@@ -1946,7 +1978,12 @@ class LazyScript : public gc::TenuredCell
uint32_t shouldDeclareArguments : 1;
uint32_t hasThisBinding : 1;
uint32_t isAsync : 1;
+ uint32_t isExprBody : 1;
+
uint32_t numClosedOverBindings : NumClosedOverBindingsBits;
+
+ // -- 32bit boundary --
+
uint32_t numInnerFunctions : NumInnerFunctionsBits;
uint32_t generatorKindBits : 2;
@@ -1975,14 +2012,15 @@ class LazyScript : public gc::TenuredCell
// See the comment in JSScript for the details.
uint32_t begin_;
uint32_t end_;
- uint32_t preludeStart_;
+ uint32_t toStringStart_;
+ uint32_t toStringEnd_;
// Line and column of |begin_| position, that is the position where we
// start parsing.
uint32_t lineno_;
uint32_t column_;
LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
- uint32_t begin, uint32_t end, uint32_t preludeStart,
+ uint32_t begin, uint32_t end, uint32_t toStringStart,
uint32_t lineno, uint32_t column);
// Create a LazyScript without initializing the closedOverBindings and the
@@ -1990,7 +2028,7 @@ class LazyScript : public gc::TenuredCell
// with valid atoms and functions.
static LazyScript* CreateRaw(ExclusiveContext* cx, HandleFunction fun,
uint64_t packedData, uint32_t begin, uint32_t end,
- uint32_t preludeStart, uint32_t lineno, uint32_t column);
+ uint32_t toStringStart, uint32_t lineno, uint32_t column);
public:
static const uint32_t NumClosedOverBindingsLimit = 1 << NumClosedOverBindingsBits;
@@ -2002,7 +2040,7 @@ class LazyScript : public gc::TenuredCell
const frontend::AtomVector& closedOverBindings,
Handle<GCVector<JSFunction*, 8>> innerFunctions,
JSVersion version, uint32_t begin, uint32_t end,
- uint32_t preludeStart, uint32_t lineno, uint32_t column);
+ uint32_t toStringStart, uint32_t lineno, uint32_t column);
// Create a LazyScript and initialize the closedOverBindings and the
// innerFunctions with dummy values to be replaced in a later initialization
@@ -2017,11 +2055,11 @@ class LazyScript : public gc::TenuredCell
HandleScript script, HandleScope enclosingScope,
HandleScript enclosingScript,
uint64_t packedData, uint32_t begin, uint32_t end,
- uint32_t preludeStart, uint32_t lineno, uint32_t column);
+ uint32_t toStringStart, uint32_t lineno, uint32_t column);
void initRuntimeFields(uint64_t packedFields);
- inline JSFunction* functionDelazifying(JSContext* cx) const;
+ static inline JSFunction* functionDelazifying(JSContext* cx, Handle<LazyScript*>);
JSFunction* functionNonDelazifying() const {
return function_;
}
@@ -2104,6 +2142,13 @@ class LazyScript : public gc::TenuredCell
p_.hasRest = true;
}
+ bool isExprBody() const {
+ return p_.isExprBody;
+ }
+ void setIsExprBody() {
+ p_.isExprBody = true;
+ }
+
bool strict() const {
return p_.strict;
}
@@ -2190,8 +2235,11 @@ class LazyScript : public gc::TenuredCell
uint32_t end() const {
return end_;
}
- uint32_t preludeStart() const {
- return preludeStart_;
+ uint32_t toStringStart() const {
+ return toStringStart_;
+ }
+ uint32_t toStringEnd() const {
+ return toStringEnd_;
}
uint32_t lineno() const {
return lineno_;
@@ -2200,6 +2248,12 @@ class LazyScript : public gc::TenuredCell
return column_;
}
+ void setToStringEnd(uint32_t toStringEnd) {
+ MOZ_ASSERT(toStringStart_ <= toStringEnd);
+ MOZ_ASSERT(toStringEnd_ >= end_);
+ toStringEnd_ = toStringEnd;
+ }
+
bool hasUncompiledEnclosingScript() const;
friend class GCMarker;
diff --git a/js/src/jsscriptinlines.h b/js/src/jsscriptinlines.h
index da23804ac..e1052111b 100644
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -74,13 +74,13 @@ void
SetFrameArgumentsObject(JSContext* cx, AbstractFramePtr frame,
HandleScript script, JSObject* argsobj);
-inline JSFunction*
-LazyScript::functionDelazifying(JSContext* cx) const
+/* static */ inline JSFunction*
+LazyScript::functionDelazifying(JSContext* cx, Handle<LazyScript*> script)
{
- Rooted<const LazyScript*> self(cx, this);
- if (self->function_ && !self->function_->getOrCreateScript(cx))
+ RootedFunction fun(cx, script->function_);
+ if (script->function_ && !JSFunction::getOrCreateScript(cx, fun))
return nullptr;
- return self->function_;
+ return script->function_;
}
} // namespace js
@@ -100,7 +100,7 @@ JSScript::functionDelazifying() const
}
inline void
-JSScript::ensureNonLazyCanonicalFunction(JSContext* cx)
+JSScript::ensureNonLazyCanonicalFunction()
{
// Infallibly delazify the canonical script.
JSFunction* fun = function();
diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp
index 7765b1197..74f61b87d 100644
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -461,9 +461,13 @@ ToStringForStringFunction(JSContext* cx, HandleValue thisv)
RootedObject obj(cx, &thisv.toObject());
if (obj->is<StringObject>()) {
StringObject* nobj = &obj->as<StringObject>();
- Rooted<jsid> id(cx, NameToId(cx->names().toString));
- if (ClassMethodIsNative(cx, nobj, &StringObject::class_, id, str_toString))
+ // We have to make sure that the ToPrimitive call from ToString
+ // would be unobservable.
+ if (HasNoToPrimitiveMethodPure(nobj, cx) &&
+ HasNativeMethodPure(nobj, cx->names().toString, str_toString, cx))
+ {
return nobj->unbox();
+ }
}
} else if (thisv.isNullOrUndefined()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
@@ -2369,7 +2373,7 @@ js::str_replace_string_raw(JSContext* cx, HandleString string, HandleString patt
}
// ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18.
-static JSObject*
+static ArrayObject*
SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleLinearString sep,
HandleObjectGroup group)
{
@@ -2466,7 +2470,7 @@ SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleLinearS
}
// Fast-path for splitting a string into a character array via split("").
-static JSObject*
+static ArrayObject*
CharSplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleObjectGroup group)
{
size_t strLength = str->length();
@@ -2491,7 +2495,7 @@ CharSplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleObj
}
// ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18.
-JSObject*
+ArrayObject*
js::str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep, uint32_t limit)
{
@@ -2901,8 +2905,8 @@ StringObject::assignInitialShape(ExclusiveContext* cx, Handle<StringObject*> obj
{
MOZ_ASSERT(obj->empty());
- return obj->addDataProperty(cx, cx->names().length, LENGTH_SLOT,
- JSPROP_PERMANENT | JSPROP_READONLY);
+ return NativeObject::addDataProperty(cx, obj, cx->names().length, LENGTH_SLOT,
+ JSPROP_PERMANENT | JSPROP_READONLY);
}
JSObject*
@@ -2910,17 +2914,20 @@ js::InitStringClass(JSContext* cx, HandleObject obj)
{
MOZ_ASSERT(obj->isNative());
- Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
Rooted<JSString*> empty(cx, cx->runtime()->emptyString);
- RootedObject proto(cx, global->createBlankPrototype(cx, &StringObject::class_));
- if (!proto || !proto->as<StringObject>().init(cx, empty))
+ RootedObject proto(cx, GlobalObject::createBlankPrototype(cx, global, &StringObject::class_));
+ if (!proto)
+ return nullptr;
+ Handle<StringObject*> protoObj = proto.as<StringObject>();
+ if (!StringObject::init(cx, protoObj, empty))
return nullptr;
/* Now create the String function. */
RootedFunction ctor(cx);
- ctor = global->createConstructor(cx, StringConstructor, cx->names().String, 1,
- AllocKind::FUNCTION, &jit::JitInfo_String);
+ ctor = GlobalObject::createConstructor(cx, StringConstructor, cx->names().String, 1,
+ AllocKind::FUNCTION, &jit::JitInfo_String);
if (!ctor)
return nullptr;
diff --git a/js/src/jsstr.h b/js/src/jsstr.h
index 38fbfa85e..68175c826 100644
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -465,7 +465,7 @@ FileEscapedString(FILE* fp, const char* chars, size_t length, uint32_t quote)
return res;
}
-JSObject*
+ArrayObject*
str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep,
uint32_t limit);
diff --git a/js/src/jswatchpoint.cpp b/js/src/jswatchpoint.cpp
index e37323555..34479a990 100644
--- a/js/src/jswatchpoint.cpp
+++ b/js/src/jswatchpoint.cpp
@@ -64,7 +64,7 @@ WatchpointMap::watch(JSContext* cx, HandleObject obj, HandleId id,
{
MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id) || JSID_IS_SYMBOL(id));
- if (!obj->setWatched(cx))
+ if (!JSObject::setWatched(cx, obj))
return false;
Watchpoint w(handler, closure, false);
diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h
index 84ebe2732..5f3704e32 100644
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -136,7 +136,7 @@ class JS_FRIEND_API(Wrapper) : public BaseProxyHandler
static JSObject* New(JSContext* cx, JSObject* obj, const Wrapper* handler,
const WrapperOptions& options = WrapperOptions());
- static JSObject* Renew(JSContext* cx, JSObject* existing, JSObject* obj, const Wrapper* handler);
+ static JSObject* Renew(JSObject* existing, JSObject* obj, const Wrapper* handler);
static const Wrapper* wrapperHandler(JSObject* wrapper);
diff --git a/js/src/moz.build b/js/src/moz.build
index 888741138..a0f074d1c 100644
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -355,7 +355,6 @@ UNIFIED_SOURCES += [
'vm/UbiNode.cpp',
'vm/UbiNodeCensus.cpp',
'vm/UbiNodeShortestPaths.cpp',
- 'vm/UnboxedObject.cpp',
'vm/Unicode.cpp',
'vm/Value.cpp',
'vm/WeakMapPtr.cpp',
@@ -620,11 +619,11 @@ else:
'perf/pm_stub.cpp'
]
-GENERATED_FILES += ['jsautokw.h']
-jsautokw = GENERATED_FILES['jsautokw.h']
-jsautokw.script = 'jsautokw.py'
-jsautokw.inputs += [
- 'vm/Keywords.h'
+GENERATED_FILES += ['frontend/ReservedWordsGenerated.h']
+ReservedWordsGenerated = GENERATED_FILES['frontend/ReservedWordsGenerated.h']
+ReservedWordsGenerated.script = 'frontend/GenerateReservedWords.py'
+ReservedWordsGenerated.inputs += [
+ 'frontend/ReservedWords.h'
]
# JavaScript must be built shared, even for static builds, as it is used by
diff --git a/js/src/proxy/CrossCompartmentWrapper.cpp b/js/src/proxy/CrossCompartmentWrapper.cpp
index 2a6cb5400..246639956 100644
--- a/js/src/proxy/CrossCompartmentWrapper.cpp
+++ b/js/src/proxy/CrossCompartmentWrapper.cpp
@@ -89,7 +89,7 @@ CrossCompartmentWrapper::getPrototype(JSContext* cx, HandleObject wrapper,
if (!GetPrototype(cx, wrapped, protop))
return false;
if (protop) {
- if (!protop->setDelegate(cx))
+ if (!JSObject::setDelegate(cx, protop))
return false;
}
}
@@ -122,7 +122,7 @@ CrossCompartmentWrapper::getPrototypeIfOrdinary(JSContext* cx, HandleObject wrap
return true;
if (protop) {
- if (!protop->setDelegate(cx))
+ if (!JSObject::setDelegate(cx, protop))
return false;
}
}
diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp
index b43fd02d2..2c1cffb77 100644
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -774,7 +774,7 @@ js::NewProxyObject(JSContext* cx, const BaseProxyHandler* handler, HandleValue p
}
void
-ProxyObject::renew(JSContext* cx, const BaseProxyHandler* handler, const Value& priv)
+ProxyObject::renew(const BaseProxyHandler* handler, const Value& priv)
{
MOZ_ASSERT(!IsInsideNursery(this));
MOZ_ASSERT_IF(IsCrossCompartmentWrapper(this), IsDeadProxyObject(this));
@@ -796,9 +796,9 @@ js::InitProxyClass(JSContext* cx, HandleObject obj)
JS_FS_END
};
- Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
RootedFunction ctor(cx);
- ctor = global->createConstructor(cx, proxy, cx->names().Proxy, 2);
+ ctor = GlobalObject::createConstructor(cx, proxy, cx->names().Proxy, 2);
if (!ctor)
return nullptr;
diff --git a/js/src/proxy/Wrapper.cpp b/js/src/proxy/Wrapper.cpp
index 43d559ff3..67f437262 100644
--- a/js/src/proxy/Wrapper.cpp
+++ b/js/src/proxy/Wrapper.cpp
@@ -312,9 +312,9 @@ Wrapper::New(JSContext* cx, JSObject* obj, const Wrapper* handler,
}
JSObject*
-Wrapper::Renew(JSContext* cx, JSObject* existing, JSObject* obj, const Wrapper* handler)
+Wrapper::Renew(JSObject* existing, JSObject* obj, const Wrapper* handler)
{
- existing->as<ProxyObject>().renew(cx, handler, ObjectValue(*obj));
+ existing->as<ProxyObject>().renew(handler, ObjectValue(*obj));
return existing;
}
diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
index 193d8d22b..51cd11fe8 100644
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -320,7 +320,6 @@ static bool enableIon = false;
static bool enableAsmJS = false;
static bool enableWasm = false;
static bool enableNativeRegExp = false;
-static bool enableUnboxedArrays = false;
static bool enableSharedMemory = SHARED_MEMORY_DEFAULT;
static bool enableWasmAlwaysBaseline = false;
static bool enableArrayProtoValues = true;
@@ -2311,7 +2310,7 @@ ValueToScript(JSContext* cx, HandleValue v, JSFunction** funp = nullptr)
return nullptr;
}
- JSScript* script = fun->getOrCreateScript(cx);
+ JSScript* script = JSFunction::getOrCreateScript(cx, fun);
if (!script)
return nullptr;
@@ -2551,6 +2550,14 @@ SrcNotes(JSContext* cx, HandleScript script, Sprinter* sp)
return false;
break;
+ case SRC_CLASS_SPAN: {
+ unsigned startOffset = GetSrcNoteOffset(sn, 0);
+ unsigned endOffset = GetSrcNoteOffset(sn, 1);
+ if (!sp->jsprintf(" %u %u", startOffset, endOffset))
+ return false;
+ break;
+ }
+
default:
MOZ_ASSERT_UNREACHABLE("unrecognized srcnote");
}
@@ -2690,7 +2697,7 @@ DisassembleScript(JSContext* cx, HandleScript script, HandleFunction fun,
if (sp->put(" CONSTRUCTOR") < 0)
return false;
}
- if (fun->isExprBody()) {
+ if (script->isExprBody()) {
if (sp->put(" EXPRESSION_CLOSURE") < 0)
return false;
}
@@ -2727,7 +2734,7 @@ DisassembleScript(JSContext* cx, HandleScript script, HandleFunction fun,
RootedFunction fun(cx, &obj->as<JSFunction>());
if (fun->isInterpreted()) {
- RootedScript script(cx, fun->getOrCreateScript(cx));
+ RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
if (script) {
if (!DisassembleScript(cx, script, fun, lines, recursive, sourceNotes, sp))
return false;
@@ -3489,8 +3496,8 @@ GroupOf(JSContext* cx, unsigned argc, JS::Value* vp)
JS_ReportErrorASCII(cx, "groupOf: object expected");
return false;
}
- JSObject* obj = &args[0].toObject();
- ObjectGroup* group = obj->getGroup(cx);
+ RootedObject obj(cx, &args[0].toObject());
+ ObjectGroup* group = JSObject::getGroup(cx, obj);
if (!group)
return false;
args.rval().set(JS_NumberValue(double(uintptr_t(group) >> 3)));
@@ -5404,7 +5411,7 @@ DumpScopeChain(JSContext* cx, unsigned argc, Value* vp)
ReportUsageErrorASCII(cx, callee, "Argument must be an interpreted function");
return false;
}
- script = fun->getOrCreateScript(cx);
+ script = JSFunction::getOrCreateScript(cx, fun);
} else {
script = obj->as<ModuleObject>().script();
}
@@ -6420,6 +6427,14 @@ CreateLastWarningObject(JSContext* cx, JSErrorReport* report)
if (!DefineProperty(cx, warningObj, cx->names().columnNumber, columnVal))
return false;
+ RootedObject notesArray(cx, CreateErrorNotesArray(cx, report));
+ if (!notesArray)
+ return false;
+
+ RootedValue notesArrayVal(cx, ObjectValue(*notesArray));
+ if (!DefineProperty(cx, warningObj, cx->names().notes, notesArrayVal))
+ return false;
+
GetShellContext(cx)->lastWarning.setObject(*warningObj);
return true;
}
@@ -7260,7 +7275,6 @@ SetContextOptions(JSContext* cx, const OptionParser& op)
enableAsmJS = !op.getBoolOption("no-asmjs");
enableWasm = !op.getBoolOption("no-wasm");
enableNativeRegExp = !op.getBoolOption("no-native-regexp");
- enableUnboxedArrays = op.getBoolOption("unboxed-arrays");
enableWasmAlwaysBaseline = op.getBoolOption("wasm-always-baseline");
enableArrayProtoValues = !op.getBoolOption("no-array-proto-values");
@@ -7270,7 +7284,6 @@ SetContextOptions(JSContext* cx, const OptionParser& op)
.setWasm(enableWasm)
.setWasmAlwaysBaseline(enableWasmAlwaysBaseline)
.setNativeRegExp(enableNativeRegExp)
- .setUnboxedArrays(enableUnboxedArrays)
.setArrayProtoValues(enableArrayProtoValues);
if (op.getBoolOption("wasm-check-bce"))
@@ -7539,7 +7552,6 @@ SetWorkerContextOptions(JSContext* cx)
.setWasm(enableWasm)
.setWasmAlwaysBaseline(enableWasmAlwaysBaseline)
.setNativeRegExp(enableNativeRegExp)
- .setUnboxedArrays(enableUnboxedArrays)
.setArrayProtoValues(enableArrayProtoValues);
cx->setOffthreadIonCompilationEnabled(offthreadCompilation);
cx->profilingScripts = enableCodeCoverage || enableDisassemblyDumps;
@@ -7709,8 +7721,6 @@ main(int argc, char** argv, char** envp)
|| !op.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation")
|| !op.addBoolOption('\0', "no-wasm", "Disable WebAssembly compilation")
|| !op.addBoolOption('\0', "no-native-regexp", "Disable native regexp compilation")
- || !op.addBoolOption('\0', "no-unboxed-objects", "Disable creating unboxed plain objects")
- || !op.addBoolOption('\0', "unboxed-arrays", "Allow creating unboxed arrays")
|| !op.addBoolOption('\0', "wasm-always-baseline", "Enable wasm baseline compiler when possible")
|| !op.addBoolOption('\0', "wasm-check-bce", "Always generate wasm bounds check, even redundant ones.")
|| !op.addBoolOption('\0', "no-array-proto-values", "Remove Array.prototype.values")
diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js b/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js
index d87abd7be..8cda44d87 100644
--- a/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js
+++ b/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js
@@ -1,7 +1,7 @@
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
// Generated by make_intl_data.py. DO NOT EDIT.
-// tzdata version = 2018e
+// tzdata version = 2019a
const tzMapper = [
x => x,
@@ -67,6 +67,7 @@ const links = {
"Cuba": "America/Havana",
"Egypt": "Africa/Cairo",
"Eire": "Europe/Dublin",
+ "Etc/UCT": "Etc/UTC",
"GB": "Europe/London",
"GB-Eire": "Europe/London",
"GMT+0": "Etc/GMT",
@@ -98,7 +99,7 @@ const links = {
"ROK": "Asia/Seoul",
"Singapore": "Asia/Singapore",
"Turkey": "Europe/Istanbul",
- "UCT": "Etc/UCT",
+ "UCT": "Etc/UTC",
"US/Alaska": "America/Anchorage",
"US/Aleutian": "America/Adak",
"US/Arizona": "America/Phoenix",
diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js
index b96dac96f..a3efe0310 100644
--- a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js
+++ b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js
@@ -1,7 +1,7 @@
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
// Generated by make_intl_data.py. DO NOT EDIT.
-// tzdata version = 2018e
+// tzdata version = 2019a
const tzMapper = [
x => x,
diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js
index 66ef3075d..b61593f78 100644
--- a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js
+++ b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js
@@ -1,7 +1,7 @@
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
// Generated by make_intl_data.py. DO NOT EDIT.
-// tzdata version = 2018e
+// tzdata version = 2019a
const tzMapper = [
x => x,
diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js b/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js
index 8d44204bc..12b55c214 100644
--- a/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js
+++ b/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js
@@ -1,7 +1,7 @@
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
// Generated by make_intl_data.py. DO NOT EDIT.
-// tzdata version = 2018e
+// tzdata version = 2019a
const tzMapper = [
x => x,
diff --git a/js/src/tests/ecma_2017/AsyncFunctions/await-error.js b/js/src/tests/ecma_2017/AsyncFunctions/await-error.js
new file mode 100644
index 000000000..1f40ea8a0
--- /dev/null
+++ b/js/src/tests/ecma_2017/AsyncFunctions/await-error.js
@@ -0,0 +1,16 @@
+var BUGNUMBER = 1317153;
+var summary = "await outside of async function should provide better error";
+
+print(BUGNUMBER + ": " + summary);
+
+let caught = false;
+try {
+ eval("await 10");
+} catch(e) {
+ assertEq(e.message, "await is only valid in async functions");
+ caught = true;
+}
+assertEq(caught, true);
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/ecma_2017/AsyncFunctions/await-in-arrow-parameters.js b/js/src/tests/ecma_2017/AsyncFunctions/await-in-arrow-parameters.js
new file mode 100644
index 000000000..ebb4ea9da
--- /dev/null
+++ b/js/src/tests/ecma_2017/AsyncFunctions/await-in-arrow-parameters.js
@@ -0,0 +1,94 @@
+var ieval = eval;
+var AsyncFunction = async function(){}.constructor;
+
+var functionContext = {
+ Function: {
+ constructor: Function,
+ toSourceBody: code => `function f() { ${code} }`,
+ toSourceParameter: code => `function f(x = ${code}) { }`,
+ },
+ AsyncFunction: {
+ constructor: AsyncFunction,
+ toSourceBody: code => `async function f() { ${code} }`,
+ toSourceParameter: code => `async function f(x = ${code}) { }`,
+ },
+};
+
+function assertSyntaxError(kind, code) {
+ var {constructor, toSourceBody, toSourceParameter} = functionContext[kind];
+ var body = toSourceBody(code);
+ var parameter = toSourceParameter(code);
+
+ assertThrowsInstanceOf(() => { constructor(code); }, SyntaxError, constructor.name + ":" + code);
+ assertThrowsInstanceOf(() => { constructor(`x = ${code}`, ""); }, SyntaxError, constructor.name + ":" + code);
+
+ assertThrowsInstanceOf(() => { eval(body); }, SyntaxError, "eval:" + body);
+ assertThrowsInstanceOf(() => { ieval(body); }, SyntaxError, "indirect eval:" + body);
+
+ assertThrowsInstanceOf(() => { eval(parameter); }, SyntaxError, "eval:" + parameter);
+ assertThrowsInstanceOf(() => { ieval(parameter); }, SyntaxError, "indirect eval:" + parameter);
+}
+
+function assertNoSyntaxError(kind, code) {
+ var {constructor, toSourceBody, toSourceParameter} = functionContext[kind];
+ var body = toSourceBody(code);
+ var parameter = toSourceParameter(code);
+
+ constructor(code);
+ constructor(`x = ${code}`, "");
+
+ eval(body);
+ ieval(body);
+
+ eval(parameter);
+ ieval(parameter);
+}
+
+function assertSyntaxErrorAsync(code) {
+ assertNoSyntaxError("Function", code);
+ assertSyntaxError("AsyncFunction", code);
+}
+
+function assertSyntaxErrorBoth(code) {
+ assertSyntaxError("Function", code);
+ assertSyntaxError("AsyncFunction", code);
+}
+
+
+// Bug 1353691
+// |await| expression is invalid in arrow functions in async-context.
+// |await/r/g| first parses as |AwaitExpression RegularExpressionLiteral|, when reparsing the
+// arrow function, it is parsed as |IdentRef DIV IdentRef DIV IdentRef|. We need to ensure in this
+// case, that we still treat |await| as a keyword and hence throw a SyntaxError.
+assertSyntaxErrorAsync("(a = await/r/g) => {}");
+assertSyntaxErrorBoth("async(a = await/r/g) => {}");
+
+// Also applies when nesting arrow functions.
+assertSyntaxErrorAsync("(a = (b = await/r/g) => {}) => {}");
+assertSyntaxErrorBoth("async(a = (b = await/r/g) => {}) => {}");
+assertSyntaxErrorBoth("(a = async(b = await/r/g) => {}) => {}");
+assertSyntaxErrorBoth("async(a = async(b = await/r/g) => {}) => {}");
+
+
+// Bug 1355860
+// |await| cannot be used as rest-binding parameter in arrow functions in async-context.
+assertSyntaxErrorAsync("(...await) => {}");
+assertSyntaxErrorBoth("async(...await) => {}");
+
+assertSyntaxErrorAsync("(a, ...await) => {}");
+assertSyntaxErrorBoth("async(a, ...await) => {}");
+
+// Also test nested arrow functions.
+assertSyntaxErrorAsync("(a = (...await) => {}) => {}");
+assertSyntaxErrorBoth("(a = async(...await) => {}) => {}");
+assertSyntaxErrorBoth("async(a = (...await) => {}) => {}");
+assertSyntaxErrorBoth("async(a = async(...await) => {}) => {}");
+
+assertSyntaxErrorAsync("(a = (b, ...await) => {}) => {}");
+assertSyntaxErrorBoth("(a = async(b, ...await) => {}) => {}");
+assertSyntaxErrorBoth("async(a = (b, ...await) => {}) => {}");
+assertSyntaxErrorBoth("async(a = async(b, ...await) => {}) => {}");
+
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/ecma_2017/AsyncFunctions/forbidden-as-consequent.js b/js/src/tests/ecma_2017/AsyncFunctions/forbidden-as-consequent.js
new file mode 100644
index 000000000..656ed46de
--- /dev/null
+++ b/js/src/tests/ecma_2017/AsyncFunctions/forbidden-as-consequent.js
@@ -0,0 +1,14 @@
+/* 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/. */
+
+assertThrowsInstanceOf(() => eval("if (1) async function foo() {}"),
+ SyntaxError);
+assertThrowsInstanceOf(() => eval("'use strict'; if (1) async function foo() {}"),
+ SyntaxError);
+
+var async = 42;
+assertEq(eval("if (1) async \n function foo() {}"), 42);
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/ecma_2017/AsyncFunctions/inner-caller.js b/js/src/tests/ecma_2017/AsyncFunctions/inner-caller.js
new file mode 100644
index 000000000..523eb79ea
--- /dev/null
+++ b/js/src/tests/ecma_2017/AsyncFunctions/inner-caller.js
@@ -0,0 +1,26 @@
+var BUGNUMBER = 1185106;
+var summary = "caller property of function inside async function should return wrapped async function";
+
+print(BUGNUMBER + ": " + summary);
+
+(async function f() {
+ var inner = (function g() {
+ return g.caller;
+ })();
+ assertEq(inner, f);
+})();
+
+(async function f() {
+ "use strict";
+ try {
+ (function g() {
+ return g.caller;
+ })();
+ assertEq(true, false);
+ } catch (e) {
+ assertEq(e instanceof TypeError, true);
+ }
+})();
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/ecma_6/Class/parenExprToString.js b/js/src/tests/ecma_6/Class/parenExprToString.js
new file mode 100644
index 000000000..a93972ce9
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/parenExprToString.js
@@ -0,0 +1,8 @@
+// Test that parenthesized class expressions don't get their toString offsets
+// messed up.
+
+assertEq((class {}).toString(), "class {}");
+assertEq(((class {})).toString(), "class {}");
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0, "OK");
diff --git a/js/src/tests/ecma_6/Comprehensions/for-reserved-word.js b/js/src/tests/ecma_6/Comprehensions/for-reserved-word.js
new file mode 100644
index 000000000..9b320fc91
--- /dev/null
+++ b/js/src/tests/ecma_6/Comprehensions/for-reserved-word.js
@@ -0,0 +1,107 @@
+var BUGNUMBER = 1340089;
+var summary = "Comprehension should check the binding names";
+
+print(BUGNUMBER + ": " + summary);
+
+// Non strict mode.
+// Keywords, literals, 'let', and 'yield' are not allowed.
+
+assertThrowsInstanceOf(function () {
+ eval("[for (true of [1]) 2]");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+ eval("(for (true of [1]) 2)");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function () {
+ eval("[for (throw of [1]) 2]");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+ eval("(for (throw of [1]) 2)");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function () {
+ eval("[for (let of [1]) 2]");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+ eval("(for (let of [1]) 2)");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function () {
+ eval("[for (yield of [1]) 2]");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+ eval("(for (yield of [1]) 2)");
+}, SyntaxError);
+
+eval("[for (public of [1]) 2]");
+eval("(for (public of [1]) 2)");
+
+eval("[for (static of [1]) 2]");
+eval("(for (static of [1]) 2)");
+
+// Strict mode.
+// All reserved words are not allowed.
+
+assertThrowsInstanceOf(function () {
+ "use strict";
+ eval("[for (true of [1]) 2]");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+ "use strict";
+ eval("(for (true of [1]) 2)");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function () {
+ "use strict";
+ eval("[for (throw of [1]) 2]");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+ "use strict";
+ eval("(for (throw of [1]) 2)");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function () {
+ "use strict";
+ eval("[for (let of [1]) 2]");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+ "use strict";
+ eval("(for (let of [1]) 2)");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function () {
+ "use strict";
+ eval("[for (yield of [1]) 2]");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+ "use strict";
+ eval("(for (yield of [1]) 2)");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function () {
+ "use strict";
+ eval("[for (public of [1]) 2]");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+ "use strict";
+ eval("(for (public of [1]) 2)");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function () {
+ "use strict";
+ eval("[for (static of [1]) 2]");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+ "use strict";
+ eval("(for (static of [1]) 2)");
+}, SyntaxError);
+
+(function () {
+ "use strict";
+ eval("[for (await of [1]) 2]");
+ eval("(for (await of [1]) 2)");
+})();
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/ecma_6/Function/constructor-binding.js b/js/src/tests/ecma_6/Function/constructor-binding.js
new file mode 100644
index 000000000..e82274d27
--- /dev/null
+++ b/js/src/tests/ecma_6/Function/constructor-binding.js
@@ -0,0 +1,11 @@
+var BUGNUMBER = 636635;
+var summary = "A function created by Function constructor shouldn't have anonymous binding";
+
+print(BUGNUMBER + ": " + summary);
+
+assertEq(new Function("return typeof anonymous")(), "undefined");
+assertEq(new Function("return function() { return typeof anonymous; }")()(), "undefined");
+assertEq(new Function("return function() { eval(''); return typeof anonymous; }")()(), "undefined");
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/ecma_6/Function/throw-type-error.js b/js/src/tests/ecma_6/Function/throw-type-error.js
new file mode 100644
index 000000000..68dd6e1d0
--- /dev/null
+++ b/js/src/tests/ecma_6/Function/throw-type-error.js
@@ -0,0 +1,16 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+
+const ThrowTypeError = function(){
+ "use strict";
+ return Object.getOwnPropertyDescriptor(arguments, "callee").get;
+}();
+
+assertDeepEq(Object.getOwnPropertyDescriptor(ThrowTypeError, "length"), {
+ value: 0, writable: false, enumerable: false, configurable: false
+});
+
+assertEq(Object.isFrozen(ThrowTypeError), true);
+
+if (typeof reportCompare == "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/ecma_6/Generators/forbidden-as-consequent.js b/js/src/tests/ecma_6/Generators/forbidden-as-consequent.js
new file mode 100644
index 000000000..13647e154
--- /dev/null
+++ b/js/src/tests/ecma_6/Generators/forbidden-as-consequent.js
@@ -0,0 +1,11 @@
+/* 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/. */
+
+assertThrowsInstanceOf(() => eval("if (1) function* foo() {}"),
+ SyntaxError);
+assertThrowsInstanceOf(() => eval("'use strict'; if (1) function* foo() {}"),
+ SyntaxError);
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-parameter.js b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-parameter.js
new file mode 100644
index 000000000..ae7fbe879
--- /dev/null
+++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-parameter.js
@@ -0,0 +1,21 @@
+// Annex B.3.3.1 disallows Annex B lexical function behavior when redeclaring a
+// parameter.
+
+(function(f) {
+ if (true) function f() { }
+ assertEq(f, 123);
+}(123));
+
+(function(f) {
+ { function f() { } }
+ assertEq(f, 123);
+}(123));
+
+(function(f = 123) {
+ assertEq(f, 123);
+ { function f() { } }
+ assertEq(f, 123);
+}());
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/ecma_6/TemplateStrings/tagTempl.js b/js/src/tests/ecma_6/TemplateStrings/tagTempl.js
index 1e3f52bfb..99c2098dc 100644
--- a/js/src/tests/ecma_6/TemplateStrings/tagTempl.js
+++ b/js/src/tests/ecma_6/TemplateStrings/tagTempl.js
@@ -287,5 +287,176 @@ assertEq(String.raw`h\r\ney${4}there\n`, "h\\r\\ney4there\\n");
assertEq(String.raw`hey`, "hey");
assertEq(String.raw``, "");
+// Invalid escape sequences
+check(raw`\01`, ["\\01"]);
+check(raw`\01${0}right`, ["\\01","right"]);
+check(raw`left${0}\01`, ["left","\\01"]);
+check(raw`left${0}\01${1}right`, ["left","\\01","right"]);
+check(raw`\1`, ["\\1"]);
+check(raw`\1${0}right`, ["\\1","right"]);
+check(raw`left${0}\1`, ["left","\\1"]);
+check(raw`left${0}\1${1}right`, ["left","\\1","right"]);
+check(raw`\xg`, ["\\xg"]);
+check(raw`\xg${0}right`, ["\\xg","right"]);
+check(raw`left${0}\xg`, ["left","\\xg"]);
+check(raw`left${0}\xg${1}right`, ["left","\\xg","right"]);
+check(raw`\xAg`, ["\\xAg"]);
+check(raw`\xAg${0}right`, ["\\xAg","right"]);
+check(raw`left${0}\xAg`, ["left","\\xAg"]);
+check(raw`left${0}\xAg${1}right`, ["left","\\xAg","right"]);
+check(raw`\u0`, ["\\u0"]);
+check(raw`\u0${0}right`, ["\\u0","right"]);
+check(raw`left${0}\u0`, ["left","\\u0"]);
+check(raw`left${0}\u0${1}right`, ["left","\\u0","right"]);
+check(raw`\u0g`, ["\\u0g"]);
+check(raw`\u0g${0}right`, ["\\u0g","right"]);
+check(raw`left${0}\u0g`, ["left","\\u0g"]);
+check(raw`left${0}\u0g${1}right`, ["left","\\u0g","right"]);
+check(raw`\u00g`, ["\\u00g"]);
+check(raw`\u00g${0}right`, ["\\u00g","right"]);
+check(raw`left${0}\u00g`, ["left","\\u00g"]);
+check(raw`left${0}\u00g${1}right`, ["left","\\u00g","right"]);
+check(raw`\u000g`, ["\\u000g"]);
+check(raw`\u000g${0}right`, ["\\u000g","right"]);
+check(raw`left${0}\u000g`, ["left","\\u000g"]);
+check(raw`left${0}\u000g${1}right`, ["left","\\u000g","right"]);
+check(raw`\u{}`, ["\\u{}"]);
+check(raw`\u{}${0}right`, ["\\u{}","right"]);
+check(raw`left${0}\u{}`, ["left","\\u{}"]);
+check(raw`left${0}\u{}${1}right`, ["left","\\u{}","right"]);
+check(raw`\u{-0}`, ["\\u{-0}"]);
+check(raw`\u{-0}${0}right`, ["\\u{-0}","right"]);
+check(raw`left${0}\u{-0}`, ["left","\\u{-0}"]);
+check(raw`left${0}\u{-0}${1}right`, ["left","\\u{-0}","right"]);
+check(raw`\u{g}`, ["\\u{g}"]);
+check(raw`\u{g}${0}right`, ["\\u{g}","right"]);
+check(raw`left${0}\u{g}`, ["left","\\u{g}"]);
+check(raw`left${0}\u{g}${1}right`, ["left","\\u{g}","right"]);
+check(raw`\u{0`, ["\\u{0"]);
+check(raw`\u{0${0}right`, ["\\u{0","right"]);
+check(raw`left${0}\u{0`, ["left","\\u{0"]);
+check(raw`left${0}\u{0${1}right`, ["left","\\u{0","right"]);
+check(raw`\u{\u{0}`, ["\\u{\\u{0}"]);
+check(raw`\u{\u{0}${0}right`, ["\\u{\\u{0}","right"]);
+check(raw`left${0}\u{\u{0}`, ["left","\\u{\\u{0}"]);
+check(raw`left${0}\u{\u{0}${1}right`, ["left","\\u{\\u{0}","right"]);
+check(raw`\u{110000}`, ["\\u{110000}"]);
+check(raw`\u{110000}${0}right`, ["\\u{110000}","right"]);
+check(raw`left${0}\u{110000}`, ["left","\\u{110000}"]);
+check(raw`left${0}\u{110000}${1}right`, ["left","\\u{110000}","right"]);
+
+check(cooked`\01`, [void 0]);
+check(cooked`\01${0}right`, [void 0,"right"]);
+check(cooked`left${0}\01`, ["left",void 0]);
+check(cooked`left${0}\01${1}right`, ["left",void 0,"right"]);
+check(cooked`\1`, [void 0]);
+check(cooked`\1${0}right`, [void 0,"right"]);
+check(cooked`left${0}\1`, ["left",void 0]);
+check(cooked`left${0}\1${1}right`, ["left",void 0,"right"]);
+check(cooked`\xg`, [void 0]);
+check(cooked`\xg${0}right`, [void 0,"right"]);
+check(cooked`left${0}\xg`, ["left",void 0]);
+check(cooked`left${0}\xg${1}right`, ["left",void 0,"right"]);
+check(cooked`\xAg`, [void 0]);
+check(cooked`\xAg${0}right`, [void 0,"right"]);
+check(cooked`left${0}\xAg`, ["left",void 0]);
+check(cooked`left${0}\xAg${1}right`, ["left",void 0,"right"]);
+check(cooked`\u0`, [void 0]);
+check(cooked`\u0${0}right`, [void 0,"right"]);
+check(cooked`left${0}\u0`, ["left",void 0]);
+check(cooked`left${0}\u0${1}right`, ["left",void 0,"right"]);
+check(cooked`\u0g`, [void 0]);
+check(cooked`\u0g${0}right`, [void 0,"right"]);
+check(cooked`left${0}\u0g`, ["left",void 0]);
+check(cooked`left${0}\u0g${1}right`, ["left",void 0,"right"]);
+check(cooked`\u00g`, [void 0]);
+check(cooked`\u00g${0}right`, [void 0,"right"]);
+check(cooked`left${0}\u00g`, ["left",void 0]);
+check(cooked`left${0}\u00g${1}right`, ["left",void 0,"right"]);
+check(cooked`\u000g`, [void 0]);
+check(cooked`\u000g${0}right`, [void 0,"right"]);
+check(cooked`left${0}\u000g`, ["left",void 0]);
+check(cooked`left${0}\u000g${1}right`, ["left",void 0,"right"]);
+check(cooked`\u{}`, [void 0]);
+check(cooked`\u{}${0}right`, [void 0,"right"]);
+check(cooked`left${0}\u{}`, ["left",void 0]);
+check(cooked`left${0}\u{}${1}right`, ["left",void 0,"right"]);
+check(cooked`\u{-0}`, [void 0]);
+check(cooked`\u{-0}${0}right`, [void 0,"right"]);
+check(cooked`left${0}\u{-0}`, ["left",void 0]);
+check(cooked`left${0}\u{-0}${1}right`, ["left",void 0,"right"]);
+check(cooked`\u{g}`, [void 0]);
+check(cooked`\u{g}${0}right`, [void 0,"right"]);
+check(cooked`left${0}\u{g}`, ["left",void 0]);
+check(cooked`left${0}\u{g}${1}right`, ["left",void 0,"right"]);
+check(cooked`\u{0`, [void 0]);
+check(cooked`\u{0${0}right`, [void 0,"right"]);
+check(cooked`left${0}\u{0`, ["left",void 0]);
+check(cooked`left${0}\u{0${1}right`, ["left",void 0,"right"]);
+check(cooked`\u{\u{0}`, [void 0]);
+check(cooked`\u{\u{0}${0}right`, [void 0,"right"]);
+check(cooked`left${0}\u{\u{0}`, ["left",void 0]);
+check(cooked`left${0}\u{\u{0}${1}right`, ["left",void 0,"right"]);
+check(cooked`\u{110000}`, [void 0]);
+check(cooked`\u{110000}${0}right`, [void 0,"right"]);
+check(cooked`left${0}\u{110000}`, ["left",void 0]);
+check(cooked`left${0}\u{110000}${1}right`, ["left",void 0,"right"]);
+
+syntaxError("`\\01`");
+syntaxError("`\\01${0}right`");
+syntaxError("`left${0}\\01`");
+syntaxError("`left${0}\\01${1}right`");
+syntaxError("`\\1`");
+syntaxError("`\\1${0}right`");
+syntaxError("`left${0}\\1`");
+syntaxError("`left${0}\\1${1}right`");
+syntaxError("`\\xg`");
+syntaxError("`\\xg${0}right`");
+syntaxError("`left${0}\\xg`");
+syntaxError("`left${0}\\xg${1}right`");
+syntaxError("`\\xAg`");
+syntaxError("`\\xAg${0}right`");
+syntaxError("`left${0}\\xAg`");
+syntaxError("`left${0}\\xAg${1}right`");
+syntaxError("`\\u0`");
+syntaxError("`\\u0${0}right`");
+syntaxError("`left${0}\\u0`");
+syntaxError("`left${0}\\u0${1}right`");
+syntaxError("`\\u0g`");
+syntaxError("`\\u0g${0}right`");
+syntaxError("`left${0}\\u0g`");
+syntaxError("`left${0}\\u0g${1}right`");
+syntaxError("`\\u00g`");
+syntaxError("`\\u00g${0}right`");
+syntaxError("`left${0}\\u00g`");
+syntaxError("`left${0}\\u00g${1}right`");
+syntaxError("`\\u000g`");
+syntaxError("`\\u000g${0}right`");
+syntaxError("`left${0}\\u000g`");
+syntaxError("`left${0}\\u000g${1}right`");
+syntaxError("`\\u{}`");
+syntaxError("`\\u{}${0}right`");
+syntaxError("`left${0}\\u{}`");
+syntaxError("`left${0}\\u{}${1}right`");
+syntaxError("`\\u{-0}`");
+syntaxError("`\\u{-0}${0}right`");
+syntaxError("`left${0}\\u{-0}`");
+syntaxError("`left${0}\\u{-0}${1}right`");
+syntaxError("`\\u{g}`");
+syntaxError("`\\u{g}${0}right`");
+syntaxError("`left${0}\\u{g}`");
+syntaxError("`left${0}\\u{g}${1}right`");
+syntaxError("`\\u{0`");
+syntaxError("`\\u{0${0}right`");
+syntaxError("`left${0}\\u{0`");
+syntaxError("`left${0}\\u{0${1}right`");
+syntaxError("`\\u{\\u{0}`");
+syntaxError("`\\u{\\u{0}${0}right`");
+syntaxError("`left${0}\\u{\\u{0}`");
+syntaxError("`left${0}\\u{\\u{0}${1}right`");
+syntaxError("`\\u{110000}`");
+syntaxError("`\\u{110000}${0}right`");
+syntaxError("`left${0}\\u{110000}`");
+syntaxError("`left${0}\\u{110000}${1}right`");
reportCompare(0, 0, "ok");
diff --git a/js/src/tests/ecma_6/extensions/newer-type-functions-caller-arguments.js b/js/src/tests/ecma_6/extensions/newer-type-functions-caller-arguments.js
new file mode 100644
index 000000000..7fed18037
--- /dev/null
+++ b/js/src/tests/ecma_6/extensions/newer-type-functions-caller-arguments.js
@@ -0,0 +1,43 @@
+// Tests that newer-type functions (i.e. anything not defined by regular function declarations and
+// expressions) throw when accessing their 'arguments' and 'caller' properties.
+
+// 9.2.7 (AddRestrictedFunctionProperties) defines accessors on Function.prototype which throw on
+// every 'get' and 'set' of 'caller' and 'arguments'.
+// Additionally, 16.2 (Forbidden Extensions) forbids adding properties to all non-"legacy" function
+// declarations and expressions. This creates the effect that all newer-style functions act like
+// strict-mode functions when accessing their 'caller' and 'arguments' properties.
+
+const container = {
+ async asyncMethod() {},
+ *genMethod() {},
+ method() {}
+};
+
+[
+ async function(){},
+ function*(){},
+ () => {},
+ async () => {},
+
+ container.asyncMethod,
+ container.genMethod,
+ container.method
+].forEach(f => {
+ checkArgumentsAccess(f);
+ checkCallerAccess(f);
+});
+
+function checkArgumentsAccess(f) {
+ assertThrowsInstanceOf(() => f.arguments, TypeError,
+ `Expected 'arguments' property access to throw on ${f}`);
+}
+
+function checkCallerAccess(f) {
+ assertThrowsInstanceOf(() => f.caller, TypeError,
+ `Expected 'caller' property access to throw on ${f}`);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
+
+print("Tests complete");
diff --git a/js/src/tests/ecma_7/AsyncFunctions/async-contains-unicode-escape.js b/js/src/tests/ecma_7/AsyncFunctions/async-contains-unicode-escape.js
new file mode 100644
index 000000000..d53dff696
--- /dev/null
+++ b/js/src/tests/ecma_7/AsyncFunctions/async-contains-unicode-escape.js
@@ -0,0 +1,54 @@
+var BUGNUMBER = 1315815;
+var summary = "async/await containing escapes";
+
+print(BUGNUMBER + ": " + summary);
+
+// Using "eval" as the argument name is fugly, but it means evals below are
+// *direct* evals, and so their effects in the unescaped case won't extend
+// past each separate |test| call (as would happen if we used a different name
+// that made each eval into an indirect eval, affecting code in the global
+// scope).
+function test(code, eval)
+{
+ var unescaped = code.replace("###", "async");
+ var escaped = code.replace("###", "\\u0061");
+
+ assertThrowsInstanceOf(() => eval(escaped), SyntaxError);
+ eval(unescaped);
+}
+
+test("### function f() {}", eval);
+test("var x = ### function f() {}", eval);
+test("### x => {};", eval);
+test("var x = ### x => {}", eval);
+test("### () => {};", eval);
+test("var x = ### () => {}", eval);
+test("### (y) => {};", eval);
+test("var x = ### (y) => {}", eval);
+test("({ ### x() {} })", eval);
+test("var x = ### function f() {}", eval);
+
+if (typeof parseModule === "function")
+ test("export default ### function f() {}", parseModule);
+
+assertThrowsInstanceOf(() => eval("async await => 1;"),
+ SyntaxError);
+assertThrowsInstanceOf(() => eval("async aw\\u0061it => 1;"),
+ SyntaxError);
+
+var async = 0;
+assertEq(\u0061sync, 0);
+
+var obj = { \u0061sync() { return 1; } };
+assertEq(obj.async(), 1);
+
+async = function() { return 42; };
+
+var z = async(obj);
+assertEq(z, 42);
+
+var w = async(obj)=>{};
+assertEq(typeof w, "function");
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/js1_8/regress/regress-467495-05.js b/js/src/tests/js1_8/regress/regress-467495-05.js
index 505fb6bd6..ecf47b8fe 100644
--- a/js/src/tests/js1_8/regress/regress-467495-05.js
+++ b/js/src/tests/js1_8/regress/regress-467495-05.js
@@ -20,7 +20,7 @@ function test()
printBugNumber(BUGNUMBER);
printStatus (summary);
- expect = 'function x() {}';
+ expect = '1';
function g(x) { if (1) function x() {} return x; }
print(actual = g(1) + '');
diff --git a/js/src/tests/js1_8/regress/regress-467495-06.js b/js/src/tests/js1_8/regress/regress-467495-06.js
index d8bc81c83..72adeee9e 100644
--- a/js/src/tests/js1_8/regress/regress-467495-06.js
+++ b/js/src/tests/js1_8/regress/regress-467495-06.js
@@ -32,7 +32,7 @@ function test()
var r = f(0);
- if (typeof(r[0]) != "function")
+ if (typeof(r[0]) != "number")
actual += "Bad r[0]";
if (typeof(r[1]) != "function")
diff --git a/js/src/tests/js1_8_5/reflect-parse/object-rest.js b/js/src/tests/js1_8_5/reflect-parse/object-rest.js
new file mode 100644
index 000000000..5af06909b
--- /dev/null
+++ b/js/src/tests/js1_8_5/reflect-parse/object-rest.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!xulRuntime.shell)
+
+function property(key, value = key, shorthand = key === value) {
+ return { key, value, shorthand };
+}
+
+function assertDestrAssign(src, pattern) {
+ assertExpr(`(${src} = 0)`, aExpr("=", pattern, lit(0)));
+}
+
+function assertDestrBinding(src, pattern) {
+ assertDecl(`var ${src} = 0`, varDecl([{id: pattern, init: lit(0)}]));
+}
+
+function test() {
+ // Target expression must be a simple assignment target or a nested pattern
+ // in object assignment patterns.
+ assertDestrAssign("{...x}", objPatt([spread(ident("x"))]));
+ assertDestrAssign("{...(x)}", objPatt([spread(ident("x"))]));
+ assertDestrAssign("{...obj.p}", objPatt([spread(dotExpr(ident("obj"), ident("p")))]));
+ assertDestrAssign("{...{}}", objPatt([spread(objPatt([]))]));
+ assertDestrAssign("{...[]}", objPatt([spread(arrPatt([]))]));
+
+ // Object binding patterns only allow binding identifiers or nested patterns.
+ assertDestrBinding("{...x}", objPatt([spread(ident("x"))]));
+ assertDestrBinding("{...{}}", objPatt([spread(objPatt([]))]));
+ assertDestrBinding("{...[]}", objPatt([spread(arrPatt([]))]));
+
+ // The rest-property can be preceded by other properties.
+ for (var assertDestr of [assertDestrAssign, assertDestrBinding]) {
+ assertDestr("{a, ...x}", objPatt([property(ident("a")), spread(ident("x"))]));
+ assertDestr("{a: b, ...x}", objPatt([property(ident("a"), ident("b")), spread(ident("x"))]));
+ assertDestr("{[a]: b, ...x}", objPatt([property(comp(ident("a")), ident("b")), spread(ident("x"))]));
+ }
+
+ // Tests when __proto__ is used in the object pattern.
+ for (var assertDestr of [assertDestrAssign, assertDestrBinding]) {
+ assertDestr("{...__proto__}", objPatt([spread(ident("__proto__"))]));
+ assertDestr("{__proto__, ...x}", objPatt([property(ident("__proto__")), spread(ident("x"))]));
+ }
+ assertDestrAssign("{__proto__: a, ...x}", objPatt([property(lit("__proto__"), ident("a")), spread(ident("x"))]));
+ assertDestrBinding("{__proto__: a, ...x}", objPatt([property(ident("__proto__"), ident("a")), spread(ident("x"))]));
+}
+
+runtest(test);
diff --git a/js/src/tests/js1_8_5/reflect-parse/object-spread.js b/js/src/tests/js1_8_5/reflect-parse/object-spread.js
new file mode 100644
index 000000000..a4b269c40
--- /dev/null
+++ b/js/src/tests/js1_8_5/reflect-parse/object-spread.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!xulRuntime.shell)
+
+function property(key, value = key, shorthand = key === value) {
+ return { key, value, shorthand };
+}
+
+function test() {
+ // Any expression can be spreaded.
+ assertExpr("({...x})", objExpr([spread(ident("x"))]));
+ assertExpr("({...f()})", objExpr([spread(callExpr(ident("f"), []))]));
+ assertExpr("({...123})", objExpr([spread(lit(123))]));
+
+ // Multiple spread expression are allowed.
+ assertExpr("({...x, ...obj.p})", objExpr([spread(ident("x")), spread(dotExpr(ident("obj"), ident("p")))]));
+
+ // Spread expression can appear anywhere in an object literal.
+ assertExpr("({p, ...x})", objExpr([property(ident("p")), spread(ident("x"))]));
+ assertExpr("({p: a, ...x})", objExpr([property(ident("p"), ident("a")), spread(ident("x"))]));
+ assertExpr("({...x, p: a})", objExpr([spread(ident("x")), property(ident("p"), ident("a"))]));
+
+ // Trailing comma after spread expression is allowed.
+ assertExpr("({...x,})", objExpr([spread(ident("x"))]));
+
+ // __proto__ is not special in spread expressions.
+ assertExpr("({...__proto__})", objExpr([spread(ident("__proto__"))]));
+ assertExpr("({...__proto__, ...__proto__})", objExpr([spread(ident("__proto__")), spread(ident("__proto__"))]));
+}
+
+runtest(test);
diff --git a/js/src/tests/js1_8_5/reflect-parse/templateStrings.js b/js/src/tests/js1_8_5/reflect-parse/templateStrings.js
index c87ba96b8..fb12afd00 100644
--- a/js/src/tests/js1_8_5/reflect-parse/templateStrings.js
+++ b/js/src/tests/js1_8_5/reflect-parse/templateStrings.js
@@ -7,6 +7,8 @@ assertStringExpr("`hey\nthere`", literal("hey\nthere"));
assertExpr("`hey${\"there\"}`", templateLit([lit("hey"), lit("there"), lit("")]));
assertExpr("`hey${\"there\"}mine`", templateLit([lit("hey"), lit("there"), lit("mine")]));
assertExpr("`hey${a == 5}mine`", templateLit([lit("hey"), binExpr("==", ident("a"), lit(5)), lit("mine")]));
+assertExpr("func`hey\\x`", taggedTemplate(ident("func"), template(["hey\\x"], [void 0])));
+assertExpr("func`hey${4}\\x`", taggedTemplate(ident("func"), template(["hey","\\x"], ["hey",void 0], lit(4))));
assertExpr("`hey${`there${\"how\"}`}mine`", templateLit([lit("hey"),
templateLit([lit("there"), lit("how"), lit("")]), lit("mine")]));
assertExpr("func`hey`", taggedTemplate(ident("func"), template(["hey"], ["hey"])));
diff --git a/js/src/tests/shell/futex.js b/js/src/tests/shell/futex.js
index b0951f12e..8ba61d71c 100644
--- a/js/src/tests/shell/futex.js
+++ b/js/src/tests/shell/futex.js
@@ -67,6 +67,8 @@ if (helperThreadCount() === 0) {
quit();
}
+var mem = new Int32Array(new SharedArrayBuffer(1024));
+
////////////////////////////////////////////////////////////
// wait() returns "not-equal" if the value is not the expected one.
@@ -102,7 +104,7 @@ dprint("Sleeping for 2 seconds");
sleep(2);
dprint("Waking the main thread now");
setSharedArrayBuffer(null);
-assertEq(Atomics.wake(mem, 0, 1), 1); // Can fail spuriously but very unlikely
+assertEq(Atomics.notify(mem, 0, 1), 1); // Can fail spuriously but very unlikely
`);
var then = Date.now();
@@ -113,14 +115,14 @@ assertEq(getSharedArrayBuffer(), null); // The worker's clearing of the mbx is v
////////////////////////////////////////////////////////////
-// Test the default argument to atomics.wake()
+// Test the default argument to atomics.notify()
setSharedArrayBuffer(mem.buffer);
evalInWorker(`
var mem = new Int32Array(getSharedArrayBuffer());
sleep(2); // Probably long enough to avoid a spurious error next
-assertEq(Atomics.wake(mem, 0), 1); // Last argument to wake should default to +Infinity
+assertEq(Atomics.notify(mem, 0), 1); // Last argument to wake should default to +Infinity
`);
var then = Date.now();
diff --git a/js/src/tests/test262/built-ins/Object/getOwnPropertyDescriptors/shell.js b/js/src/tests/test262/built-ins/Object/getOwnPropertyDescriptors/shell.js
index 6ed766e94..e69de29bb 100644
--- a/js/src/tests/test262/built-ins/Object/getOwnPropertyDescriptors/shell.js
+++ b/js/src/tests/test262/built-ins/Object/getOwnPropertyDescriptors/shell.js
@@ -1,27 +0,0 @@
-var assert = {
- sameValue: assertEq,
- notSameValue(a, b, msg) {
- try {
- assertEq(a, b);
- throw "equal"
- } catch (e) {
- if (e === "equal")
- throw new Error("Assertion failed: expected different values, got " + a);
- }
- },
- throws(ctor, f) {
- var fullmsg;
- try {
- f();
- } catch (exc) {
- if (exc instanceof ctor)
- return;
- fullmsg = "Assertion failed: expected exception " + ctor.name + ", got " + exc;
- }
- if (fullmsg === undefined)
- fullmsg = "Assertion failed: expected exception " + ctor.name + ", no exception thrown";
- if (msg !== undefined)
- fullmsg += " - " + msg;
- throw new Error(fullmsg);
- }
-}
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-1.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-1.js
new file mode 100755
index 000000000..08925e079
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-1.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapped value is not changed when property was made non-configurable.
+flags: [noStrict]
+---*/
+
+function argumentsNonConfigurable(a) {
+ Object.defineProperty(arguments, "0", {configurable: false});
+
+ assert.sameValue(a, 1);
+ assert.sameValue(arguments[0], 1);
+}
+argumentsNonConfigurable(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-2.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-2.js
new file mode 100755
index 000000000..265481e01
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-2.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapping works when property is non-configurable, variable is
+ changed with SetMutableBinding.
+flags: [noStrict]
+---*/
+
+function argumentsAndSetMutableBinding(a) {
+ Object.defineProperty(arguments, "0", {configurable: false});
+
+ a = 2;
+ assert.sameValue(a, 2);
+ assert.sameValue(arguments[0], 2);
+}
+argumentsAndSetMutableBinding(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-3.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-3.js
new file mode 100755
index 000000000..6877a0c9d
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-3.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapping works when property is non-configurable, arguments property
+ is changed with [[DefineOwnProperty]].
+flags: [noStrict]
+---*/
+
+function argumentsAndDefineOwnProperty(a) {
+ Object.defineProperty(arguments, "0", {configurable: false});
+
+ Object.defineProperty(arguments, "0", {value: 2});
+ assert.sameValue(a, 2);
+ assert.sameValue(arguments[0], 2);
+}
+argumentsAndDefineOwnProperty(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-4.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-4.js
new file mode 100755
index 000000000..d2ac4d376
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-4.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapping works when property is non-configurable, arguments property
+ is changed with [[Set]].
+flags: [noStrict]
+---*/
+
+function argumentsAndSet(a) {
+ Object.defineProperty(arguments, "0", {configurable: false});
+
+ arguments[0] = 2;
+ assert.sameValue(a, 2);
+ assert.sameValue(arguments[0], 2);
+}
+argumentsAndSet(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-1.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-1.js
new file mode 100755
index 000000000..67f971369
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-1.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapping works when property is non-configurable, arguments property
+ was not deleted. [[Delete]] operation returns false.
+flags: [noStrict]
+---*/
+
+function argumentsAndDelete(a) {
+ Object.defineProperty(arguments, "0", {configurable: false});
+
+ assert.sameValue(delete arguments[0], false);
+ assert.sameValue(a, 1);
+ assert.sameValue(arguments[0], 1);
+}
+argumentsAndDelete(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-2.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-2.js
new file mode 100755
index 000000000..9a089eaac
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-2.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapping works when property is non-configurable, arguments property
+ was not deleted. Variable is changed with SetMutableBinding.
+flags: [noStrict]
+---*/
+
+function argumentsAndDeleteSetMutableBinding(a) {
+ Object.defineProperty(arguments, "0", {configurable: false});
+
+ // Precondition: Delete is unsuccessful and doesn't affect mapping.
+ assert.sameValue(delete arguments[0], false);
+ assert.sameValue(a, 1);
+ assert.sameValue(arguments[0], 1);
+
+ a = 2;
+ assert.sameValue(a, 2);
+ assert.sameValue(arguments[0], 2);
+}
+argumentsAndDeleteSetMutableBinding(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-3.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-3.js
new file mode 100755
index 000000000..26676a4ee
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-3.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapping works when property is non-configurable, arguments property
+ was not deleted. Arguments property is changed with
+ [[DefineOwnProperty]].
+flags: [noStrict]
+---*/
+
+function argumentsAndDeleteDefineOwnProperty(a) {
+ Object.defineProperty(arguments, "0", {configurable: false});
+
+ // Precondition: Delete is unsuccessful and doesn't affect mapping.
+ assert.sameValue(delete arguments[0], false);
+ assert.sameValue(a, 1);
+ assert.sameValue(arguments[0], 1);
+
+ Object.defineProperty(arguments, "0", {value: 2});
+ assert.sameValue(a, 2);
+ assert.sameValue(arguments[0], 2);
+}
+argumentsAndDeleteDefineOwnProperty(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-4.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-4.js
new file mode 100755
index 000000000..ed96a0e67
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-delete-4.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapping works when property is non-configurable, arguments property
+ was not deleted. Arguments property is changed with
+ [[Set]].
+flags: [noStrict]
+---*/
+
+function argumentsAndDeleteSet(a) {
+ Object.defineProperty(arguments, "0", {configurable: false});
+
+ // Precondition: Delete is unsuccessful and doesn't affect mapping.
+ assert.sameValue(delete arguments[0], false);
+ assert.sameValue(a, 1);
+ assert.sameValue(arguments[0], 1);
+
+ arguments[0] = 2;
+ assert.sameValue(a, 2);
+ assert.sameValue(arguments[0], 2);
+}
+argumentsAndDeleteSet(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-1.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-1.js
new file mode 100755
index 000000000..cfeba05c4
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-1.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapped arguments property is changed to non-configurable and
+ non-writable. Perform property attribute changes with a single
+ [[DefineOwnProperty]] call. Mapped values are unchanged, mapping
+ itself is removed.
+flags: [noStrict]
+---*/
+
+function argumentsNonConfigurableAndNonWritable(a) {
+ Object.defineProperty(arguments, "0", {configurable: false, writable: false});
+ assert.sameValue(a, 1);
+ assert.sameValue(arguments[0], 1);
+
+ // Postcondition: Arguments mapping is removed.
+ a = 2;
+ assert.sameValue(a, 2);
+ assert.sameValue(arguments[0], 1);
+}
+argumentsNonConfigurableAndNonWritable(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-2.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-2.js
new file mode 100755
index 000000000..69c0e125b
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-2.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapped arguments property is changed to non-configurable and
+ non-writable. Perform property attribute changes with two
+ consecutive [[DefineOwnProperty]] calls. Mapped values are
+ unchanged, mapping itself is removed.
+flags: [noStrict]
+---*/
+
+function argumentsNonConfigurableThenNonWritable(a) {
+ Object.defineProperty(arguments, "0", {configurable: false});
+ Object.defineProperty(arguments, "0", {writable: false});
+ assert.sameValue(a, 1);
+ assert.sameValue(arguments[0], 1);
+
+ // Postcondition: Arguments mapping is removed.
+ a = 2;
+ assert.sameValue(a, 2);
+ assert.sameValue(arguments[0], 1);
+}
+argumentsNonConfigurableThenNonWritable(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-3.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-3.js
new file mode 100755
index 000000000..dca0adcd2
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-3.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapped arguments property is changed to non-configurable and
+ non-writable. Perform property attribute changes with two
+ [[DefineOwnProperty]] calls. Add intervening call to
+ SetMutableBinding.
+flags: [noStrict]
+---*/
+
+function argumentsNonConfigurableThenNonWritableWithInterveningSetMutableBinding(a) {
+ Object.defineProperty(arguments, "0", {configurable: false});
+ a = 2;
+ Object.defineProperty(arguments, "0", {writable: false});
+ assert.sameValue(a, 2);
+ // `arguments[0] === 1` per ES2015, Rev 38, April 14, 2015 Final Draft.
+ // Specification bug: https://bugs.ecmascript.org/show_bug.cgi?id=4371
+ assert.sameValue(arguments[0], 2);
+
+ // Postcondition: Arguments mapping is removed.
+ a = 3;
+ assert.sameValue(a, 3);
+ assert.sameValue(arguments[0], 2);
+}
+argumentsNonConfigurableThenNonWritableWithInterveningSetMutableBinding(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-4.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-4.js
new file mode 100755
index 000000000..80d56fe1c
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-4.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapped arguments property is changed to non-configurable and
+ non-writable. Perform property attribute changes with two
+ [[DefineOwnProperty]] calls. Add intervening call to [[Set]].
+flags: [noStrict]
+---*/
+
+function argumentsNonConfigurableThenNonWritableWithInterveningSet(a) {
+ Object.defineProperty(arguments, "0", {configurable: false});
+ arguments[0] = 2;
+ Object.defineProperty(arguments, "0", {writable: false});
+ assert.sameValue(a, 2);
+ assert.sameValue(arguments[0], 2);
+
+ // Postcondition: Arguments mapping is removed.
+ a = 3;
+ assert.sameValue(a, 3);
+ assert.sameValue(arguments[0], 2);
+}
+argumentsNonConfigurableThenNonWritableWithInterveningSet(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-5.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-5.js
new file mode 100755
index 000000000..bbb951502
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-5.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapped arguments property is changed to non-configurable and
+ non-writable. Perform property attribute changes with two
+ [[DefineOwnProperty]] calls. Add intervening call to
+ [[DefineOwnProperty]].
+flags: [noStrict]
+---*/
+
+function argumentsNonConfigurableThenNonWritableWithDefineOwnProperty(a) {
+ Object.defineProperty(arguments, "0", {configurable: false});
+ Object.defineProperty(arguments, "0", {value: 2});
+ Object.defineProperty(arguments, "0", {writable: false});
+ assert.sameValue(a, 2);
+ assert.sameValue(arguments[0], 2);
+
+ // Postcondition: Arguments mapping is removed.
+ a = 3;
+ assert.sameValue(a, 3);
+ assert.sameValue(arguments[0], 2);
+}
+argumentsNonConfigurableThenNonWritableWithDefineOwnProperty(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-1.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-1.js
new file mode 100755
index 000000000..b918f75a1
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-1.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapping works when property is non-configurable, arguments property
+ was not deleted. [[Delete]] operations throws TypeError if called
+ from strict-mode code.
+flags: [noStrict]
+---*/
+
+function argumentsAndStrictDelete(a) {
+ Object.defineProperty(arguments, "0", {configurable: false});
+
+ var args = arguments;
+ assert.throws(TypeError, function() { "use strict"; delete args[0]; });
+ assert.sameValue(a, 1);
+ assert.sameValue(arguments[0], 1);
+}
+argumentsAndStrictDelete(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-2.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-2.js
new file mode 100755
index 000000000..01afbe4de
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-2.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapping works when property is non-configurable, arguments property
+ was not deleted. [[Delete]] operations throws TypeError if called
+ from strict-mode code. Variable is changed with SetMutableBinding.
+flags: [noStrict]
+---*/
+
+function argumentsAndStrictDeleteSetMutableBinding(a) {
+ Object.defineProperty(arguments, "0", {configurable: false});
+
+ // Precondition: Delete is unsuccessful and doesn't affect mapping.
+ var args = arguments;
+ assert.throws(TypeError, function() { "use strict"; delete args[0]; });
+ assert.sameValue(a, 1);
+ assert.sameValue(arguments[0], 1);
+
+ a = 2;
+ assert.sameValue(a, 2);
+ assert.sameValue(arguments[0], 2);
+}
+argumentsAndStrictDeleteSetMutableBinding(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-3.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-3.js
new file mode 100755
index 000000000..9aa2a2ed2
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-3.js
@@ -0,0 +1,27 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapping works when property is non-configurable, arguments property
+ was not deleted. [[Delete]] operations throws TypeError if called
+ from strict-mode code. Arguments property is changed with
+ [[DefineOwnProperty]].
+flags: [noStrict]
+---*/
+
+function argumentsAndStrictDeleteDefineOwnProperty(a) {
+ Object.defineProperty(arguments, "0", {configurable: false});
+
+ // Precondition: Delete is unsuccessful and doesn't affect mapping.
+ var args = arguments;
+ assert.throws(TypeError, function() { "use strict"; delete args[0]; });
+ assert.sameValue(a, 1);
+ assert.sameValue(arguments[0], 1);
+
+ Object.defineProperty(arguments, "0", {value: 2});
+ assert.sameValue(a, 2);
+ assert.sameValue(arguments[0], 2);
+}
+argumentsAndStrictDeleteDefineOwnProperty(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-4.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-4.js
new file mode 100755
index 000000000..b48018843
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-4.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapping works when property is non-configurable, arguments property
+ was not deleted. [[Delete]] operations throws TypeError if called
+ from strict-mode code. Arguments property is changed with [[Set]].
+flags: [noStrict]
+---*/
+
+function argumentsAndStrictDeleteSet(a) {
+ Object.defineProperty(arguments, "0", {configurable: false});
+
+ // Precondition: Delete is unsuccessful and doesn't affect mapping.
+ var args = arguments;
+ assert.throws(TypeError, function() { "use strict"; delete args[0]; });
+ assert.sameValue(a, 1);
+ assert.sameValue(arguments[0], 1);
+
+ arguments[0] = 2;
+ assert.sameValue(a, 2);
+ assert.sameValue(arguments[0], 2);
+}
+argumentsAndStrictDeleteSet(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-1.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-1.js
new file mode 100755
index 000000000..2dae07678
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-1.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapped arguments property is changed to non-writable and
+ non-configurable. Perform property attribute changes with two
+ consecutive [[DefineOwnProperty]] calls. Mapped values are
+ unchanged, mapping itself is removed.
+flags: [noStrict]
+---*/
+
+function argumentsNonWritableThenNonConfigurable(a) {
+ Object.defineProperty(arguments, "0", {writable: false});
+ Object.defineProperty(arguments, "0", {configurable: false});
+ assert.sameValue(a, 1);
+ assert.sameValue(arguments[0], 1);
+
+ // Postcondition: Arguments mapping is removed.
+ a = 2;
+ assert.sameValue(a, 2);
+ assert.sameValue(arguments[0], 1);
+}
+argumentsNonWritableThenNonConfigurable(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-2.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-2.js
new file mode 100755
index 000000000..63585b437
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-2.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapped arguments property is changed to non-writable and
+ non-configurable. Perform property attribute changes with two
+ [[DefineOwnProperty]] calls. Add intervening call to
+ SetMutableBinding.
+flags: [noStrict]
+---*/
+
+function argumentsNonWritableThenNonConfigurableWithInterveningSetMutableBinding(a) {
+ Object.defineProperty(arguments, "0", {writable: false});
+ a = 2;
+ Object.defineProperty(arguments, "0", {configurable: false});
+ assert.sameValue(a, 2);
+ assert.sameValue(arguments[0], 1);
+
+ // Postcondition: Arguments mapping is removed.
+ a = 3;
+ assert.sameValue(a, 3);
+ assert.sameValue(arguments[0], 1);
+}
+argumentsNonWritableThenNonConfigurableWithInterveningSetMutableBinding(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-3.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-3.js
new file mode 100755
index 000000000..2bd1bc9c1
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-3.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapped arguments property is changed to non-writable and
+ non-configurable. Perform property attribute changes with two
+ [[DefineOwnProperty]] calls. Add intervening call to [[Set]].
+flags: [noStrict]
+---*/
+
+function argumentsNonWritableThenNonConfigurableWithInterveningSet(a) {
+ Object.defineProperty(arguments, "0", {writable: false});
+ arguments[0] = 2;
+ Object.defineProperty(arguments, "0", {configurable: false});
+ assert.sameValue(a, 1);
+ assert.sameValue(arguments[0], 1);
+
+ // Postcondition: Arguments mapping is removed.
+ a = 3;
+ assert.sameValue(a, 3);
+ assert.sameValue(arguments[0], 1);
+}
+argumentsNonWritableThenNonConfigurableWithInterveningSet(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-4.js b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-4.js
new file mode 100755
index 000000000..ea40a49a8
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-4.js
@@ -0,0 +1,26 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+info: Mapped arguments object with non-configurable property
+description: >
+ Mapped arguments property is changed to non-writable and
+ non-configurable. Perform property attribute changes with two
+ [[DefineOwnProperty]] calls. Add intervening call to
+ [[DefineOwnProperty]].
+flags: [noStrict]
+---*/
+
+function argumentsNonWritableThenNonConfigurableWithInterveningDefineOwnProperty(a) {
+ Object.defineProperty(arguments, "0", {writable: false});
+ Object.defineProperty(arguments, "0", {value: 2});
+ Object.defineProperty(arguments, "0", {configurable: false});
+ assert.sameValue(a, 1);
+ assert.sameValue(arguments[0], 2);
+
+ // Postcondition: Arguments mapping is removed.
+ a = 3;
+ assert.sameValue(a, 3);
+ assert.sameValue(arguments[0], 2);
+}
+argumentsNonWritableThenNonConfigurableWithInterveningDefineOwnProperty(1);
diff --git a/js/src/tests/test262/language/arguments-object/mapped/shell.js b/js/src/tests/test262/language/arguments-object/mapped/shell.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/mapped/shell.js
diff --git a/js/src/tests/test262/language/arguments-object/shell.js b/js/src/tests/test262/language/arguments-object/shell.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/js/src/tests/test262/language/arguments-object/shell.js
diff --git a/js/src/tests/test262/language/shell.js b/js/src/tests/test262/language/shell.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/js/src/tests/test262/language/shell.js
diff --git a/js/src/tests/test262/shell.js b/js/src/tests/test262/shell.js
index b70bb5dbb..462ec9cf8 100644
--- a/js/src/tests/test262/shell.js
+++ b/js/src/tests/test262/shell.js
@@ -938,3 +938,31 @@ var fnGlobalObject = (function()
var global = Function("return this")();
return function fnGlobalObject() { return global; };
})();
+
+var assert = {
+ sameValue: assertEq,
+ notSameValue(a, b, msg) {
+ try {
+ assertEq(a, b);
+ throw "equal"
+ } catch (e) {
+ if (e === "equal")
+ throw new Error("Assertion failed: expected different values, got " + a);
+ }
+ },
+ throws(ctor, f) {
+ var fullmsg;
+ try {
+ f();
+ } catch (exc) {
+ if (exc instanceof ctor)
+ return;
+ fullmsg = "Assertion failed: expected exception " + ctor.name + ", got " + exc;
+ }
+ if (fullmsg === undefined)
+ fullmsg = "Assertion failed: expected exception " + ctor.name + ", no exception thrown";
+ if (msg !== undefined)
+ fullmsg += " - " + msg;
+ throw new Error(fullmsg);
+ }
+}
diff --git a/js/src/vm/ArgumentsObject.cpp b/js/src/vm/ArgumentsObject.cpp
index 717aa1050..66e0f40a2 100644
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -214,7 +214,7 @@ ArgumentsObject::createTemplateObject(JSContext* cx, bool mapped)
? &MappedArgumentsObject::class_
: &UnmappedArgumentsObject::class_;
- RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx));
+ RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, cx->global()));
if (!proto)
return nullptr;
@@ -475,7 +475,7 @@ MappedArgSetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue
attrs &= (JSPROP_ENUMERATE | JSPROP_PERMANENT); /* only valid attributes */
RootedFunction callee(cx, &argsobj->callee());
- RootedScript script(cx, callee->getOrCreateScript(cx));
+ RootedScript script(cx, JSFunction::getOrCreateScript(cx, callee));
if (!script)
return false;
@@ -590,6 +590,64 @@ MappedArgumentsObject::obj_enumerate(JSContext* cx, HandleObject obj)
return true;
}
+// ES 2017 draft 9.4.4.2
+/* static */ bool
+MappedArgumentsObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
+ Handle<PropertyDescriptor> desc, ObjectOpResult& result)
+{
+ // Step 1.
+ Rooted<MappedArgumentsObject*> argsobj(cx, &obj->as<MappedArgumentsObject>());
+
+ // Steps 2-3.
+ bool isMapped = false;
+ if (JSID_IS_INT(id)) {
+ unsigned arg = unsigned(JSID_TO_INT(id));
+ isMapped = arg < argsobj->initialLength() && !argsobj->isElementDeleted(arg);
+ }
+
+ // Step 4.
+ Rooted<PropertyDescriptor> newArgDesc(cx, desc);
+ if (!desc.isAccessorDescriptor() && isMapped) {
+ // In this case the live mapping is supposed to keep working,
+ // we have to pass along the Getter/Setter otherwise they are overwritten.
+ newArgDesc.setGetter(MappedArgGetter);
+ newArgDesc.setSetter(MappedArgSetter);
+ }
+
+ // Steps 5-6. NativeDefineProperty will lookup [[Value]] for us.
+ if (!NativeDefineProperty(cx, obj.as<NativeObject>(), id, newArgDesc, result))
+ return false;
+ // Step 7.
+ if (!result.ok())
+ return true;
+
+ // Step 8.
+ if (isMapped) {
+ unsigned arg = unsigned(JSID_TO_INT(id));
+ if (desc.isAccessorDescriptor()) {
+ if (!argsobj->markElementDeleted(cx, arg))
+ return false;
+ } else {
+ if (desc.hasValue()) {
+ RootedFunction callee(cx, &argsobj->callee());
+ RootedScript script(cx, JSFunction::getOrCreateScript(cx, callee));
+ if (!script)
+ return false;
+ argsobj->setElement(cx, arg, desc.value());
+ if (arg < script->functionNonDelazifying()->nargs())
+ TypeScript::SetArgument(cx, script, arg, desc.value());
+ }
+ if (desc.hasWritable() && !desc.writable()) {
+ if (!argsobj->markElementDeleted(cx, arg))
+ return false;
+ }
+ }
+ }
+
+ // Step 9.
+ return result.succeed();
+}
+
static bool
UnmappedArgGetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
{
@@ -804,6 +862,11 @@ const ClassOps MappedArgumentsObject::classOps_ = {
ArgumentsObject::trace
};
+const ObjectOps MappedArgumentsObject::objectOps_ = {
+ nullptr, /* lookupProperty */
+ MappedArgumentsObject::obj_defineProperty
+};
+
const Class MappedArgumentsObject::class_ = {
"Arguments",
JSCLASS_DELAY_METADATA_BUILDER |
@@ -811,7 +874,10 @@ const Class MappedArgumentsObject::class_ = {
JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
JSCLASS_SKIP_NURSERY_FINALIZE |
JSCLASS_BACKGROUND_FINALIZE,
- &MappedArgumentsObject::classOps_
+ &MappedArgumentsObject::classOps_,
+ nullptr,
+ nullptr,
+ &MappedArgumentsObject::objectOps_
};
/*
diff --git a/js/src/vm/ArgumentsObject.h b/js/src/vm/ArgumentsObject.h
index 247c7cd94..988e41951 100644
--- a/js/src/vm/ArgumentsObject.h
+++ b/js/src/vm/ArgumentsObject.h
@@ -389,6 +389,7 @@ class ArgumentsObject : public NativeObject
class MappedArgumentsObject : public ArgumentsObject
{
static const ClassOps classOps_;
+ static const ObjectOps objectOps_;
public:
static const Class class_;
@@ -410,6 +411,8 @@ class MappedArgumentsObject : public ArgumentsObject
private:
static bool obj_enumerate(JSContext* cx, HandleObject obj);
static bool obj_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp);
+ static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
+ Handle<JS::PropertyDescriptor> desc, ObjectOpResult& result);
};
class UnmappedArgumentsObject : public ArgumentsObject
diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp
index 1053fa99d..392724b21 100644
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -140,7 +140,7 @@ static const Class ArrayBufferObjectProtoClass = {
static JSObject*
CreateArrayBufferPrototype(JSContext* cx, JSProtoKey key)
{
- return cx->global()->createBlankPrototype(cx, &ArrayBufferObjectProtoClass);
+ return GlobalObject::createBlankPrototype(cx, cx->global(), &ArrayBufferObjectProtoClass);
}
static const ClassOps ArrayBufferObjectClassOps = {
@@ -344,7 +344,7 @@ ArrayBufferObject::detach(JSContext* cx, Handle<ArrayBufferObject*> buffer,
// Make sure the global object's group has been instantiated, so the
// flag change will be observed.
AutoEnterOOMUnsafeRegion oomUnsafe;
- if (!cx->global()->getGroup(cx))
+ if (!JSObject::getGroup(cx, cx->global()))
oomUnsafe.crash("ArrayBufferObject::detach");
MarkObjectGroupFlags(cx, cx->global(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER);
cx->compartment()->detachedTypedObjects = 1;
diff --git a/js/src/vm/AsyncFunction.cpp b/js/src/vm/AsyncFunction.cpp
index f50c87114..e14b77424 100644
--- a/js/src/vm/AsyncFunction.cpp
+++ b/js/src/vm/AsyncFunction.cpp
@@ -118,7 +118,7 @@ js::WrapAsyncFunctionWithProto(JSContext* cx, HandleFunction unwrapped, HandleOb
RootedAtom funName(cx, unwrapped->explicitName());
uint16_t length;
- if (!unwrapped->getLength(cx, &length))
+ if (!JSFunction::getLength(cx, unwrapped, &length))
return nullptr;
// Steps 3 (partially).
diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h
index 8a36df083..fd1c9f5e6 100644
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -38,6 +38,7 @@
macro(Bool32x4, Bool32x4, "Bool32x4") \
macro(Bool64x2, Bool64x2, "Bool64x2") \
macro(boundWithSpace, boundWithSpace, "bound ") \
+ macro(break, break_, "break") \
macro(breakdown, breakdown, "breakdown") \
macro(buffer, buffer, "buffer") \
macro(builder, builder, "builder") \
@@ -52,8 +53,10 @@
macro(callee, callee, "callee") \
macro(caller, caller, "caller") \
macro(callFunction, callFunction, "callFunction") \
+ macro(case, case_, "case") \
macro(caseFirst, caseFirst, "caseFirst") \
- macro(class_, class_, "class") \
+ macro(catch, catch_, "catch") \
+ macro(class, class_, "class") \
macro(close, close, "close") \
macro(Collator, Collator, "Collator") \
macro(CollatorCompareGet, CollatorCompareGet, "Intl_Collator_compare_get") \
@@ -62,10 +65,14 @@
macro(comma, comma, ",") \
macro(compare, compare, "compare") \
macro(configurable, configurable, "configurable") \
+ macro(const, const_, "const") \
macro(construct, construct, "construct") \
macro(constructContentFunction, constructContentFunction, "constructContentFunction") \
macro(constructor, constructor, "constructor") \
+ macro(continue, continue_, "continue") \
macro(ConvertAndCopyTo, ConvertAndCopyTo, "ConvertAndCopyTo") \
+ macro(CopyDataProperties, CopyDataProperties, "CopyDataProperties") \
+ macro(CopyDataPropertiesUnfiltered, CopyDataPropertiesUnfiltered, "CopyDataPropertiesUnfiltered") \
macro(copyWithin, copyWithin, "copyWithin") \
macro(count, count, "count") \
macro(CreateResolvingFunctions, CreateResolvingFunctions, "CreateResolvingFunctions") \
@@ -76,28 +83,32 @@
macro(DateTimeFormatFormatToParts, DateTimeFormatFormatToParts, "Intl_DateTimeFormat_formatToParts") \
macro(day, day, "day") \
macro(dayPeriod, dayPeriod, "dayPeriod") \
+ macro(debugger, debugger, "debugger") \
macro(decodeURI, decodeURI, "decodeURI") \
macro(decodeURIComponent, decodeURIComponent, "decodeURIComponent") \
macro(DefaultBaseClassConstructor, DefaultBaseClassConstructor, "DefaultBaseClassConstructor") \
macro(DefaultDerivedClassConstructor, DefaultDerivedClassConstructor, "DefaultDerivedClassConstructor") \
- macro(default_, default_, "default") \
+ macro(default, default_, "default") \
macro(defineGetter, defineGetter, "__defineGetter__") \
macro(defineProperty, defineProperty, "defineProperty") \
macro(defineSetter, defineSetter, "__defineSetter__") \
macro(delete, delete_, "delete") \
macro(deleteProperty, deleteProperty, "deleteProperty") \
macro(displayURL, displayURL, "displayURL") \
+ macro(do, do_, "do") \
macro(done, done, "done") \
macro(dotGenerator, dotGenerator, ".generator") \
macro(dotThis, dotThis, ".this") \
macro(each, each, "each") \
macro(elementType, elementType, "elementType") \
+ macro(else, else_, "else") \
macro(empty, empty, "") \
macro(emptyRegExp, emptyRegExp, "(?:)") \
macro(encodeURI, encodeURI, "encodeURI") \
macro(encodeURIComponent, encodeURIComponent, "encodeURIComponent") \
macro(endTimestamp, endTimestamp, "endTimestamp") \
macro(entries, entries, "entries") \
+ macro(enum, enum_, "enum") \
macro(enumerable, enumerable, "enumerable") \
macro(enumerate, enumerate, "enumerate") \
macro(era, era, "era") \
@@ -105,11 +116,14 @@
macro(escape, escape, "escape") \
macro(eval, eval, "eval") \
macro(exec, exec, "exec") \
+ macro(export, export_, "export") \
+ macro(extends, extends, "extends") \
macro(false, false_, "false") \
macro(fieldOffsets, fieldOffsets, "fieldOffsets") \
macro(fieldTypes, fieldTypes, "fieldTypes") \
macro(fileName, fileName, "fileName") \
macro(fill, fill, "fill") \
+ macro(finally, finally_, "finally") \
macro(find, find, "find") \
macro(findIndex, findIndex, "findIndex") \
macro(firstDayOfWeek, firstDayOfWeek, "firstDayOfWeek") \
@@ -121,6 +135,7 @@
macro(Float32x4, Float32x4, "Float32x4") \
macro(float64, float64, "float64") \
macro(Float64x2, Float64x2, "Float64x2") \
+ macro(for, for_, "for") \
macro(forceInterpreter, forceInterpreter, "forceInterpreter") \
macro(forEach, forEach, "forEach") \
macro(format, format, "format") \
@@ -146,8 +161,12 @@
macro(hasOwn, hasOwn, "hasOwn") \
macro(hasOwnProperty, hasOwnProperty, "hasOwnProperty") \
macro(hour, hour, "hour") \
+ macro(if, if_, "if") \
macro(ignoreCase, ignoreCase, "ignoreCase") \
macro(ignorePunctuation, ignorePunctuation, "ignorePunctuation") \
+ macro(implements, implements, "implements") \
+ macro(import, import, "import") \
+ macro(in, in, "in") \
macro(includes, includes, "includes") \
macro(incumbentGlobal, incumbentGlobal, "incumbentGlobal") \
macro(index, index, "index") \
@@ -158,12 +177,14 @@
macro(innermost, innermost, "innermost") \
macro(inNursery, inNursery, "inNursery") \
macro(input, input, "input") \
+ macro(instanceof, instanceof, "instanceof") \
macro(int8, int8, "int8") \
macro(int16, int16, "int16") \
macro(int32, int32, "int32") \
macro(Int8x16, Int8x16, "Int8x16") \
macro(Int16x8, Int16x8, "Int16x8") \
macro(Int32x4, Int32x4, "Int32x4") \
+ macro(interface, interface, "interface") \
macro(InterpretGeneratorResume, InterpretGeneratorResume, "InterpretGeneratorResume") \
macro(isEntryPoint, isEntryPoint, "isEntryPoint") \
macro(isExtensible, isExtensible, "isExtensible") \
@@ -217,6 +238,7 @@
macro(noFilename, noFilename, "noFilename") \
macro(nonincrementalReason, nonincrementalReason, "nonincrementalReason") \
macro(noStack, noStack, "noStack") \
+ macro(notes, notes, "notes") \
macro(NumberFormat, NumberFormat, "NumberFormat") \
macro(NumberFormatFormatGet, NumberFormatFormatGet, "Intl_NumberFormat_format_get") \
macro(numeric, numeric, "numeric") \
@@ -238,13 +260,18 @@
macro(other, other, "other") \
macro(outOfMemory, outOfMemory, "out of memory") \
macro(ownKeys, ownKeys, "ownKeys") \
+ macro(Object_valueOf, Object_valueOf, "Object_valueOf") \
+ macro(package, package, "package") \
macro(parseFloat, parseFloat, "parseFloat") \
macro(parseInt, parseInt, "parseInt") \
macro(pattern, pattern, "pattern") \
macro(pending, pending, "pending") \
+ macro(public, public_, "public") \
macro(preventExtensions, preventExtensions, "preventExtensions") \
+ macro(private, private_, "private") \
macro(promise, promise, "promise") \
macro(propertyIsEnumerable, propertyIsEnumerable, "propertyIsEnumerable") \
+ macro(protected, protected_, "protected") \
macro(proto, proto, "__proto__") \
macro(prototype, prototype, "prototype") \
macro(proxy, proxy, "proxy") \
@@ -293,10 +320,12 @@
macro(StructType, StructType, "StructType") \
macro(style, style, "style") \
macro(super, super, "super") \
+ macro(switch, switch_, "switch") \
macro(Symbol_iterator_fun, Symbol_iterator_fun, "[Symbol.iterator]") \
macro(target, target, "target") \
macro(test, test, "test") \
macro(then, then, "then") \
+ macro(this, this_, "this") \
macro(throw, throw_, "throw") \
macro(timestamp, timestamp, "timestamp") \
macro(timeZone, timeZone, "timeZone") \
@@ -309,7 +338,9 @@
macro(toString, toString, "toString") \
macro(toUTCString, toUTCString, "toUTCString") \
macro(true, true_, "true") \
+ macro(try, try_, "try") \
macro(type, type, "type") \
+ macro(typeof, typeof_, "typeof") \
macro(uint8, uint8, "uint8") \
macro(uint8Clamped, uint8Clamped, "uint8Clamped") \
macro(uint16, uint16, "uint16") \
@@ -329,6 +360,7 @@
macro(useAsm, useAsm, "use asm") \
macro(useGrouping, useGrouping, "useGrouping") \
macro(useStrict, useStrict, "use strict") \
+ macro(void, void_, "void") \
macro(value, value, "value") \
macro(valueOf, valueOf, "valueOf") \
macro(values, values, "values") \
@@ -343,6 +375,8 @@
macro(weekday, weekday, "weekday") \
macro(weekendEnd, weekendEnd, "weekendEnd") \
macro(weekendStart, weekendStart, "weekendStart") \
+ macro(while, while_, "while") \
+ macro(with, with, "with") \
macro(writable, writable, "writable") \
macro(year, year, "year") \
macro(yield, yield, "yield") \
diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp
index d16781326..d68d1b75e 100644
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -224,7 +224,7 @@ EnsureFunctionHasScript(JSContext* cx, HandleFunction fun)
{
if (fun->isInterpretedLazy()) {
AutoCompartment ac(cx, fun);
- return !!fun->getOrCreateScript(cx);
+ return !!JSFunction::getOrCreateScript(cx, fun);
}
return true;
}
@@ -2234,7 +2234,7 @@ Debugger::appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame
RootedAtom ctorName(cx);
{
AutoCompartment ac(cx, obj);
- if (!obj->constructorDisplayAtom(cx, &ctorName))
+ if (!JSObject::constructorDisplayAtom(cx, obj, &ctorName))
return false;
}
@@ -7227,8 +7227,8 @@ static const JSFunctionSpec DebuggerSource_methods[] = {
/* static */ NativeObject*
DebuggerFrame::initClass(JSContext* cx, HandleObject dbgCtor, HandleObject obj)
{
- Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
- RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
+ RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
return InitClass(cx, dbgCtor, objProto, &class_, construct, 0, properties_,
methods_, nullptr, nullptr);
@@ -8666,6 +8666,14 @@ DebuggerObject::errorMessageNameGetter(JSContext *cx, unsigned argc, Value* vp)
}
/* static */ bool
+DebuggerObject::errorNotesGetter(JSContext *cx, unsigned argc, Value* vp)
+{
+ THIS_DEBUGOBJECT(cx, argc, vp, "get errorNotes", args, object)
+
+ return DebuggerObject::getErrorNotes(cx, object, args.rval());
+}
+
+/* static */ bool
DebuggerObject::errorLineNumberGetter(JSContext *cx, unsigned argc, Value* vp)
{
THIS_DEBUGOBJECT(cx, argc, vp, "get errorLineNumber", args, object)
@@ -9324,6 +9332,7 @@ const JSPropertySpec DebuggerObject::properties_[] = {
JS_PSG("global", DebuggerObject::globalGetter, 0),
JS_PSG("allocationSite", DebuggerObject::allocationSiteGetter, 0),
JS_PSG("errorMessageName", DebuggerObject::errorMessageNameGetter, 0),
+ JS_PSG("errorNotes", DebuggerObject::errorNotesGetter, 0),
JS_PSG("errorLineNumber", DebuggerObject::errorLineNumberGetter, 0),
JS_PSG("errorColumnNumber", DebuggerObject::errorColumnNumberGetter, 0),
JS_PSG("isProxy", DebuggerObject::isProxyGetter, 0),
@@ -9376,8 +9385,8 @@ const JSFunctionSpec DebuggerObject::methods_[] = {
/* static */ NativeObject*
DebuggerObject::initClass(JSContext* cx, HandleObject obj, HandleObject debugCtor)
{
- Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
- RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
+ RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
RootedNativeObject objectProto(cx, InitClass(cx, debugCtor, objProto, &class_,
construct, 0, properties_,
@@ -9611,7 +9620,7 @@ DebuggerObject::getBoundArguments(JSContext* cx, HandleDebuggerObject object,
if (!result.resize(length))
return false;
for (size_t i = 0; i < length; i++) {
- result[i].set(referent->getBoundFunctionArgument(cx, i));
+ result[i].set(referent->getBoundFunctionArgument(i));
if (!dbg->wrapDebuggeeValue(cx, result[i]))
return false;
}
@@ -9695,6 +9704,30 @@ DebuggerObject::getErrorMessageName(JSContext* cx, HandleDebuggerObject object,
}
/* static */ bool
+DebuggerObject::getErrorNotes(JSContext* cx, HandleDebuggerObject object,
+ MutableHandleValue result)
+{
+ RootedObject referent(cx, object->referent());
+ JSErrorReport* report;
+ if (!getErrorReport(cx, referent, report))
+ return false;
+
+ if (!report) {
+ result.setUndefined();
+ return true;
+ }
+
+ RootedObject errorNotesArray(cx, CreateErrorNotesArray(cx, report));
+ if (!errorNotesArray)
+ return false;
+
+ if (!cx->compartment()->wrap(cx, &errorNotesArray))
+ return false;
+ result.setObject(*errorNotesArray);
+ return true;
+}
+
+/* static */ bool
DebuggerObject::getErrorLineNumber(JSContext* cx, HandleDebuggerObject object,
MutableHandleValue result)
{
@@ -10577,8 +10610,8 @@ const JSFunctionSpec DebuggerEnvironment::methods_[] = {
/* static */ NativeObject*
DebuggerEnvironment::initClass(JSContext* cx, HandleObject dbgCtor, HandleObject obj)
{
- Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
- RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
+ RootedObject objProto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
return InitClass(cx, dbgCtor, objProto, &DebuggerEnvironment::class_, construct, 0,
properties_, methods_, nullptr, nullptr);
@@ -10774,7 +10807,8 @@ DebuggerEnvironment::getVariable(JSContext* cx, HandleDebuggerEnvironment enviro
//
// See wrapDebuggeeValue for how the sentinel values are wrapped.
if (referent->is<DebugEnvironmentProxy>()) {
- if (!referent->as<DebugEnvironmentProxy>().getMaybeSentinelValue(cx, id, result))
+ Rooted<DebugEnvironmentProxy*> env(cx, &referent->as<DebugEnvironmentProxy>());
+ if (!DebugEnvironmentProxy::getMaybeSentinelValue(cx, env, id, result))
return false;
} else {
if (!GetProperty(cx, referent, referent, id, result))
@@ -10942,9 +10976,9 @@ JS_DefineDebuggerObject(JSContext* cx, HandleObject obj)
memoryProto(cx);
RootedObject debuggeeWouldRunProto(cx);
RootedValue debuggeeWouldRunCtor(cx);
- Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
+ Handle<GlobalObject*> global = obj.as<GlobalObject>();
- objProto = global->getOrCreateObjectPrototype(cx);
+ objProto = GlobalObject::getOrCreateObjectPrototype(cx, global);
if (!objProto)
return false;
debugProto = InitClass(cx, obj,
diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h
index 3239ade6d..cdcf2d67f 100644
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -1246,6 +1246,8 @@ class DebuggerObject : public NativeObject
MutableHandleObject result);
static MOZ_MUST_USE bool getErrorMessageName(JSContext* cx, HandleDebuggerObject object,
MutableHandleString result);
+ static MOZ_MUST_USE bool getErrorNotes(JSContext* cx, HandleDebuggerObject object,
+ MutableHandleValue result);
static MOZ_MUST_USE bool getErrorLineNumber(JSContext* cx, HandleDebuggerObject object,
MutableHandleValue result);
static MOZ_MUST_USE bool getErrorColumnNumber(JSContext* cx, HandleDebuggerObject object,
@@ -1371,6 +1373,7 @@ class DebuggerObject : public NativeObject
static MOZ_MUST_USE bool globalGetter(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE bool allocationSiteGetter(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE bool errorMessageNameGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool errorNotesGetter(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE bool errorLineNumberGetter(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE bool errorColumnNumberGetter(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE bool isProxyGetter(JSContext* cx, unsigned argc, Value* vp);
diff --git a/js/src/vm/EnvironmentObject.cpp b/js/src/vm/EnvironmentObject.cpp
index 34c39eabf..a5aac2ab4 100644
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -816,7 +816,7 @@ NonSyntacticVariablesObject::create(JSContext* cx)
return nullptr;
MOZ_ASSERT(obj->isUnqualifiedVarObj());
- if (!obj->setQualifiedVarObj(cx))
+ if (!JSObject::setQualifiedVarObj(cx, obj))
return nullptr;
obj->initEnclosingEnvironment(&cx->global()->lexicalEnvironment());
@@ -957,7 +957,7 @@ LexicalEnvironmentObject::createHollowForDebug(JSContext* cx, Handle<LexicalScop
return nullptr;
}
- if (!env->setFlags(cx, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE))
+ if (!JSObject::setFlags(cx, env, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE))
return nullptr;
env->initScopeUnchecked(scope);
@@ -1425,7 +1425,8 @@ class DebugEnvironmentProxyHandler : public BaseProxyHandler
/* Handle unaliased formals, vars, lets, and consts at function scope. */
if (env->is<CallObject>()) {
CallObject& callobj = env->as<CallObject>();
- RootedScript script(cx, callobj.callee().getOrCreateScript(cx));
+ RootedFunction fun(cx, &callobj.callee());
+ RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx))
return false;
@@ -2233,11 +2234,11 @@ DebugEnvironmentProxy::isForDeclarative() const
e.is<LexicalEnvironmentObject>();
}
-bool
-DebugEnvironmentProxy::getMaybeSentinelValue(JSContext* cx, HandleId id, MutableHandleValue vp)
+/* static */ bool
+DebugEnvironmentProxy::getMaybeSentinelValue(JSContext* cx, Handle<DebugEnvironmentProxy*> env,
+ HandleId id, MutableHandleValue vp)
{
- Rooted<DebugEnvironmentProxy*> self(cx, this);
- return DebugEnvironmentProxyHandler::singleton.getMaybeSentinelValue(cx, self, id, vp);
+ return DebugEnvironmentProxyHandler::singleton.getMaybeSentinelValue(cx, env, id, vp);
}
bool
@@ -2960,7 +2961,7 @@ js::GetDebugEnvironmentForFunction(JSContext* cx, HandleFunction fun)
MOZ_ASSERT(CanUseDebugEnvironmentMaps(cx));
if (!DebugEnvironments::updateLiveEnvironments(cx))
return nullptr;
- JSScript* script = fun->getOrCreateScript(cx);
+ JSScript* script = JSFunction::getOrCreateScript(cx, fun);
if (!script)
return nullptr;
EnvironmentIter ei(cx, fun->environment(), script->enclosingScope());
@@ -3468,11 +3469,13 @@ RemoveReferencedNames(JSContext* cx, HandleScript script, PropertyNameSet& remai
if (script->hasObjects()) {
ObjectArray* objects = script->objects();
+ RootedFunction fun(cx);
+ RootedScript innerScript(cx);
for (size_t i = 0; i < objects->length; i++) {
JSObject* obj = objects->vector[i];
if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) {
- JSFunction* fun = &obj->as<JSFunction>();
- RootedScript innerScript(cx, fun->getOrCreateScript(cx));
+ fun = &obj->as<JSFunction>();
+ innerScript = JSFunction::getOrCreateScript(cx, fun);
if (!innerScript)
return false;
@@ -3535,11 +3538,13 @@ AnalyzeEntrainedVariablesInScript(JSContext* cx, HandleScript script, HandleScri
if (innerScript->hasObjects()) {
ObjectArray* objects = innerScript->objects();
+ RootedFunction fun(cx);
+ RootedScript innerInnerScript(cx);
for (size_t i = 0; i < objects->length; i++) {
JSObject* obj = objects->vector[i];
if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) {
- JSFunction* fun = &obj->as<JSFunction>();
- RootedScript innerInnerScript(cx, fun->getOrCreateScript(cx));
+ fun = &obj->as<JSFunction>();
+ innerInnerScript = JSFunction::getOrCreateScript(cx, fun);
if (!innerInnerScript ||
!AnalyzeEntrainedVariablesInScript(cx, script, innerInnerScript))
{
@@ -3570,11 +3575,13 @@ js::AnalyzeEntrainedVariables(JSContext* cx, HandleScript script)
return true;
ObjectArray* objects = script->objects();
+ RootedFunction fun(cx);
+ RootedScript innerScript(cx);
for (size_t i = 0; i < objects->length; i++) {
JSObject* obj = objects->vector[i];
if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) {
- JSFunction* fun = &obj->as<JSFunction>();
- RootedScript innerScript(cx, fun->getOrCreateScript(cx));
+ fun = &obj->as<JSFunction>();
+ innerScript = JSFunction::getOrCreateScript(cx, fun);
if (!innerScript)
return false;
diff --git a/js/src/vm/EnvironmentObject.h b/js/src/vm/EnvironmentObject.h
index 032286116..c527cd1b0 100644
--- a/js/src/vm/EnvironmentObject.h
+++ b/js/src/vm/EnvironmentObject.h
@@ -872,7 +872,8 @@ class DebugEnvironmentProxy : public ProxyObject
// Get a property by 'id', but returns sentinel values instead of throwing
// on exceptional cases.
- bool getMaybeSentinelValue(JSContext* cx, HandleId id, MutableHandleValue vp);
+ static bool getMaybeSentinelValue(JSContext* cx, Handle<DebugEnvironmentProxy*> env,
+ HandleId id, MutableHandleValue vp);
// Returns true iff this is a function environment with its own this-binding
// (all functions except arrow functions and generator expression lambdas).
diff --git a/js/src/vm/ErrorObject.cpp b/js/src/vm/ErrorObject.cpp
index d8d29830b..271132801 100644
--- a/js/src/vm/ErrorObject.cpp
+++ b/js/src/vm/ErrorObject.cpp
@@ -29,11 +29,11 @@ js::ErrorObject::assignInitialShape(ExclusiveContext* cx, Handle<ErrorObject*> o
{
MOZ_ASSERT(obj->empty());
- if (!obj->addDataProperty(cx, cx->names().fileName, FILENAME_SLOT, 0))
+ if (!NativeObject::addDataProperty(cx, obj, cx->names().fileName, FILENAME_SLOT, 0))
return nullptr;
- if (!obj->addDataProperty(cx, cx->names().lineNumber, LINENUMBER_SLOT, 0))
+ if (!NativeObject::addDataProperty(cx, obj, cx->names().lineNumber, LINENUMBER_SLOT, 0))
return nullptr;
- return obj->addDataProperty(cx, cx->names().columnNumber, COLUMNNUMBER_SLOT, 0);
+ return NativeObject::addDataProperty(cx, obj, cx->names().columnNumber, COLUMNNUMBER_SLOT, 0);
}
/* static */ bool
@@ -57,7 +57,7 @@ js::ErrorObject::init(JSContext* cx, Handle<ErrorObject*> obj, JSExnType type,
// |new Error()|.
RootedShape messageShape(cx);
if (message) {
- messageShape = obj->addDataProperty(cx, cx->names().message, MESSAGE_SLOT, 0);
+ messageShape = NativeObject::addDataProperty(cx, obj, cx->names().message, MESSAGE_SLOT, 0);
if (!messageShape)
return false;
MOZ_ASSERT(messageShape->slot() == MESSAGE_SLOT);
diff --git a/js/src/vm/ErrorReporting.cpp b/js/src/vm/ErrorReporting.cpp
new file mode 100644
index 000000000..5877f3a4b
--- /dev/null
+++ b/js/src/vm/ErrorReporting.cpp
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "vm/ErrorReporting.h"
+
+#include "mozilla/Move.h"
+
+#include <stdarg.h>
+
+#include "jscntxt.h"
+#include "jsexn.h"
+
+using mozilla::Move;
+
+using JS::UniqueTwoByteChars;
+
+void
+CallWarningReporter(JSContext* cx, JSErrorReport* reportp)
+{
+ MOZ_ASSERT(reportp);
+ MOZ_ASSERT(JSREPORT_IS_WARNING(reportp->flags));
+
+ if (JS::WarningReporter warningReporter = cx->runtime()->warningReporter)
+ warningReporter(cx, reportp);
+}
+
+void
+CompileError::throwError(JSContext* cx)
+{
+ if (JSREPORT_IS_WARNING(flags)) {
+ CallWarningReporter(cx, this);
+ return;
+ }
+
+ // If there's a runtime exception type associated with this error
+ // number, set that as the pending exception. For errors occuring at
+ // compile time, this is very likely to be a JSEXN_SYNTAXERR.
+ //
+ // If an exception is thrown but not caught, the JSREPORT_EXCEPTION
+ // flag will be set in report.flags. Proper behavior for an error
+ // reporter is to ignore a report with this flag for all but top-level
+ // compilation errors. The exception will remain pending, and so long
+ // as the non-top-level "load", "eval", or "compile" native function
+ // returns false, the top-level reporter will eventually receive the
+ // uncaught exception report.
+ ErrorToException(cx, this, nullptr, nullptr);
+}
+
+bool
+ReportCompileWarning(JSContext* cx, ErrorMetadata&& metadata, UniquePtr<JSErrorNotes> notes,
+ unsigned flags, unsigned errorNumber, va_list args)
+{
+ // On the main thread, report the error immediately. When compiling off
+ // thread, save the error so that the thread finishing the parse can report
+ // it later.
+ CompileError tempErr;
+ CompileError* err = &tempErr;
+ if (!cx->isJSContext() && !cx->addPendingCompileError(&err)) {
+ return false;
+ }
+
+ err->notes = Move(notes);
+ err->flags = flags;
+ err->errorNumber = errorNumber;
+
+ err->filename = metadata.filename;
+ err->lineno = metadata.lineNumber;
+ err->column = metadata.columnNumber;
+ err->isMuted = metadata.isMuted;
+
+ if (UniqueTwoByteChars lineOfContext = Move(metadata.lineOfContext))
+ err->initOwnedLinebuf(lineOfContext.release(), metadata.lineLength, metadata.tokenOffset);
+
+ if (!ExpandErrorArgumentsVA(cx, GetErrorMessage, nullptr, errorNumber,
+ nullptr, ArgumentsAreLatin1, err, args))
+ {
+ return false;
+ }
+
+ if (cx->isJSContext()) {
+ err->throwError(cx->asJSContext());
+ }
+
+ return true;
+}
+
+void
+ReportCompileError(JSContext* cx, ErrorMetadata&& metadata, UniquePtr<JSErrorNotes> notes,
+ unsigned flags, unsigned errorNumber, va_list args)
+{
+ // On the main thread, report the error immediately. When compiling off
+ // thread, save the error so that the thread finishing the parse can report
+ // it later.
+ CompileError tempErr;
+ CompileError* err = &tempErr;
+ if (!cx->isJSContext() && !cx->addPendingCompileError(&err)) {
+ return;
+ }
+
+ err->notes = Move(notes);
+ err->flags = flags;
+ err->errorNumber = errorNumber;
+
+ err->filename = metadata.filename;
+ err->lineno = metadata.lineNumber;
+ err->column = metadata.columnNumber;
+ err->isMuted = metadata.isMuted;
+
+ if (UniqueTwoByteChars lineOfContext = Move(metadata.lineOfContext))
+ err->initOwnedLinebuf(lineOfContext.release(), metadata.lineLength, metadata.tokenOffset);
+
+ if (!ExpandErrorArgumentsVA(cx, GetErrorMessage, nullptr, errorNumber,
+ nullptr, ArgumentsAreLatin1, err, args))
+ {
+ return;
+ }
+
+ if (cx->isJSContext()) {
+ err->throwError(cx->asJSContext());
+ }
+}
diff --git a/js/src/vm/ErrorReporting.h b/js/src/vm/ErrorReporting.h
new file mode 100644
index 000000000..02bbe2c63
--- /dev/null
+++ b/js/src/vm/ErrorReporting.h
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef vm_ErrorReporting_h
+#define vm_ErrorReporting_h
+
+#include "mozilla/Move.h"
+
+#include <stdarg.h>
+
+#include "jsapi.h" // for JSErrorNotes, JSErrorReport
+
+#include "js/UniquePtr.h" // for UniquePtr
+#include "js/Utility.h" // for UniqueTwoByteChars
+
+struct JSContext;
+
+namespace js {
+
+/**
+ * Metadata for a compilation error (or warning) at a particular offset, or at
+ * no offset (i.e. with respect to a script overall).
+ */
+struct ErrorMetadata
+{
+ // The file/URL where the error occurred.
+ const char* filename;
+
+ // The line and column numbers where the error occurred. If the error
+ // is with respect to the entire script and not with respect to a
+ // particular location, these will both be zero.
+ uint32_t lineNumber;
+ uint32_t columnNumber;
+
+ // If the error occurs at a particular location, context surrounding the
+ // location of the error: the line that contained the error, or a small
+ // portion of it if the line is long.
+ //
+ // This information is provided on a best-effort basis: code populating
+ // ErrorMetadata instances isn't obligated to supply this.
+ JS::UniqueTwoByteChars lineOfContext;
+
+ // If |lineOfContext| is non-null, its length.
+ size_t lineLength;
+
+ // If |lineOfContext| is non-null, the offset within it of the token that
+ // triggered the error.
+ size_t tokenOffset;
+
+ // Whether the error is "muted" because it derives from a cross-origin
+ // load. See the comment in TransitiveCompileOptions in jsapi.h for
+ // details.
+ bool isMuted;
+};
+
+class CompileError : public JSErrorReport
+{
+ public:
+ void throwError(JSContext* cx);
+};
+
+/** Send a JSErrorReport to the warningReporter callback. */
+extern void
+CallWarningReporter(JSContext* cx, JSErrorReport* report);
+
+/**
+ * Report a compile error during script processing prior to execution of the
+ * script.
+ */
+extern void
+ReportCompileError(ErrorMetadata&& metadata, UniquePtr<JSErrorNotes> notes,
+ unsigned flags, unsigned errorNumber, va_list args);
+
+/**
+ * Report a compile warning during script processing prior to execution of the
+ * script. Returns true if the warning was successfully reported, false if an
+ * error occurred.
+ *
+ * This function DOES NOT respect an existing werror option. If the caller
+ * wishes such option to be respected, it must do so itself.
+ */
+extern MOZ_MUST_USE bool
+ReportCompileWarning(JSContext* cx, ErrorMetadata&& metadata, UniquePtr<JSErrorNotes> notes,
+ unsigned flags, unsigned errorNumber, va_list args);
+
+} // namespace js
+
+#endif /* vm_ErrorReporting_h */
diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp
index 690c0bf48..ba28501e6 100644
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -256,7 +256,7 @@ static const JSFunctionSpec legacy_generator_methods[] = {
static JSObject*
NewSingletonObjectWithObjectPrototype(JSContext* cx, Handle<GlobalObject*> global)
{
- RootedObject proto(cx, global->getOrCreateObjectPrototype(cx));
+ RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
if (!proto)
return nullptr;
return NewObjectWithGivenProto<PlainObject>(cx, proto, SingletonObject);
@@ -265,7 +265,7 @@ NewSingletonObjectWithObjectPrototype(JSContext* cx, Handle<GlobalObject*> globa
JSObject*
js::NewSingletonObjectWithFunctionPrototype(JSContext* cx, Handle<GlobalObject*> global)
{
- RootedObject proto(cx, global->getOrCreateFunctionPrototype(cx));
+ RootedObject proto(cx, GlobalObject::getOrCreateFunctionPrototype(cx, global));
if (!proto)
return nullptr;
return NewObjectWithGivenProto<PlainObject>(cx, proto, SingletonObject);
@@ -278,7 +278,7 @@ GlobalObject::initLegacyGeneratorProto(JSContext* cx, Handle<GlobalObject*> glob
return true;
RootedObject proto(cx, NewSingletonObjectWithObjectPrototype(cx, global));
- if (!proto || !proto->setDelegate(cx))
+ if (!proto || !JSObject::setDelegate(cx, proto))
return false;
if (!DefinePropertiesAndFunctions(cx, proto, nullptr, legacy_generator_methods))
return false;
@@ -297,9 +297,9 @@ GlobalObject::initStarGenerators(JSContext* cx, Handle<GlobalObject*> global)
if (!iteratorProto)
return false;
- RootedObject genObjectProto(cx, global->createBlankPrototypeInheriting(cx,
- &PlainObject::class_,
- iteratorProto));
+ RootedObject genObjectProto(cx, GlobalObject::createBlankPrototypeInheriting(cx, global,
+ &PlainObject::class_,
+ iteratorProto));
if (!genObjectProto)
return false;
if (!DefinePropertiesAndFunctions(cx, genObjectProto, nullptr, star_generator_methods) ||
@@ -309,7 +309,7 @@ GlobalObject::initStarGenerators(JSContext* cx, Handle<GlobalObject*> global)
}
RootedObject genFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global));
- if (!genFunctionProto || !genFunctionProto->setDelegate(cx))
+ if (!genFunctionProto || !JSObject::setDelegate(cx, genFunctionProto))
return false;
if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto) ||
!DefineToStringTag(cx, genFunctionProto, cx->names().GeneratorFunction))
diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp
index c90b6b85f..85707e1c6 100644
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -329,15 +329,15 @@ GlobalObject::createInternal(JSContext* cx, const Class* clasp)
cx->compartment()->initGlobal(*global);
- if (!global->setQualifiedVarObj(cx))
+ if (!JSObject::setQualifiedVarObj(cx, global))
return nullptr;
- if (!global->setDelegate(cx))
+ if (!JSObject::setDelegate(cx, global))
return nullptr;
return global;
}
-GlobalObject*
+/* static */ GlobalObject*
GlobalObject::new_(JSContext* cx, const Class* clasp, JSPrincipals* principals,
JS::OnNewGlobalHookOption hookOption,
const JS::CompartmentOptions& options)
@@ -398,7 +398,7 @@ GlobalObject::emptyGlobalScope() const
GlobalObject::getOrCreateEval(JSContext* cx, Handle<GlobalObject*> global,
MutableHandleObject eval)
{
- if (!global->getOrCreateObjectPrototype(cx))
+ if (!getOrCreateObjectPrototype(cx, global))
return false;
eval.set(&global->getSlot(EVAL).toObject());
return true;
@@ -573,7 +573,7 @@ GlobalObject::warnOnceAbout(JSContext* cx, HandleObject obj, WarnOnceFlag flag,
return true;
}
-JSFunction*
+/* static */ JSFunction*
GlobalObject::createConstructor(JSContext* cx, Native ctor, JSAtom* nameArg, unsigned length,
gc::AllocKind kind, const JSJitInfo* jitInfo)
{
@@ -595,28 +595,27 @@ CreateBlankProto(JSContext* cx, const Class* clasp, HandleObject proto, HandleOb
RootedNativeObject blankProto(cx, NewNativeObjectWithGivenProto(cx, clasp, proto,
SingletonObject));
- if (!blankProto || !blankProto->setDelegate(cx))
+ if (!blankProto || !JSObject::setDelegate(cx, blankProto))
return nullptr;
return blankProto;
}
-NativeObject*
-GlobalObject::createBlankPrototype(JSContext* cx, const Class* clasp)
+/* static */ NativeObject*
+GlobalObject::createBlankPrototype(JSContext* cx, Handle<GlobalObject*> global, const Class* clasp)
{
- Rooted<GlobalObject*> self(cx, this);
- RootedObject objectProto(cx, getOrCreateObjectPrototype(cx));
+ RootedObject objectProto(cx, getOrCreateObjectPrototype(cx, global));
if (!objectProto)
return nullptr;
- return CreateBlankProto(cx, clasp, objectProto, self);
+ return CreateBlankProto(cx, clasp, objectProto, global);
}
-NativeObject*
-GlobalObject::createBlankPrototypeInheriting(JSContext* cx, const Class* clasp, HandleObject proto)
+/* static */ NativeObject*
+GlobalObject::createBlankPrototypeInheriting(JSContext* cx, Handle<GlobalObject*> global,
+ const Class* clasp, HandleObject proto)
{
- Rooted<GlobalObject*> self(cx, this);
- return CreateBlankProto(cx, clasp, proto, self);
+ return CreateBlankProto(cx, clasp, proto, global);
}
bool
@@ -729,21 +728,20 @@ GlobalObject::hasRegExpStatics() const
return !getSlot(REGEXP_STATICS).isUndefined();
}
-RegExpStatics*
-GlobalObject::getRegExpStatics(ExclusiveContext* cx) const
+/* static */ RegExpStatics*
+GlobalObject::getRegExpStatics(ExclusiveContext* cx, Handle<GlobalObject*> global)
{
MOZ_ASSERT(cx);
- Rooted<GlobalObject*> self(cx, const_cast<GlobalObject*>(this));
RegExpStaticsObject* resObj = nullptr;
- const Value& val = this->getSlot(REGEXP_STATICS);
+ const Value& val = global->getSlot(REGEXP_STATICS);
if (!val.isObject()) {
MOZ_ASSERT(val.isUndefined());
- resObj = RegExpStatics::create(cx, self);
+ resObj = RegExpStatics::create(cx, global);
if (!resObj)
return nullptr;
- self->initSlot(REGEXP_STATICS, ObjectValue(*resObj));
+ global->initSlot(REGEXP_STATICS, ObjectValue(*resObj));
} else {
resObj = &val.toObject().as<RegExpStaticsObject>();
}
@@ -866,7 +864,7 @@ GlobalObject::addIntrinsicValue(JSContext* cx, Handle<GlobalObject*> global,
/* static */ bool
GlobalObject::ensureModulePrototypesCreated(JSContext *cx, Handle<GlobalObject*> global)
{
- return global->getOrCreateObject(cx, MODULE_PROTO, initModuleProto) &&
- global->getOrCreateObject(cx, IMPORT_ENTRY_PROTO, initImportEntryProto) &&
- global->getOrCreateObject(cx, EXPORT_ENTRY_PROTO, initExportEntryProto);
+ return getOrCreateObject(cx, global, MODULE_PROTO, initModuleProto) &&
+ getOrCreateObject(cx, global, IMPORT_ENTRY_PROTO, initImportEntryProto) &&
+ getOrCreateObject(cx, global, EXPORT_ENTRY_PROTO, initExportEntryProto);
}
diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h
index 3534ef2f6..5aacfc5dc 100644
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -290,8 +290,8 @@ class GlobalObject : public NativeObject
* Create a constructor function with the specified name and length using
* ctor, a method which creates objects with the given class.
*/
- JSFunction*
- createConstructor(JSContext* cx, JSNative ctor, JSAtom* name, unsigned length,
+ static JSFunction*
+ createConstructor(JSContext* cx, JSNative ctor, JSAtom* name, unsigned length,
gc::AllocKind kind = gc::AllocKind::FUNCTION,
const JSJitInfo* jitInfo = nullptr);
@@ -303,48 +303,44 @@ class GlobalObject : public NativeObject
* complete the minimal initialization to make the returned object safe to
* touch.
*/
- NativeObject* createBlankPrototype(JSContext* cx, const js::Class* clasp);
+ static NativeObject*
+ createBlankPrototype(JSContext* cx, Handle<GlobalObject*> global, const js::Class* clasp);
/*
* Identical to createBlankPrototype, but uses proto as the [[Prototype]]
* of the returned blank prototype.
*/
- NativeObject* createBlankPrototypeInheriting(JSContext* cx, const js::Class* clasp,
- HandleObject proto);
+ static NativeObject*
+ createBlankPrototypeInheriting(JSContext* cx, Handle<GlobalObject*> global,
+ const js::Class* clasp, HandleObject proto);
template <typename T>
- T* createBlankPrototype(JSContext* cx) {
- NativeObject* res = createBlankPrototype(cx, &T::class_);
+ static T*
+ createBlankPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ NativeObject* res = createBlankPrototype(cx, global, &T::class_);
return res ? &res->template as<T>() : nullptr;
}
- NativeObject* getOrCreateObjectPrototype(JSContext* cx) {
- if (functionObjectClassesInitialized())
- return &getPrototype(JSProto_Object).toObject().as<NativeObject>();
- RootedGlobalObject self(cx, this);
- if (!ensureConstructor(cx, self, JSProto_Object))
+ static NativeObject*
+ getOrCreateObjectPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ if (global->functionObjectClassesInitialized())
+ return &global->getPrototype(JSProto_Object).toObject().as<NativeObject>();
+ if (!ensureConstructor(cx, global, JSProto_Object))
return nullptr;
- return &self->getPrototype(JSProto_Object).toObject().as<NativeObject>();
- }
-
- static NativeObject* getOrCreateObjectPrototype(JSContext* cx, Handle<GlobalObject*> global) {
- return global->getOrCreateObjectPrototype(cx);
+ return &global->getPrototype(JSProto_Object).toObject().as<NativeObject>();
}
- NativeObject* getOrCreateFunctionPrototype(JSContext* cx) {
- if (functionObjectClassesInitialized())
- return &getPrototype(JSProto_Function).toObject().as<NativeObject>();
- RootedGlobalObject self(cx, this);
- if (!ensureConstructor(cx, self, JSProto_Object))
+ static NativeObject*
+ getOrCreateFunctionPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ if (global->functionObjectClassesInitialized())
+ return &global->getPrototype(JSProto_Function).toObject().as<NativeObject>();
+ if (!ensureConstructor(cx, global, JSProto_Object))
return nullptr;
- return &self->getPrototype(JSProto_Function).toObject().as<NativeObject>();
- }
-
- static NativeObject* getOrCreateFunctionPrototype(JSContext* cx, Handle<GlobalObject*> global) {
- return global->getOrCreateFunctionPrototype(cx);
+ return &global->getPrototype(JSProto_Function).toObject().as<NativeObject>();
}
- static NativeObject* getOrCreateArrayPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ static NativeObject*
+ getOrCreateArrayPrototype(JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_Array))
return nullptr;
return &global->getPrototype(JSProto_Array).toObject().as<NativeObject>();
@@ -356,37 +352,43 @@ class GlobalObject : public NativeObject
return nullptr;
}
- static NativeObject* getOrCreateBooleanPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ static NativeObject*
+ getOrCreateBooleanPrototype(JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_Boolean))
return nullptr;
return &global->getPrototype(JSProto_Boolean).toObject().as<NativeObject>();
}
- static NativeObject* getOrCreateNumberPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ static NativeObject*
+ getOrCreateNumberPrototype(JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_Number))
return nullptr;
return &global->getPrototype(JSProto_Number).toObject().as<NativeObject>();
}
- static NativeObject* getOrCreateStringPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ static NativeObject*
+ getOrCreateStringPrototype(JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_String))
return nullptr;
return &global->getPrototype(JSProto_String).toObject().as<NativeObject>();
}
- static NativeObject* getOrCreateSymbolPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ static NativeObject*
+ getOrCreateSymbolPrototype(JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_Symbol))
return nullptr;
return &global->getPrototype(JSProto_Symbol).toObject().as<NativeObject>();
}
- static NativeObject* getOrCreatePromisePrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ static NativeObject*
+ getOrCreatePromisePrototype(JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_Promise))
return nullptr;
return &global->getPrototype(JSProto_Promise).toObject().as<NativeObject>();
}
- static NativeObject* getOrCreateRegExpPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ static NativeObject*
+ getOrCreateRegExpPrototype(JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_RegExp))
return nullptr;
return &global->getPrototype(JSProto_RegExp).toObject().as<NativeObject>();
@@ -398,28 +400,30 @@ class GlobalObject : public NativeObject
return nullptr;
}
- static NativeObject* getOrCreateSavedFramePrototype(JSContext* cx,
- Handle<GlobalObject*> global) {
+ static NativeObject*
+ getOrCreateSavedFramePrototype(JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_SavedFrame))
return nullptr;
return &global->getPrototype(JSProto_SavedFrame).toObject().as<NativeObject>();
}
- static JSObject* getOrCreateArrayBufferPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ static JSObject*
+ getOrCreateArrayBufferPrototype(JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_ArrayBuffer))
return nullptr;
return &global->getPrototype(JSProto_ArrayBuffer).toObject();
}
- JSObject* getOrCreateSharedArrayBufferPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ static JSObject*
+ getOrCreateSharedArrayBufferPrototype(JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_SharedArrayBuffer))
return nullptr;
return &global->getPrototype(JSProto_SharedArrayBuffer).toObject();
}
- static JSObject* getOrCreateCustomErrorPrototype(JSContext* cx,
- Handle<GlobalObject*> global,
- JSExnType exnType)
+ static JSObject*
+ getOrCreateCustomErrorPrototype(JSContext* cx, Handle<GlobalObject*> global,
+ JSExnType exnType)
{
JSProtoKey key = GetExceptionProtoKey(exnType);
if (!ensureConstructor(cx, global, key))
@@ -439,35 +443,41 @@ class GlobalObject : public NativeObject
return getOrCreateCustomErrorPrototype(cx, global, JSEXN_ERR);
}
- static NativeObject* getOrCreateSetPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ static NativeObject*
+ getOrCreateSetPrototype(JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_Set))
return nullptr;
return &global->getPrototype(JSProto_Set).toObject().as<NativeObject>();
}
- static NativeObject* getOrCreateWeakSetPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ static NativeObject*
+ getOrCreateWeakSetPrototype(JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_WeakSet))
return nullptr;
return &global->getPrototype(JSProto_WeakSet).toObject().as<NativeObject>();
}
- JSObject* getOrCreateIntlObject(JSContext* cx) {
- return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_Intl, initIntlObject);
+ static JSObject*
+ getOrCreateIntlObject(JSContext* cx, Handle<GlobalObject*> global) {
+ return getOrCreateObject(cx, global, APPLICATION_SLOTS + JSProto_Intl, initIntlObject);
}
- JSObject* getOrCreateTypedObjectModule(JSContext* cx) {
- return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_TypedObject, initTypedObjectModule);
+ static JSObject*
+ getOrCreateTypedObjectModule(JSContext* cx, Handle<GlobalObject*> global) {
+ return getOrCreateObject(cx, global, APPLICATION_SLOTS + JSProto_TypedObject,
+ initTypedObjectModule);
}
- JSObject* getOrCreateSimdGlobalObject(JSContext* cx) {
- return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_SIMD, initSimdObject);
+ static JSObject*
+ getOrCreateSimdGlobalObject(JSContext* cx, Handle<GlobalObject*> global) {
+ return getOrCreateObject(cx, global, APPLICATION_SLOTS + JSProto_SIMD, initSimdObject);
}
// Get the type descriptor for one of the SIMD types.
// simdType is one of the JS_SIMDTYPEREPR_* constants.
// Implemented in builtin/SIMD.cpp.
- static SimdTypeDescr* getOrCreateSimdTypeDescr(JSContext* cx, Handle<GlobalObject*> global,
- SimdType simdType);
+ static SimdTypeDescr*
+ getOrCreateSimdTypeDescr(JSContext* cx, Handle<GlobalObject*> global, SimdType simdType);
TypedObjectModuleObject& getTypedObjectModule() const;
@@ -475,16 +485,19 @@ class GlobalObject : public NativeObject
return &getPrototype(JSProto_Iterator).toObject();
}
- JSObject* getOrCreateCollatorPrototype(JSContext* cx) {
- return getOrCreateObject(cx, COLLATOR_PROTO, initIntlObject);
+ static JSObject*
+ getOrCreateCollatorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ return getOrCreateObject(cx, global, COLLATOR_PROTO, initIntlObject);
}
- JSObject* getOrCreateNumberFormatPrototype(JSContext* cx) {
- return getOrCreateObject(cx, NUMBER_FORMAT_PROTO, initIntlObject);
+ static JSObject*
+ getOrCreateNumberFormatPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ return getOrCreateObject(cx, global, NUMBER_FORMAT_PROTO, initIntlObject);
}
- JSObject* getOrCreateDateTimeFormatPrototype(JSContext* cx) {
- return getOrCreateObject(cx, DATE_TIME_FORMAT_PROTO, initIntlObject);
+ static JSObject*
+ getOrCreateDateTimeFormatPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ return getOrCreateObject(cx, global, DATE_TIME_FORMAT_PROTO, initIntlObject);
}
static bool ensureModulePrototypesCreated(JSContext *cx, Handle<GlobalObject*> global);
@@ -539,88 +552,86 @@ class GlobalObject : public NativeObject
private:
typedef bool (*ObjectInitOp)(JSContext* cx, Handle<GlobalObject*> global);
- JSObject* getOrCreateObject(JSContext* cx, unsigned slot, ObjectInitOp init) {
- Value v = getSlotRef(slot);
+ static JSObject*
+ getOrCreateObject(JSContext* cx, Handle<GlobalObject*> global, unsigned slot,
+ ObjectInitOp init)
+ {
+ Value v = global->getSlotRef(slot);
if (v.isObject())
return &v.toObject();
- RootedGlobalObject self(cx, this);
- if (!init(cx, self))
+ if (!init(cx, global))
return nullptr;
- return &self->getSlot(slot).toObject();
+ return &global->getSlot(slot).toObject();
}
public:
- static NativeObject* getOrCreateIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global)
- {
- return MaybeNativeObject(global->getOrCreateObject(cx, ITERATOR_PROTO, initIteratorProto));
+ static NativeObject*
+ getOrCreateIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ return MaybeNativeObject(getOrCreateObject(cx, global, ITERATOR_PROTO, initIteratorProto));
}
- static NativeObject* getOrCreateArrayIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global)
- {
- return MaybeNativeObject(global->getOrCreateObject(cx, ARRAY_ITERATOR_PROTO, initArrayIteratorProto));
+ static NativeObject*
+ getOrCreateArrayIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ return MaybeNativeObject(getOrCreateObject(cx, global, ARRAY_ITERATOR_PROTO,
+ initArrayIteratorProto));
}
- static NativeObject* getOrCreateStringIteratorPrototype(JSContext* cx,
- Handle<GlobalObject*> global)
- {
- return MaybeNativeObject(global->getOrCreateObject(cx, STRING_ITERATOR_PROTO, initStringIteratorProto));
+ static NativeObject*
+ getOrCreateStringIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ return MaybeNativeObject(getOrCreateObject(cx, global, STRING_ITERATOR_PROTO,
+ initStringIteratorProto));
}
- static NativeObject* getOrCreateLegacyGeneratorObjectPrototype(JSContext* cx,
- Handle<GlobalObject*> global)
- {
- return MaybeNativeObject(global->getOrCreateObject(cx, LEGACY_GENERATOR_OBJECT_PROTO,
- initLegacyGeneratorProto));
+ static NativeObject*
+ getOrCreateLegacyGeneratorObjectPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ return MaybeNativeObject(getOrCreateObject(cx, global, LEGACY_GENERATOR_OBJECT_PROTO,
+ initLegacyGeneratorProto));
}
- static NativeObject* getOrCreateStarGeneratorObjectPrototype(JSContext* cx,
- Handle<GlobalObject*> global)
+ static NativeObject*
+ getOrCreateStarGeneratorObjectPrototype(JSContext* cx, Handle<GlobalObject*> global)
{
- return MaybeNativeObject(global->getOrCreateObject(cx, STAR_GENERATOR_OBJECT_PROTO, initStarGenerators));
+ return MaybeNativeObject(getOrCreateObject(cx, global, STAR_GENERATOR_OBJECT_PROTO,
+ initStarGenerators));
}
- static NativeObject* getOrCreateStarGeneratorFunctionPrototype(JSContext* cx,
- Handle<GlobalObject*> global)
- {
- return MaybeNativeObject(global->getOrCreateObject(cx, STAR_GENERATOR_FUNCTION_PROTO, initStarGenerators));
+ static NativeObject*
+ getOrCreateStarGeneratorFunctionPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ return MaybeNativeObject(getOrCreateObject(cx, global, STAR_GENERATOR_FUNCTION_PROTO,
+ initStarGenerators));
}
- static JSObject* getOrCreateStarGeneratorFunction(JSContext* cx,
- Handle<GlobalObject*> global)
- {
- return global->getOrCreateObject(cx, STAR_GENERATOR_FUNCTION, initStarGenerators);
+ static JSObject*
+ getOrCreateStarGeneratorFunction(JSContext* cx, Handle<GlobalObject*> global) {
+ return getOrCreateObject(cx, global, STAR_GENERATOR_FUNCTION, initStarGenerators);
}
- static NativeObject* getOrCreateAsyncFunctionPrototype(JSContext* cx,
- Handle<GlobalObject*> global)
- {
- return MaybeNativeObject(global->getOrCreateObject(cx, ASYNC_FUNCTION_PROTO,
- initAsyncFunction));
+ static NativeObject*
+ getOrCreateAsyncFunctionPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_FUNCTION_PROTO,
+ initAsyncFunction));
}
- static JSObject* getOrCreateAsyncFunction(JSContext* cx,
- Handle<GlobalObject*> global)
- {
- return global->getOrCreateObject(cx, ASYNC_FUNCTION, initAsyncFunction);
+ static JSObject*
+ getOrCreateAsyncFunction(JSContext* cx, Handle<GlobalObject*> global) {
+ return getOrCreateObject(cx, global, ASYNC_FUNCTION, initAsyncFunction);
}
- static JSObject* getOrCreateMapIteratorPrototype(JSContext* cx,
- Handle<GlobalObject*> global)
- {
- return global->getOrCreateObject(cx, MAP_ITERATOR_PROTO, initMapIteratorProto);
+ static JSObject*
+ getOrCreateMapIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ return getOrCreateObject(cx, global, MAP_ITERATOR_PROTO, initMapIteratorProto);
}
- static JSObject* getOrCreateSetIteratorPrototype(JSContext* cx,
- Handle<GlobalObject*> global)
- {
- return global->getOrCreateObject(cx, SET_ITERATOR_PROTO, initSetIteratorProto);
+ static JSObject*
+ getOrCreateSetIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ return getOrCreateObject(cx, global, SET_ITERATOR_PROTO, initSetIteratorProto);
}
- JSObject* getOrCreateDataViewPrototype(JSContext* cx) {
- RootedGlobalObject self(cx, this);
- if (!ensureConstructor(cx, self, JSProto_DataView))
+ static JSObject*
+ getOrCreateDataViewPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ if (!ensureConstructor(cx, global, JSProto_DataView))
return nullptr;
- return &self->getPrototype(JSProto_DataView).toObject();
+ return &global->getPrototype(JSProto_DataView).toObject();
}
static JSFunction*
@@ -678,8 +689,9 @@ class GlobalObject : public NativeObject
return true;
}
- static bool getIntrinsicValue(JSContext* cx, Handle<GlobalObject*> global,
- HandlePropertyName name, MutableHandleValue value)
+ static bool
+ getIntrinsicValue(JSContext* cx, Handle<GlobalObject*> global,
+ HandlePropertyName name, MutableHandleValue value)
{
bool exists = false;
if (!GlobalObject::maybeGetIntrinsicValue(cx, global, name, value, &exists))
@@ -709,7 +721,8 @@ class GlobalObject : public NativeObject
unsigned nargs, MutableHandleValue funVal);
bool hasRegExpStatics() const;
- RegExpStatics* getRegExpStatics(ExclusiveContext* cx) const;
+ static RegExpStatics* getRegExpStatics(ExclusiveContext* cx,
+ Handle<GlobalObject*> global);
RegExpStatics* getAlreadyCreatedRegExpStatics() const;
JSObject* getThrowTypeError() const {
@@ -996,7 +1009,7 @@ GenericCreateConstructor(JSContext* cx, JSProtoKey key)
// Note - We duplicate the trick from ClassName() so that we don't need to
// include jsatominlines.h here.
PropertyName* name = (&cx->names().Null)[key];
- return cx->global()->createConstructor(cx, ctor, name, length, kind, jitInfo);
+ return GlobalObject::createConstructor(cx, ctor, name, length, kind, jitInfo);
}
inline JSObject*
@@ -1009,7 +1022,7 @@ GenericCreatePrototype(JSContext* cx, JSProtoKey key)
if (!GlobalObject::ensureConstructor(cx, cx->global(), protoKey))
return nullptr;
RootedObject parentProto(cx, &cx->global()->getPrototype(protoKey).toObject());
- return cx->global()->createBlankPrototypeInheriting(cx, clasp, parentProto);
+ return GlobalObject::createBlankPrototypeInheriting(cx, cx->global(), clasp, parentProto);
}
inline JSProtoKey
diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp
index bd29d0c79..44915521f 100644
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -1291,7 +1291,7 @@ GlobalHelperThreadState::finishModuleParseTask(JSContext* cx, void* token)
MOZ_ASSERT(script->module());
RootedModuleObject module(cx, script->module());
- module->fixEnvironmentsAfterCompartmentMerge(cx);
+ module->fixEnvironmentsAfterCompartmentMerge();
if (!ModuleObject::Freeze(cx, module))
return nullptr;
diff --git a/js/src/vm/Interpreter-inl.h b/js/src/vm/Interpreter-inl.h
index 710f1d89b..acfa8f74b 100644
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -593,7 +593,7 @@ InitArrayElemOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, uint32_t
JSOp op = JSOp(*pc);
MOZ_ASSERT(op == JSOP_INITELEM_ARRAY || op == JSOP_INITELEM_INC);
- MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
+ MOZ_ASSERT(obj->is<ArrayObject>());
if (op == JSOP_INITELEM_INC && index == INT32_MAX) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SPREAD_TOO_LARGE);
@@ -830,7 +830,7 @@ class FastCallGuard
if (useIon_ && fun_) {
if (!script_) {
- script_ = fun_->getOrCreateScript(cx);
+ script_ = JSFunction::getOrCreateScript(cx, fun_);
if (!script_)
return false;
}
diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp
index 3cf2deb83..030f0f3b6 100644
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -261,11 +261,16 @@ SetPropertyOperation(JSContext* cx, JSOp op, HandleValue lval, HandleId id, Hand
}
static JSFunction*
-MakeDefaultConstructor(JSContext* cx, JSOp op, JSAtom* atom, HandleObject proto)
+MakeDefaultConstructor(JSContext* cx, HandleScript script, jsbytecode* pc, HandleObject proto)
{
+ JSOp op = JSOp(*pc);
+ JSAtom* atom = script->getAtom(pc);
bool derived = op == JSOP_DERIVEDCONSTRUCTOR;
MOZ_ASSERT(derived == !!proto);
+ jssrcnote* classNote = GetSrcNote(cx, script, pc);
+ MOZ_ASSERT(classNote && SN_TYPE(classNote) == SRC_CLASS_SPAN);
+
PropertyName* lookup = derived ? cx->names().DefaultDerivedClassConstructor
: cx->names().DefaultBaseClassConstructor;
@@ -285,6 +290,17 @@ MakeDefaultConstructor(JSContext* cx, JSOp op, JSAtom* atom, HandleObject proto)
MOZ_ASSERT(ctor->infallibleIsDefaultClassConstructor(cx));
+ // Create the script now, as the source span needs to be overridden for
+ // toString. Calling toString on a class constructor must not return the
+ // source for just the constructor function.
+ JSScript *ctorScript = JSFunction::getOrCreateScript(cx, ctor);
+ if (!ctorScript)
+ return nullptr;
+ uint32_t classStartOffset = GetSrcNoteOffset(classNote, 0);
+ uint32_t classEndOffset = GetSrcNoteOffset(classNote, 1);
+ ctorScript->setDefaultClassConstructorSpan(script->sourceObject(), classStartOffset,
+ classEndOffset);
+
return ctor;
}
@@ -373,7 +389,7 @@ js::RunScript(JSContext* cx, RunState& state)
SPSEntryMarker marker(cx->runtime(), state.script());
- state.script()->ensureNonLazyCanonicalFunction(cx);
+ state.script()->ensureNonLazyCanonicalFunction();
if (jit::IsIonEnabled(cx)) {
jit::MethodStatus status = jit::CanEnter(cx, state);
@@ -446,7 +462,7 @@ js::InternalCallOrConstruct(JSContext* cx, const CallArgs& args, MaybeConstruct
}
/* Invoke native functions. */
- JSFunction* fun = &args.callee().as<JSFunction>();
+ RootedFunction fun(cx, &args.callee().as<JSFunction>());
if (construct != CONSTRUCT && fun->isClassConstructor()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CALL_CLASS_CONSTRUCTOR);
return false;
@@ -454,10 +470,16 @@ js::InternalCallOrConstruct(JSContext* cx, const CallArgs& args, MaybeConstruct
if (fun->isNative()) {
MOZ_ASSERT_IF(construct, !fun->isConstructor());
- return CallJSNative(cx, fun->native(), args);
+ JSNative native = fun->native();
+ if (!construct && args.ignoresReturnValue()) {
+ const JSJitInfo* jitInfo = fun->jitInfo();
+ if (jitInfo && jitInfo->type() == JSJitInfo::IgnoresReturnValueNative)
+ native = jitInfo->ignoresReturnValueMethod;
+ }
+ return CallJSNative(cx, native, args);
}
- if (!fun->getOrCreateScript(cx))
+ if (!JSFunction::getOrCreateScript(cx, fun))
return false;
/* Run function until JSOP_RETRVAL, JSOP_RETURN or error. */
@@ -1543,7 +1565,7 @@ SetObjectElementOperation(JSContext* cx, HandleObject obj, HandleId id, HandleVa
}
}
- if (obj->isNative() && !JSID_IS_INT(id) && !obj->setHadElementsAccess(cx))
+ if (obj->isNative() && !JSID_IS_INT(id) && !JSObject::setHadElementsAccess(cx, obj))
return false;
ObjectOpResult result;
@@ -1916,6 +1938,7 @@ CASE(EnableInterruptsPseudoOpcode)
/* Various 1-byte no-ops. */
CASE(JSOP_NOP)
CASE(JSOP_NOP_DESTRUCTURING)
+CASE(JSOP_UNUSED126)
CASE(JSOP_UNUSED192)
CASE(JSOP_UNUSED209)
CASE(JSOP_UNUSED210)
@@ -2958,6 +2981,7 @@ CASE(JSOP_FUNAPPLY)
CASE(JSOP_NEW)
CASE(JSOP_CALL)
+CASE(JSOP_CALL_IGNORES_RV)
CASE(JSOP_CALLITER)
CASE(JSOP_SUPERCALL)
CASE(JSOP_FUNCALL)
@@ -2966,10 +2990,11 @@ CASE(JSOP_FUNCALL)
cx->runtime()->spsProfiler.updatePC(script, REGS.pc);
MaybeConstruct construct = MaybeConstruct(*REGS.pc == JSOP_NEW || *REGS.pc == JSOP_SUPERCALL);
+ bool ignoresReturnValue = *REGS.pc == JSOP_CALL_IGNORES_RV;
unsigned argStackSlots = GET_ARGC(REGS.pc) + construct;
MOZ_ASSERT(REGS.stackDepth() >= 2u + GET_ARGC(REGS.pc));
- CallArgs args = CallArgsFromSp(argStackSlots, REGS.sp, construct);
+ CallArgs args = CallArgsFromSp(argStackSlots, REGS.sp, construct, ignoresReturnValue);
JSFunction* maybeFun;
bool isFunction = IsFunctionObject(args.calleev(), &maybeFun);
@@ -2999,7 +3024,7 @@ CASE(JSOP_FUNCALL)
{
MOZ_ASSERT(maybeFun);
ReservedRooted<JSFunction*> fun(&rootFunction0, maybeFun);
- ReservedRooted<JSScript*> funScript(&rootScript0, fun->getOrCreateScript(cx));
+ ReservedRooted<JSScript*> funScript(&rootScript0, JSFunction::getOrCreateScript(cx, fun));
if (!funScript)
goto error;
@@ -3636,7 +3661,6 @@ CASE(JSOP_NEWINIT)
END_CASE(JSOP_NEWINIT)
CASE(JSOP_NEWARRAY)
-CASE(JSOP_SPREADCALLARRAY)
{
uint32_t length = GET_UINT32(REGS.pc);
JSObject* obj = NewArrayOperation(cx, script, REGS.pc, length);
@@ -4174,8 +4198,8 @@ CASE(JSOP_DERIVEDCONSTRUCTOR)
MOZ_ASSERT(REGS.sp[-1].isObject());
ReservedRooted<JSObject*> proto(&rootObject0, &REGS.sp[-1].toObject());
- JSFunction* constructor = MakeDefaultConstructor(cx, JSOp(*REGS.pc), script->getAtom(REGS.pc),
- proto);
+ JSFunction* constructor = MakeDefaultConstructor(cx, script, REGS.pc, proto);
+
if (!constructor)
goto error;
@@ -4185,8 +4209,7 @@ END_CASE(JSOP_DERIVEDCONSTRUCTOR)
CASE(JSOP_CLASSCONSTRUCTOR)
{
- JSFunction* constructor = MakeDefaultConstructor(cx, JSOp(*REGS.pc), script->getAtom(REGS.pc),
- nullptr);
+ JSFunction* constructor = MakeDefaultConstructor(cx, script, REGS.pc, nullptr);
if (!constructor)
goto error;
PUSH_OBJECT(*constructor);
@@ -4725,7 +4748,8 @@ js::RunOnceScriptPrologue(JSContext* cx, HandleScript script)
// Force instantiation of the script's function's group to ensure the flag
// is preserved in type information.
- if (!script->functionNonDelazifying()->getGroup(cx))
+ RootedFunction fun(cx, script->functionNonDelazifying());
+ if (!JSObject::getGroup(cx, fun))
return false;
MarkObjectGroupFlags(cx, script->functionNonDelazifying(), OBJECT_FLAG_RUNONCE_INVALIDATED);
@@ -4933,7 +4957,7 @@ js::NewObjectOperation(JSContext* cx, HandleScript script, jsbytecode* pc,
newKind = TenuredObject;
}
- RootedObject obj(cx);
+ RootedPlainObject obj(cx);
if (*pc == JSOP_NEWOBJECT) {
RootedPlainObject baseObject(cx, &script->getObject(pc)->as<PlainObject>());
@@ -4970,11 +4994,6 @@ js::NewObjectOperationWithTemplate(JSContext* cx, HandleObject templateObject)
NewObjectKind newKind = templateObject->group()->shouldPreTenure() ? TenuredObject : GenericObject;
- if (templateObject->group()->maybeUnboxedLayout()) {
- RootedObjectGroup group(cx, templateObject->group());
- return UnboxedPlainObject::create(cx, group, newKind);
- }
-
JSObject* obj = CopyInitializerObject(cx, templateObject.as<PlainObject>(), newKind);
if (!obj)
return nullptr;
@@ -5001,9 +5020,6 @@ js::NewArrayOperation(JSContext* cx, HandleScript script, jsbytecode* pc, uint32
if (group->shouldPreTenure() || group->maybePreliminaryObjects())
newKind = TenuredObject;
-
- if (group->maybeUnboxedLayout())
- return UnboxedArrayObject::create(cx, group, length, newKind);
}
ArrayObject* obj = NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
@@ -5014,9 +5030,6 @@ js::NewArrayOperation(JSContext* cx, HandleScript script, jsbytecode* pc, uint32
MOZ_ASSERT(obj->isSingleton());
} else {
obj->setGroup(group);
-
- if (PreliminaryObjectArray* preliminaryObjects = group->maybePreliminaryObjects())
- preliminaryObjects->registerNewObject(obj);
}
return obj;
@@ -5029,12 +5042,6 @@ js::NewArrayOperationWithTemplate(JSContext* cx, HandleObject templateObject)
NewObjectKind newKind = templateObject->group()->shouldPreTenure() ? TenuredObject : GenericObject;
- if (templateObject->is<UnboxedArrayObject>()) {
- uint32_t length = templateObject->as<UnboxedArrayObject>().length();
- RootedObjectGroup group(cx, templateObject->group());
- return UnboxedArrayObject::create(cx, group, length, newKind);
- }
-
ArrayObject* obj = NewDenseFullyAllocatedArray(cx, templateObject->as<ArrayObject>().length(),
nullptr, newKind);
if (!obj)
diff --git a/js/src/vm/JSONParser.cpp b/js/src/vm/JSONParser.cpp
index 01883bb15..e50da3bc4 100644
--- a/js/src/vm/JSONParser.cpp
+++ b/js/src/vm/JSONParser.cpp
@@ -606,8 +606,8 @@ JSONParserBase::finishArray(MutableHandleValue vp, ElementVector& elements)
{
MOZ_ASSERT(&elements == &stack.back().elements());
- JSObject* obj = ObjectGroup::newArrayObject(cx, elements.begin(), elements.length(),
- GenericObject);
+ ArrayObject* obj = ObjectGroup::newArrayObject(cx, elements.begin(), elements.length(),
+ GenericObject);
if (!obj)
return false;
diff --git a/js/src/vm/NativeObject-inl.h b/js/src/vm/NativeObject-inl.h
index 48a42a8db..e55e3db04 100644
--- a/js/src/vm/NativeObject-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -235,6 +235,38 @@ NativeObject::ensureDenseElements(ExclusiveContext* cx, uint32_t index, uint32_t
return DenseElementResult::Success;
}
+inline DenseElementResult
+NativeObject::setOrExtendDenseElements(ExclusiveContext* cx, uint32_t start, const Value* vp,
+ uint32_t count,
+ ShouldUpdateTypes updateTypes)
+{
+ if (denseElementsAreFrozen())
+ return DenseElementResult::Incomplete;
+
+ if (is<ArrayObject>() &&
+ !as<ArrayObject>().lengthIsWritable() &&
+ start + count >= as<ArrayObject>().length())
+ {
+ return DenseElementResult::Incomplete;
+ }
+
+ DenseElementResult result = ensureDenseElements(cx, start, count);
+ if (result != DenseElementResult::Success)
+ return result;
+
+ if (is<ArrayObject>() && start + count >= as<ArrayObject>().length())
+ as<ArrayObject>().setLengthInt32(start + count);
+
+ if (updateTypes == ShouldUpdateTypes::DontUpdate && !shouldConvertDoubleElements()) {
+ copyDenseElements(start, vp, count);
+ } else {
+ for (size_t i = 0; i < count; i++)
+ setDenseElementWithType(cx, start + i, vp[i]);
+ }
+
+ return DenseElementResult::Success;
+}
+
inline Value
NativeObject::getDenseOrTypedArrayElement(uint32_t idx)
{
diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp
index 21f73f4a9..da0f59fe2 100644
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -390,33 +390,6 @@ NativeObject::setLastPropertyMakeNonNative(Shape* shape)
shape_ = shape;
}
-void
-NativeObject::setLastPropertyMakeNative(ExclusiveContext* cx, Shape* shape)
-{
- MOZ_ASSERT(getClass()->isNative());
- MOZ_ASSERT(shape->getObjectClass()->isNative());
- MOZ_ASSERT(!shape->inDictionary());
-
- // This method is used to convert unboxed objects into native objects. In
- // this case, the shape_ field was previously used to store other data and
- // this should be treated as an initialization.
- shape_.init(shape);
-
- slots_ = nullptr;
- elements_ = emptyObjectElements;
-
- size_t oldSpan = shape->numFixedSlots();
- size_t newSpan = shape->slotSpan();
-
- initializeSlotRange(0, oldSpan);
-
- // A failure at this point will leave the object as a mutant, and we
- // can't recover.
- AutoEnterOOMUnsafeRegion oomUnsafe;
- if (oldSpan != newSpan && !updateSlotsForSpan(cx, oldSpan, newSpan))
- oomUnsafe.crash("NativeObject::setLastPropertyMakeNative");
-}
-
bool
NativeObject::setSlotSpan(ExclusiveContext* cx, uint32_t span)
{
@@ -699,10 +672,10 @@ NativeObject::maybeDensifySparseElements(js::ExclusiveContext* cx, HandleNativeO
*/
if (shape != obj->lastProperty()) {
shape = shape->previous();
- if (!obj->removeProperty(cx, id))
+ if (!NativeObject::removeProperty(cx, obj, id))
return DenseElementResult::Failure;
} else {
- if (!obj->removeProperty(cx, id))
+ if (!NativeObject::removeProperty(cx, obj, id))
return DenseElementResult::Failure;
shape = obj->lastProperty();
}
@@ -718,7 +691,7 @@ NativeObject::maybeDensifySparseElements(js::ExclusiveContext* cx, HandleNativeO
* flag so that we will not start using sparse indexes again if we need
* to grow the object.
*/
- if (!obj->clearFlag(cx, BaseShape::INDEXED))
+ if (!NativeObject::clearFlag(cx, obj, BaseShape::INDEXED))
return DenseElementResult::Failure;
return DenseElementResult::Success;
@@ -1023,23 +996,22 @@ NativeObject::freeSlot(ExclusiveContext* cx, uint32_t slot)
setSlot(slot, UndefinedValue());
}
-Shape*
-NativeObject::addDataProperty(ExclusiveContext* cx, jsid idArg, uint32_t slot, unsigned attrs)
+/* static */ Shape*
+NativeObject::addDataProperty(ExclusiveContext* cx, HandleNativeObject obj,
+ jsid idArg, uint32_t slot, unsigned attrs)
{
MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
- RootedNativeObject self(cx, this);
RootedId id(cx, idArg);
- return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0);
+ return addProperty(cx, obj, id, nullptr, nullptr, slot, attrs, 0);
}
-Shape*
-NativeObject::addDataProperty(ExclusiveContext* cx, HandlePropertyName name,
- uint32_t slot, unsigned attrs)
+/* static */ Shape*
+NativeObject::addDataProperty(ExclusiveContext* cx, HandleNativeObject obj,
+ HandlePropertyName name, uint32_t slot, unsigned attrs)
{
MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
- RootedNativeObject self(cx, this);
RootedId id(cx, NameToId(name));
- return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0);
+ return addProperty(cx, obj, id, nullptr, nullptr, slot, attrs, 0);
}
template <AllowGC allowGC>
@@ -1073,7 +1045,7 @@ CallAddPropertyHook(ExclusiveContext* cx, HandleNativeObject obj, HandleShape sh
RootedId id(cx, shape->propid());
if (!CallJSAddPropertyOp(cx->asJSContext(), addProperty, obj, id, value)) {
- obj->removeProperty(cx, shape->propid());
+ NativeObject::removeProperty(cx, obj, shape->propid());
return false;
}
}
@@ -1145,7 +1117,7 @@ PurgeProtoChain(ExclusiveContext* cx, JSObject* objArg, HandleId id)
shape = obj->as<NativeObject>().lookup(cx, id);
if (shape)
- return obj->as<NativeObject>().shadowingShapeChange(cx, *shape);
+ return NativeObject::shadowingShapeChange(cx, obj.as<NativeObject>(), *shape);
obj = obj->staticPrototype();
}
@@ -2556,7 +2528,7 @@ js::NativeDeleteProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
obj->setDenseElementHole(cx, JSID_TO_INT(id));
} else {
- if (!obj->removeProperty(cx, id))
+ if (!NativeObject::removeProperty(cx, obj, id))
return false;
}
diff --git a/js/src/vm/NativeObject.h b/js/src/vm/NativeObject.h
index 4dbc167ab..9cc6d5436 100644
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -339,16 +339,19 @@ IsObjectValueInCompartment(const Value& v, JSCompartment* comp);
#endif
// Operations which change an object's dense elements can either succeed, fail,
-// or be unable to complete. For native objects, the latter is used when the
-// object's elements must become sparse instead. The enum below is used for
-// such operations, and for similar operations on unboxed arrays and methods
-// that work on both kinds of objects.
+// or be unable to complete. The latter is used when the object's elements must
+// become sparse instead. The enum below is used for such operations.
enum class DenseElementResult {
Failure,
Success,
Incomplete
};
+enum class ShouldUpdateTypes {
+ Update,
+ DontUpdate
+};
+
/*
* NativeObject specifies the internal implementation of a native object.
*
@@ -467,11 +470,6 @@ class NativeObject : public ShapedObject
// that are (temporarily) inconsistent.
void setLastPropertyMakeNonNative(Shape* shape);
- // As for setLastProperty(), but changes the class associated with the
- // object to a native one. The object's type has already been changed, and
- // this brings the shape into sync with it.
- void setLastPropertyMakeNative(ExclusiveContext* cx, Shape* shape);
-
// Newly-created TypedArrays that map a SharedArrayBuffer are
// marked as shared by giving them an ObjectElements that has the
// ObjectElements::SHARED_MEMORY flag set.
@@ -493,8 +491,8 @@ class NativeObject : public ShapedObject
void checkShapeConsistency() { }
#endif
- Shape*
- replaceWithNewEquivalentShape(ExclusiveContext* cx,
+ static Shape*
+ replaceWithNewEquivalentShape(ExclusiveContext* cx, HandleNativeObject obj,
Shape* existingShape, Shape* newShape = nullptr,
bool accessorShape = false);
@@ -512,7 +510,7 @@ class NativeObject : public ShapedObject
*/
bool setSlotSpan(ExclusiveContext* cx, uint32_t span);
- bool toDictionaryMode(ExclusiveContext* cx);
+ static MOZ_MUST_USE bool toDictionaryMode(ExclusiveContext* cx, HandleNativeObject obj);
private:
friend class TenuringTracer;
@@ -611,12 +609,15 @@ class NativeObject : public ShapedObject
}
public:
- bool generateOwnShape(ExclusiveContext* cx, Shape* newShape = nullptr) {
- return replaceWithNewEquivalentShape(cx, lastProperty(), newShape);
+ static MOZ_MUST_USE bool generateOwnShape(ExclusiveContext* cx, HandleNativeObject obj,
+ Shape* newShape = nullptr)
+ {
+ return replaceWithNewEquivalentShape(cx, obj, obj->lastProperty(), newShape);
}
- bool shadowingShapeChange(ExclusiveContext* cx, const Shape& shape);
- bool clearFlag(ExclusiveContext* cx, BaseShape::Flag flag);
+ static MOZ_MUST_USE bool shadowingShapeChange(ExclusiveContext* cx, HandleNativeObject obj,
+ const Shape& shape);
+ static bool clearFlag(ExclusiveContext* cx, HandleNativeObject obj, BaseShape::Flag flag);
// The maximum number of slots in an object.
// |MAX_SLOTS_COUNT * sizeof(JS::Value)| shouldn't overflow
@@ -743,10 +744,10 @@ class NativeObject : public ShapedObject
bool allowDictionary = true);
/* Add a data property whose id is not yet in this scope. */
- Shape* addDataProperty(ExclusiveContext* cx,
- jsid id_, uint32_t slot, unsigned attrs);
- Shape* addDataProperty(ExclusiveContext* cx, HandlePropertyName name,
- uint32_t slot, unsigned attrs);
+ static Shape* addDataProperty(ExclusiveContext* cx, HandleNativeObject obj,
+ jsid id_, uint32_t slot, unsigned attrs);
+ static Shape* addDataProperty(ExclusiveContext* cx, HandleNativeObject obj,
+ HandlePropertyName name, uint32_t slot, unsigned attrs);
/* Add or overwrite a property for id in this scope. */
static Shape*
@@ -766,7 +767,7 @@ class NativeObject : public ShapedObject
unsigned attrs, JSGetterOp getter, JSSetterOp setter);
/* Remove the property named by id from this object. */
- bool removeProperty(ExclusiveContext* cx, jsid id);
+ static bool removeProperty(ExclusiveContext* cx, HandleNativeObject obj, jsid id);
/* Clear the scope, making it empty. */
static void clear(ExclusiveContext* cx, HandleNativeObject obj);
@@ -785,7 +786,8 @@ class NativeObject : public ShapedObject
unsigned flags, ShapeTable::Entry* entry, bool allowDictionary,
const AutoKeepShapeTables& keep);
- bool fillInAfterSwap(JSContext* cx, const Vector<Value>& values, void* priv);
+ static MOZ_MUST_USE bool fillInAfterSwap(JSContext* cx, HandleNativeObject obj,
+ const Vector<Value>& values, void* priv);
public:
// Return true if this object has been converted from shared-immutable
@@ -1147,6 +1149,10 @@ class NativeObject : public ShapedObject
elementsRangeWriteBarrierPost(dstStart, count);
}
+ inline DenseElementResult
+ setOrExtendDenseElements(ExclusiveContext* cx, uint32_t start, const Value* vp, uint32_t count,
+ ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update);
+
bool shouldConvertDoubleElements() {
return getElementsHeader()->shouldConvertDoubleElements();
}
@@ -1468,19 +1474,6 @@ NativeGetExistingProperty(JSContext* cx, HandleObject receiver, HandleNativeObje
/* * */
-/*
- * If obj has an already-resolved data property for id, return true and
- * store the property value in *vp.
- */
-extern bool
-HasDataProperty(JSContext* cx, NativeObject* obj, jsid id, Value* vp);
-
-inline bool
-HasDataProperty(JSContext* cx, NativeObject* obj, PropertyName* name, Value* vp)
-{
- return HasDataProperty(cx, obj, NameToId(name), vp);
-}
-
extern bool
GetPropertyForNameLookup(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp);
diff --git a/js/src/vm/ObjectGroup-inl.h b/js/src/vm/ObjectGroup-inl.h
index 9074f4d97..d41343be6 100644
--- a/js/src/vm/ObjectGroup-inl.h
+++ b/js/src/vm/ObjectGroup-inl.h
@@ -108,20 +108,6 @@ ObjectGroup::maybePreliminaryObjects()
return maybePreliminaryObjectsDontCheckGeneration();
}
-inline UnboxedLayout*
-ObjectGroup::maybeUnboxedLayout()
-{
- maybeSweep(nullptr);
- return maybeUnboxedLayoutDontCheckGeneration();
-}
-
-inline UnboxedLayout&
-ObjectGroup::unboxedLayout()
-{
- maybeSweep(nullptr);
- return unboxedLayoutDontCheckGeneration();
-}
-
} // namespace js
#endif /* vm_ObjectGroup_inl_h */
diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp
index 46159a972..ec0a7aec1 100644
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -18,11 +18,10 @@
#include "vm/ArrayObject.h"
#include "vm/Shape.h"
#include "vm/TaggedProto.h"
-#include "vm/UnboxedObject.h"
#include "jsobjinlines.h"
-#include "vm/UnboxedObject-inl.h"
+#include "vm/NativeObject-inl.h"
using namespace js;
@@ -56,7 +55,6 @@ ObjectGroup::finalize(FreeOp* fop)
if (newScriptDontCheckGeneration())
newScriptDontCheckGeneration()->clear();
fop->delete_(newScriptDontCheckGeneration());
- fop->delete_(maybeUnboxedLayoutDontCheckGeneration());
if (maybePreliminaryObjectsDontCheckGeneration())
maybePreliminaryObjectsDontCheckGeneration()->clear();
fop->delete_(maybePreliminaryObjectsDontCheckGeneration());
@@ -83,8 +81,6 @@ ObjectGroup::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
size_t n = 0;
if (TypeNewScript* newScript = newScriptDontCheckGeneration())
n += newScript->sizeOfIncludingThis(mallocSizeOf);
- if (UnboxedLayout* layout = maybeUnboxedLayoutDontCheckGeneration())
- n += layout->sizeOfIncludingThis(mallocSizeOf);
return n;
}
@@ -253,7 +249,7 @@ ObjectGroup::useSingletonForAllocationSite(JSScript* script, jsbytecode* pc, con
/////////////////////////////////////////////////////////////////////
bool
-JSObject::shouldSplicePrototype(JSContext* cx)
+JSObject::shouldSplicePrototype()
{
/*
* During bootstrapping, if inference is enabled we need to make sure not
@@ -266,33 +262,36 @@ JSObject::shouldSplicePrototype(JSContext* cx)
return isSingleton();
}
-bool
-JSObject::splicePrototype(JSContext* cx, const Class* clasp, Handle<TaggedProto> proto)
+/* static */ bool
+JSObject::splicePrototype(JSContext* cx, HandleObject obj, const Class* clasp,
+ Handle<TaggedProto> proto)
{
- MOZ_ASSERT(cx->compartment() == compartment());
-
- RootedObject self(cx, this);
+ MOZ_ASSERT(cx->compartment() == obj->compartment());
/*
* For singleton groups representing only a single JSObject, the proto
* can be rearranged as needed without destroying type information for
* the old or new types.
*/
- MOZ_ASSERT(self->isSingleton());
+ MOZ_ASSERT(obj->isSingleton());
// Windows may not appear on prototype chains.
MOZ_ASSERT_IF(proto.isObject(), !IsWindow(proto.toObject()));
- if (proto.isObject() && !proto.toObject()->setDelegate(cx))
- return false;
+ if (proto.isObject()) {
+ RootedObject protoObj(cx, proto.toObject());
+ if (!JSObject::setDelegate(cx, protoObj))
+ return false;
+ }
// Force type instantiation when splicing lazy group.
- RootedObjectGroup group(cx, self->getGroup(cx));
+ RootedObjectGroup group(cx, JSObject::getGroup(cx, obj));
if (!group)
return false;
RootedObjectGroup protoGroup(cx, nullptr);
if (proto.isObject()) {
- protoGroup = proto.toObject()->getGroup(cx);
+ RootedObject protoObj(cx, proto.toObject());
+ protoGroup = JSObject::getGroup(cx, protoObj);
if (!protoGroup)
return false;
}
@@ -311,7 +310,7 @@ JSObject::makeLazyGroup(JSContext* cx, HandleObject obj)
/* De-lazification of functions can GC, so we need to do it up here. */
if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpretedLazy()) {
RootedFunction fun(cx, &obj->as<JSFunction>());
- if (!fun->getOrCreateScript(cx))
+ if (!JSFunction::getOrCreateScript(cx, fun))
return nullptr;
}
@@ -350,7 +349,7 @@ JSObject::makeLazyGroup(JSContext* cx, HandleObject obj)
JSObject::setNewGroupUnknown(JSContext* cx, const js::Class* clasp, JS::HandleObject obj)
{
ObjectGroup::setDefaultNewGroupUnknown(cx, clasp, obj);
- return obj->setFlags(cx, BaseShape::NEW_GROUP_UNKNOWN);
+ return JSObject::setFlags(cx, obj, BaseShape::NEW_GROUP_UNKNOWN);
}
/////////////////////////////////////////////////////////////////////
@@ -512,7 +511,7 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp,
if (proto.isObject() && !proto.toObject()->isDelegate()) {
RootedObject protoObj(cx, proto.toObject());
- if (!protoObj->setDelegate(cx))
+ if (!JSObject::setDelegate(cx, protoObj))
return nullptr;
// Objects which are prototypes of one another should be singletons, so
@@ -530,8 +529,7 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp,
if (p) {
ObjectGroup* group = p->group;
MOZ_ASSERT_IF(clasp, group->clasp() == clasp);
- MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_ ||
- group->clasp() == &UnboxedPlainObject::class_);
+ MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_);
MOZ_ASSERT(group->proto() == proto);
return group;
}
@@ -774,7 +772,7 @@ GetValueTypeForTable(const Value& v)
return type;
}
-/* static */ JSObject*
+/* static */ ArrayObject*
ObjectGroup::newArrayObject(ExclusiveContext* cx,
const Value* vp, size_t length,
NewObjectKind newKind, NewArrayKind arrayKind)
@@ -838,56 +836,13 @@ ObjectGroup::newArrayObject(ExclusiveContext* cx,
AddTypePropertyId(cx, group, nullptr, JSID_VOID, elementType);
- if (elementType != TypeSet::UnknownType()) {
- // Keep track of the initial objects we create with this type.
- // If the initial ones have a consistent shape and property types, we
- // will try to use an unboxed layout for the group.
- PreliminaryObjectArrayWithTemplate* preliminaryObjects =
- cx->new_<PreliminaryObjectArrayWithTemplate>(nullptr);
- if (!preliminaryObjects)
- return nullptr;
- group->setPreliminaryObjects(preliminaryObjects);
- }
-
if (!p.add(cx, *table, ObjectGroupCompartment::ArrayObjectKey(elementType), group))
return nullptr;
}
// The type of the elements being added will already be reflected in type
- // information, but make sure when creating an unboxed array that the
- // common element type is suitable for the unboxed representation.
+ // information.
ShouldUpdateTypes updateTypes = ShouldUpdateTypes::DontUpdate;
- if (!MaybeAnalyzeBeforeCreatingLargeArray(cx, group, vp, length))
- return nullptr;
- if (group->maybePreliminaryObjects())
- group->maybePreliminaryObjects()->maybeAnalyze(cx, group);
- if (group->maybeUnboxedLayout()) {
- switch (group->unboxedLayout().elementType()) {
- case JSVAL_TYPE_BOOLEAN:
- if (elementType != TypeSet::BooleanType())
- updateTypes = ShouldUpdateTypes::Update;
- break;
- case JSVAL_TYPE_INT32:
- if (elementType != TypeSet::Int32Type())
- updateTypes = ShouldUpdateTypes::Update;
- break;
- case JSVAL_TYPE_DOUBLE:
- if (elementType != TypeSet::Int32Type() && elementType != TypeSet::DoubleType())
- updateTypes = ShouldUpdateTypes::Update;
- break;
- case JSVAL_TYPE_STRING:
- if (elementType != TypeSet::StringType())
- updateTypes = ShouldUpdateTypes::Update;
- break;
- case JSVAL_TYPE_OBJECT:
- if (elementType != TypeSet::NullType() && !elementType.get().isObjectUnchecked())
- updateTypes = ShouldUpdateTypes::Update;
- break;
- default:
- MOZ_CRASH();
- }
- }
-
return NewCopiedArrayTryUseGroup(cx, group, vp, length, newKind, updateTypes);
}
@@ -897,49 +852,15 @@ GiveObjectGroup(ExclusiveContext* cx, JSObject* source, JSObject* target)
{
MOZ_ASSERT(source->group() != target->group());
- if (!target->is<ArrayObject>() && !target->is<UnboxedArrayObject>())
- return true;
-
- if (target->group()->maybePreliminaryObjects()) {
- bool force = IsInsideNursery(source);
- target->group()->maybePreliminaryObjects()->maybeAnalyze(cx, target->group(), force);
- }
-
- if (target->is<ArrayObject>()) {
- ObjectGroup* sourceGroup = source->group();
-
- if (source->is<UnboxedArrayObject>()) {
- Shape* shape = target->as<ArrayObject>().lastProperty();
- if (!UnboxedArrayObject::convertToNativeWithGroup(cx, source, target->group(), shape))
- return false;
- } else if (source->is<ArrayObject>()) {
- source->setGroup(target->group());
- } else {
- return true;
- }
-
- if (sourceGroup->maybePreliminaryObjects())
- sourceGroup->maybePreliminaryObjects()->unregisterObject(source);
- if (target->group()->maybePreliminaryObjects())
- target->group()->maybePreliminaryObjects()->registerNewObject(source);
-
- for (size_t i = 0; i < source->as<ArrayObject>().getDenseInitializedLength(); i++) {
- Value v = source->as<ArrayObject>().getDenseElement(i);
- AddTypePropertyId(cx, source->group(), source, JSID_VOID, v);
- }
-
+ if (!target->is<ArrayObject>() || !source->is<ArrayObject>()) {
return true;
}
- if (target->is<UnboxedArrayObject>()) {
- if (!source->is<UnboxedArrayObject>())
- return true;
- if (source->as<UnboxedArrayObject>().elementType() != JSVAL_TYPE_INT32)
- return true;
- if (target->as<UnboxedArrayObject>().elementType() != JSVAL_TYPE_DOUBLE)
- return true;
+ source->setGroup(target->group());
- return source->as<UnboxedArrayObject>().convertInt32ToDouble(cx, target->group());
+ for (size_t i = 0; i < source->as<ArrayObject>().getDenseInitializedLength(); i++) {
+ Value v = source->as<ArrayObject>().getDenseElement(i);
+ AddTypePropertyId(cx, source->group(), source, JSID_VOID, v);
}
return true;
@@ -1048,46 +969,6 @@ js::CombinePlainObjectPropertyTypes(ExclusiveContext* cx, JSObject* newObj,
}
}
}
- } else if (newObj->is<UnboxedPlainObject>()) {
- const UnboxedLayout& layout = newObj->as<UnboxedPlainObject>().layout();
- const int32_t* traceList = layout.traceList();
- if (!traceList)
- return true;
-
- uint8_t* newData = newObj->as<UnboxedPlainObject>().data();
- uint8_t* oldData = oldObj->as<UnboxedPlainObject>().data();
-
- for (; *traceList != -1; traceList++) {}
- traceList++;
- for (; *traceList != -1; traceList++) {
- JSObject* newInnerObj = *reinterpret_cast<JSObject**>(newData + *traceList);
- JSObject* oldInnerObj = *reinterpret_cast<JSObject**>(oldData + *traceList);
-
- if (!newInnerObj || !oldInnerObj || SameGroup(oldInnerObj, newInnerObj))
- continue;
-
- if (!GiveObjectGroup(cx, newInnerObj, oldInnerObj))
- return false;
-
- if (SameGroup(oldInnerObj, newInnerObj))
- continue;
-
- if (!GiveObjectGroup(cx, oldInnerObj, newInnerObj))
- return false;
-
- if (SameGroup(oldInnerObj, newInnerObj)) {
- for (size_t i = 1; i < ncompare; i++) {
- if (compare[i].isObject() && SameGroup(&compare[i].toObject(), newObj)) {
- uint8_t* otherData = compare[i].toObject().as<UnboxedPlainObject>().data();
- JSObject* otherInnerObj = *reinterpret_cast<JSObject**>(otherData + *traceList);
- if (otherInnerObj && !SameGroup(otherInnerObj, newInnerObj)) {
- if (!GiveObjectGroup(cx, otherInnerObj, newInnerObj))
- return false;
- }
- }
- }
- }
- }
}
return true;
@@ -1311,12 +1192,6 @@ ObjectGroup::newPlainObject(ExclusiveContext* cx, IdValuePair* properties, size_
RootedObjectGroup group(cx, p->value().group);
- // Watch for existing groups which now use an unboxed layout.
- if (group->maybeUnboxedLayout()) {
- MOZ_ASSERT(group->unboxedLayout().properties().length() == nproperties);
- return UnboxedPlainObject::createWithProperties(cx, group, newKind, properties);
- }
-
// Update property types according to the properties we are about to add.
// Do this before we do anything which can GC, which might move or remove
// this table entry.
@@ -1503,18 +1378,6 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* scriptArg, jsbytecode*
}
}
- if (kind == JSProto_Array &&
- (JSOp(*pc) == JSOP_NEWARRAY || IsCallPC(pc)) &&
- cx->options().unboxedArrays())
- {
- PreliminaryObjectArrayWithTemplate* preliminaryObjects =
- cx->new_<PreliminaryObjectArrayWithTemplate>(nullptr);
- if (preliminaryObjects)
- res->setPreliminaryObjects(preliminaryObjects);
- else
- cx->recoverFromOutOfMemory();
- }
-
if (!table->add(p, key, res)) {
ReportOutOfMemory(cx);
return nullptr;
diff --git a/js/src/vm/ObjectGroup.h b/js/src/vm/ObjectGroup.h
index 4e24de9f1..0b6eaee51 100644
--- a/js/src/vm/ObjectGroup.h
+++ b/js/src/vm/ObjectGroup.h
@@ -20,7 +20,6 @@
namespace js {
class TypeDescr;
-class UnboxedLayout;
class PreliminaryObjectArrayWithTemplate;
class TypeNewScript;
@@ -154,16 +153,6 @@ class ObjectGroup : public gc::TenuredCell
// For some plain objects, the addendum stores a PreliminaryObjectArrayWithTemplate.
Addendum_PreliminaryObjects,
- // When objects in this group have an unboxed representation, the
- // addendum stores an UnboxedLayout (which might have a TypeNewScript
- // as well, if the group is also constructed using 'new').
- Addendum_UnboxedLayout,
-
- // If this group is used by objects that have been converted from an
- // unboxed representation and/or have the same allocation kind as such
- // objects, the addendum points to that unboxed group.
- Addendum_OriginalUnboxedGroup,
-
// When used by typed objects, the addendum stores a TypeDescr.
Addendum_TypeDescr
};
@@ -185,7 +174,6 @@ class ObjectGroup : public gc::TenuredCell
return nullptr;
}
- TypeNewScript* anyNewScript();
void detachNewScript(bool writeBarrier, ObjectGroup* replacement);
ObjectGroupFlags flagsDontCheckGeneration() const {
@@ -225,34 +213,6 @@ class ObjectGroup : public gc::TenuredCell
maybePreliminaryObjectsDontCheckGeneration();
}
- inline UnboxedLayout* maybeUnboxedLayout();
- inline UnboxedLayout& unboxedLayout();
-
- UnboxedLayout* maybeUnboxedLayoutDontCheckGeneration() const {
- if (addendumKind() == Addendum_UnboxedLayout)
- return reinterpret_cast<UnboxedLayout*>(addendum_);
- return nullptr;
- }
-
- UnboxedLayout& unboxedLayoutDontCheckGeneration() const {
- MOZ_ASSERT(addendumKind() == Addendum_UnboxedLayout);
- return *maybeUnboxedLayoutDontCheckGeneration();
- }
-
- void setUnboxedLayout(UnboxedLayout* layout) {
- setAddendum(Addendum_UnboxedLayout, layout);
- }
-
- ObjectGroup* maybeOriginalUnboxedGroup() const {
- if (addendumKind() == Addendum_OriginalUnboxedGroup)
- return reinterpret_cast<ObjectGroup*>(addendum_);
- return nullptr;
- }
-
- void setOriginalUnboxedGroup(ObjectGroup* group) {
- setAddendum(Addendum_OriginalUnboxedGroup, group);
- }
-
TypeDescr* maybeTypeDescr() {
// Note: there is no need to sweep when accessing the type descriptor
// of an object, as it is strongly held and immutable.
@@ -313,9 +273,8 @@ class ObjectGroup : public gc::TenuredCell
* that can be read out of that property in actual JS objects. In native
* objects, property types account for plain data properties (those with a
* slot and no getter or setter hook) and dense elements. In typed objects
- * and unboxed objects, property types account for object and value
- * properties and elements in the object, and expando properties in unboxed
- * objects.
+ * property types account for object and value properties and elements in
+ * the object.
*
* For accesses on these properties, the correspondence is as follows:
*
@@ -338,10 +297,9 @@ class ObjectGroup : public gc::TenuredCell
* 2. Array lengths are special cased by the compiler and VM and are not
* reflected in property types.
*
- * 3. In typed objects (but not unboxed objects), the initial values of
- * properties (null pointers and undefined values) are not reflected in
- * the property types. These values are always possible when reading the
- * property.
+ * 3. In typed objects, the initial values of properties (null pointers and
+ * undefined values) are not reflected in the property types. These
+ * values are always possible when reading the property.
*
* We establish these by using write barriers on calls to setProperty and
* defineProperty which are on native properties, and on any jitcode which
@@ -455,12 +413,6 @@ class ObjectGroup : public gc::TenuredCell
return &flags_;
}
- // Get the bit pattern stored in an object's addendum when it has an
- // original unboxed group.
- static inline int32_t addendumOriginalUnboxedGroupValue() {
- return Addendum_OriginalUnboxedGroup << OBJECT_FLAG_ADDENDUM_SHIFT;
- }
-
inline uint32_t basePropertyCount();
private:
@@ -505,14 +457,14 @@ class ObjectGroup : public gc::TenuredCell
UnknownIndex // Make an array with an unknown element type.
};
- // Create an ArrayObject or UnboxedArrayObject with the specified elements
- // and a group specialized for the elements.
- static JSObject* newArrayObject(ExclusiveContext* cx, const Value* vp, size_t length,
- NewObjectKind newKind,
- NewArrayKind arrayKind = NewArrayKind::Normal);
+ // Create an ArrayObject with the specified elements and a group specialized
+ // for the elements.
+ static ArrayObject* newArrayObject(ExclusiveContext* cx, const Value* vp, size_t length,
+ NewObjectKind newKind,
+ NewArrayKind arrayKind = NewArrayKind::Normal);
- // Create a PlainObject or UnboxedPlainObject with the specified properties
- // and a group specialized for those properties.
+ // Create a PlainObject with the specified properties and a group specialized
+ // for those properties.
static JSObject* newPlainObject(ExclusiveContext* cx,
IdValuePair* properties, size_t nproperties,
NewObjectKind newKind);
diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h
index 4b044c8d8..3c4d61a67 100644
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -1281,17 +1281,7 @@
* Stack: receiver, obj, propval => obj[propval]
*/ \
macro(JSOP_GETELEM_SUPER, 125, "getelem-super", NULL, 1, 3, 1, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) \
- /*
- * Pushes newly created array for a spread call onto the stack. This has
- * the same semantics as JSOP_NEWARRAY, but is distinguished to avoid
- * using unboxed arrays in spread calls, which would make compiling spread
- * calls in baseline more complex.
- * Category: Literals
- * Type: Array
- * Operands: uint32_t length
- * Stack: => obj
- */ \
- macro(JSOP_SPREADCALLARRAY, 126, "spreadcallarray", NULL, 5, 0, 1, JOF_UINT32) \
+ macro(JSOP_UNUSED126, 126, "unused126", NULL, 5, 0, 1, JOF_UINT32) \
\
/*
* Defines the given function on the current scope.
@@ -2292,14 +2282,23 @@
* Operands:
* Stack: =>
*/ \
- macro(JSOP_JUMPTARGET, 230, "jumptarget", NULL, 1, 0, 0, JOF_BYTE)
+ macro(JSOP_JUMPTARGET, 230, "jumptarget", NULL, 1, 0, 0, JOF_BYTE)\
+ /*
+ * Like JSOP_CALL, but tells the function that the return value is ignored.
+ * stack.
+ * Category: Statements
+ * Type: Function
+ * Operands: uint16_t argc
+ * Stack: callee, this, args[0], ..., args[argc-1] => rval
+ * nuses: (argc+2)
+ */ \
+ macro(JSOP_CALL_IGNORES_RV, 231, "call-ignores-rv", NULL, 3, -1, 1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET)
/*
* In certain circumstances it may be useful to "pad out" the opcode space to
* a power of two. Use this macro to do so.
*/
#define FOR_EACH_TRAILING_UNUSED_OPCODE(macro) \
- macro(231) \
macro(232) \
macro(233) \
macro(234) \
diff --git a/js/src/vm/ProxyObject.h b/js/src/vm/ProxyObject.h
index a0a929b20..d86d72cc9 100644
--- a/js/src/vm/ProxyObject.h
+++ b/js/src/vm/ProxyObject.h
@@ -104,7 +104,7 @@ class ProxyObject : public ShapedObject
public:
static unsigned grayLinkExtraSlot(JSObject* obj);
- void renew(JSContext* cx, const BaseProxyHandler* handler, const Value& priv);
+ void renew(const BaseProxyHandler* handler, const Value& priv);
static void trace(JSTracer* trc, JSObject* obj);
diff --git a/js/src/vm/ReceiverGuard.cpp b/js/src/vm/ReceiverGuard.cpp
index 11c2d0727..e95e8a208 100644
--- a/js/src/vm/ReceiverGuard.cpp
+++ b/js/src/vm/ReceiverGuard.cpp
@@ -15,11 +15,7 @@ ReceiverGuard::ReceiverGuard(JSObject* obj)
: group(nullptr), shape(nullptr)
{
if (obj) {
- if (obj->is<UnboxedPlainObject>()) {
- group = obj->group();
- if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
- shape = expando->lastProperty();
- } else if (obj->is<UnboxedArrayObject>() || obj->is<TypedObject>()) {
+ if (obj->is<TypedObject>()) {
group = obj->group();
} else {
shape = obj->maybeShape();
@@ -32,9 +28,7 @@ ReceiverGuard::ReceiverGuard(ObjectGroup* group, Shape* shape)
{
if (group) {
const Class* clasp = group->clasp();
- if (clasp == &UnboxedPlainObject::class_) {
- // Keep both group and shape.
- } else if (clasp == &UnboxedArrayObject::class_ || IsTypedObjectClass(clasp)) {
+ if (IsTypedObjectClass(clasp)) {
this->shape = nullptr;
} else {
this->group = nullptr;
@@ -45,12 +39,8 @@ ReceiverGuard::ReceiverGuard(ObjectGroup* group, Shape* shape)
/* static */ int32_t
HeapReceiverGuard::keyBits(JSObject* obj)
{
- if (obj->is<UnboxedPlainObject>()) {
- // Both the group and shape need to be guarded for unboxed plain objects.
- return obj->as<UnboxedPlainObject>().maybeExpando() ? 0 : 1;
- }
- if (obj->is<UnboxedArrayObject>() || obj->is<TypedObject>()) {
- // Only the group needs to be guarded for unboxed arrays and typed objects.
+ if (obj->is<TypedObject>()) {
+ // Only the group needs to be guarded for typed objects.
return 2;
}
// Other objects only need the shape to be guarded.
diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp
index e0b44e1eb..ef97ed816 100644
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -129,10 +129,10 @@ RegExpSharedReadBarrier(JSContext* cx, RegExpShared* shared)
shared->unmarkGray();
}
-bool
-RegExpObject::getShared(JSContext* cx, RegExpGuard* g)
+/* static */ bool
+RegExpObject::getShared(JSContext* cx, Handle<RegExpObject*> regexp, RegExpGuard* g)
{
- if (RegExpShared* shared = maybeShared()) {
+ if (RegExpShared* shared = regexp->maybeShared()) {
// Fetching a RegExpShared from an object requires a read
// barrier, as the shared pointer might be weak.
RegExpSharedReadBarrier(cx, shared);
@@ -141,7 +141,7 @@ RegExpObject::getShared(JSContext* cx, RegExpGuard* g)
return true;
}
- return createShared(cx, g);
+ return createShared(cx, regexp, g);
}
/* static */ bool
@@ -199,7 +199,7 @@ RegExpObject::trace(JSTracer* trc, JSObject* obj)
static JSObject*
CreateRegExpPrototype(JSContext* cx, JSProtoKey key)
{
- return cx->global()->createBlankPrototype(cx, &RegExpObject::protoClass_);
+ return GlobalObject::createBlankPrototype(cx, cx->global(), &RegExpObject::protoClass_);
}
static const ClassOps RegExpObjectClassOps = {
@@ -279,16 +279,14 @@ RegExpObject::create(ExclusiveContext* cx, HandleAtom source, RegExpFlag flags,
return regexp;
}
-bool
-RegExpObject::createShared(JSContext* cx, RegExpGuard* g)
+/* static */ bool
+RegExpObject::createShared(JSContext* cx, Handle<RegExpObject*> regexp, RegExpGuard* g)
{
- Rooted<RegExpObject*> self(cx, this);
-
- MOZ_ASSERT(!maybeShared());
- if (!cx->compartment()->regExps.get(cx, getSource(), getFlags(), g))
+ MOZ_ASSERT(!regexp->maybeShared());
+ if (!cx->compartment()->regExps.get(cx, regexp->getSource(), regexp->getFlags(), g))
return false;
- self->setShared(**g);
+ regexp->setShared(**g);
return true;
}
@@ -300,7 +298,8 @@ RegExpObject::assignInitialShape(ExclusiveContext* cx, Handle<RegExpObject*> sel
JS_STATIC_ASSERT(LAST_INDEX_SLOT == 0);
/* The lastIndex property alone is writable but non-configurable. */
- return self->addDataProperty(cx, cx->names().lastIndex, LAST_INDEX_SLOT, JSPROP_PERMANENT);
+ return NativeObject::addDataProperty(cx, self, cx->names().lastIndex, LAST_INDEX_SLOT,
+ JSPROP_PERMANENT);
}
void
@@ -891,11 +890,12 @@ RegExpShared::dumpBytecode(JSContext* cx, bool match_only, HandleLinearString in
return true;
}
-bool
-RegExpObject::dumpBytecode(JSContext* cx, bool match_only, HandleLinearString input)
+/* static */ bool
+RegExpObject::dumpBytecode(JSContext* cx, Handle<RegExpObject*> regexp,
+ bool match_only, HandleLinearString input)
{
RegExpGuard g(cx);
- if (!getShared(cx, &g))
+ if (!getShared(cx, regexp, &g))
return false;
return g.re()->dumpBytecode(cx, match_only, input);
@@ -1430,7 +1430,7 @@ js::CloneRegExpObject(JSContext* cx, JSObject* obj_)
Rooted<JSAtom*> source(cx, regex->getSource());
RegExpGuard g(cx);
- if (!regex->getShared(cx, &g))
+ if (!RegExpObject::getShared(cx, regex, &g))
return nullptr;
clone->initAndZeroLastIndex(source, g->getFlags(), cx);
diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h
index dc428a973..f1ea101ed 100644
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -483,7 +483,8 @@ class RegExpObject : public NativeObject
static bool isOriginalFlagGetter(JSNative native, RegExpFlag* mask);
- bool getShared(JSContext* cx, RegExpGuard* g);
+ static MOZ_MUST_USE bool getShared(JSContext* cx, Handle<RegExpObject*> regexp,
+ RegExpGuard* g);
void setShared(RegExpShared& shared) {
MOZ_ASSERT(!maybeShared());
@@ -500,7 +501,8 @@ class RegExpObject : public NativeObject
void initAndZeroLastIndex(HandleAtom source, RegExpFlag flags, ExclusiveContext* cx);
#ifdef DEBUG
- bool dumpBytecode(JSContext* cx, bool match_only, HandleLinearString input);
+ static MOZ_MUST_USE bool dumpBytecode(JSContext* cx, Handle<RegExpObject*> regexp,
+ bool match_only, HandleLinearString input);
#endif
private:
@@ -508,7 +510,8 @@ class RegExpObject : public NativeObject
* Precondition: the syntax for |source| has already been validated.
* Side effect: sets the private field.
*/
- bool createShared(JSContext* cx, RegExpGuard* g);
+ static MOZ_MUST_USE bool createShared(JSContext* cx, Handle<RegExpObject*> regexp,
+ RegExpGuard* g);
RegExpShared* maybeShared() const {
return static_cast<RegExpShared*>(NativeObject::getPrivate(PRIVATE_SLOT));
}
@@ -531,7 +534,7 @@ inline bool
RegExpToShared(JSContext* cx, HandleObject obj, RegExpGuard* g)
{
if (obj->is<RegExpObject>())
- return obj->as<RegExpObject>().getShared(cx, g);
+ return RegExpObject::getShared(cx, obj.as<RegExpObject>(), g);
return Proxy::regexp_toShared(cx, obj, g);
}
diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp
index 8eb997c71..5fc8e0e17 100644
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -589,7 +589,7 @@ JSRuntime::requestInterrupt(InterruptMode mode)
// Atomics.wait().
fx.lock();
if (fx.isWaiting())
- fx.wake(FutexRuntime::WakeForJSInterrupt);
+ fx.notify(FutexRuntime::NotifyForJSInterrupt);
fx.unlock();
InterruptRunningJitCode(this);
}
diff --git a/js/src/vm/Scope.cpp b/js/src/vm/Scope.cpp
index a71c03695..0f80d7b69 100644
--- a/js/src/vm/Scope.cpp
+++ b/js/src/vm/Scope.cpp
@@ -669,6 +669,14 @@ FunctionScope::script() const
return canonicalFunction()->nonLazyScript();
}
+/* static */ bool
+FunctionScope::isSpecialName(ExclusiveContext* cx, JSAtom* name)
+{
+ return name == cx->names().arguments ||
+ name == cx->names().dotThis ||
+ name == cx->names().dotGenerator;
+}
+
/* static */ Shape*
FunctionScope::getEmptyEnvironmentShape(ExclusiveContext* cx, bool hasParameterExprs)
{
diff --git a/js/src/vm/Scope.h b/js/src/vm/Scope.h
index 1d04fd9f6..4a4ae8090 100644
--- a/js/src/vm/Scope.h
+++ b/js/src/vm/Scope.h
@@ -446,10 +446,11 @@ Scope::is<LexicalScope>() const
}
//
-// Scope corresponding to a function. Holds formal parameter names and, if the
-// function parameters contain no expressions that might possibly be
-// evaluated, the function's var bindings. For example, in these functions,
-// the FunctionScope will store a/b/c bindings but not d/e/f bindings:
+// Scope corresponding to a function. Holds formal parameter names, special
+// internal names (see FunctionScope::isSpecialName), and, if the function
+// parameters contain no expressions that might possibly be evaluated, the
+// function's var bindings. For example, in these functions, the FunctionScope
+// will store a/b/c bindings but not d/e/f bindings:
//
// function f1(a, b) {
// var c;
@@ -562,6 +563,8 @@ class FunctionScope : public Scope
return data().nonPositionalFormalStart;
}
+ static bool isSpecialName(ExclusiveContext* cx, JSAtom* name);
+
static Shape* getEmptyEnvironmentShape(ExclusiveContext* cx, bool hasParameterExprs);
};
diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
index ccd4cc8d7..792a00490 100644
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -477,7 +477,7 @@ intrinsic_FinishBoundFunctionInit(JSContext* cx, unsigned argc, Value* vp)
// Try to avoid invoking the resolve hook.
if (targetObj->is<JSFunction>() && !targetObj->as<JSFunction>().hasResolvedLength()) {
RootedValue targetLength(cx);
- if (!targetObj->as<JSFunction>().getUnresolvedLength(cx, &targetLength))
+ if (!JSFunction::getUnresolvedLength(cx, targetObj.as<JSFunction>(), &targetLength))
return false;
length = Max(0.0, targetLength.toNumber() - argCount);
@@ -2154,7 +2154,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_INLINABLE_FN("std_Array_slice", array_slice, 2,0, ArraySlice),
JS_FN("std_Array_sort", array_sort, 1,0),
JS_FN("std_Array_reverse", array_reverse, 0,0),
- JS_INLINABLE_FN("std_Array_splice", array_splice, 2,0, ArraySplice),
+ JS_FNINFO("std_Array_splice", array_splice, &array_splice_info, 2,0),
JS_FN("std_Date_now", date_now, 0,0),
JS_FN("std_Date_valueOf", date_valueOf, 0,0),
@@ -3008,7 +3008,7 @@ JSRuntime::cloneSelfHostedFunctionScript(JSContext* cx, HandlePropertyName name,
MOZ_ASSERT(targetFun->isInterpretedLazy());
MOZ_ASSERT(targetFun->isSelfHostedBuiltin());
- RootedScript sourceScript(cx, sourceFun->getOrCreateScript(cx));
+ RootedScript sourceScript(cx, JSFunction::getOrCreateScript(cx, sourceFun));
if (!sourceScript)
return false;
diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp
index 306a2c540..8fe2145e5 100644
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -460,15 +460,13 @@ NativeObject::getChildProperty(ExclusiveContext* cx,
return shape;
}
-bool
-js::NativeObject::toDictionaryMode(ExclusiveContext* cx)
+/* static */ bool
+js::NativeObject::toDictionaryMode(ExclusiveContext* cx, HandleNativeObject obj)
{
- MOZ_ASSERT(!inDictionaryMode());
- MOZ_ASSERT(cx->isInsideCurrentCompartment(this));
-
- uint32_t span = slotSpan();
+ MOZ_ASSERT(!obj->inDictionaryMode());
+ MOZ_ASSERT(cx->isInsideCurrentCompartment(obj));
- Rooted<NativeObject*> self(cx, this);
+ uint32_t span = obj->slotSpan();
// Clone the shapes into a new dictionary list. Don't update the last
// property of this object until done, otherwise a GC triggered while
@@ -476,7 +474,7 @@ js::NativeObject::toDictionaryMode(ExclusiveContext* cx)
RootedShape root(cx);
RootedShape dictionaryShape(cx);
- RootedShape shape(cx, lastProperty());
+ RootedShape shape(cx, obj->lastProperty());
while (shape) {
MOZ_ASSERT(!shape->inDictionary());
@@ -488,7 +486,7 @@ js::NativeObject::toDictionaryMode(ExclusiveContext* cx)
GCPtrShape* listp = dictionaryShape ? &dictionaryShape->parent : nullptr;
StackShape child(shape);
- dprop->initDictionaryShape(child, self->numFixedSlots(), listp);
+ dprop->initDictionaryShape(child, obj->numFixedSlots(), listp);
if (!dictionaryShape)
root = dprop;
@@ -503,18 +501,18 @@ js::NativeObject::toDictionaryMode(ExclusiveContext* cx)
return false;
}
- if (IsInsideNursery(self) &&
- !cx->asJSContext()->gc.nursery.queueDictionaryModeObjectToSweep(self))
+ if (IsInsideNursery(obj) &&
+ !cx->asJSContext()->gc.nursery.queueDictionaryModeObjectToSweep(obj))
{
ReportOutOfMemory(cx);
return false;
}
MOZ_ASSERT(root->listp == nullptr);
- root->listp = &self->shape_;
- self->shape_ = root;
+ root->listp = &obj->shape_;
+ obj->shape_ = root;
- MOZ_ASSERT(self->inDictionaryMode());
+ MOZ_ASSERT(obj->inDictionaryMode());
root->base()->setSlotSpan(span);
return true;
@@ -534,7 +532,7 @@ NativeObject::addProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId
return nullptr;
if (!extensible) {
if (cx->isJSContext())
- obj->reportNotExtensible(cx->asJSContext());
+ JSObject::reportNotExtensible(cx->asJSContext(), obj);
return nullptr;
}
@@ -592,7 +590,7 @@ NativeObject::addPropertyInternal(ExclusiveContext* cx,
if (allowDictionary &&
(!stableSlot || ShouldConvertToDictionary(obj)))
{
- if (!obj->toDictionaryMode(cx))
+ if (!toDictionaryMode(cx, obj))
return nullptr;
table = obj->lastProperty()->maybeTable(keep);
entry = &table->search<MaybeAdding::Adding>(id, keep);
@@ -727,7 +725,7 @@ CheckCanChangeAttrs(ExclusiveContext* cx, JSObject* obj, Shape* shape, unsigned*
(*attrsp & (JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED)))
{
if (cx->isJSContext())
- obj->reportNotConfigurable(cx->asJSContext(), shape->propid());
+ JSObject::reportNotConfigurable(cx->asJSContext(), shape->propid());
return false;
}
@@ -785,7 +783,7 @@ NativeObject::putProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId
if (!extensible) {
if (cx->isJSContext())
- obj->reportNotExtensible(cx->asJSContext());
+ JSObject::reportNotExtensible(cx->asJSContext(), obj);
return nullptr;
}
@@ -834,7 +832,7 @@ NativeObject::putProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId
* addPropertyInternal because a failure under add would lose data.
*/
if (shape != obj->lastProperty() && !obj->inDictionaryMode()) {
- if (!obj->toDictionaryMode(cx))
+ if (!toDictionaryMode(cx, obj))
return nullptr;
ShapeTable* table = obj->lastProperty()->maybeTable(keep);
MOZ_ASSERT(table);
@@ -853,10 +851,11 @@ NativeObject::putProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId
*/
bool updateLast = (shape == obj->lastProperty());
bool accessorShape = getter || setter || (attrs & (JSPROP_GETTER | JSPROP_SETTER));
- shape = obj->replaceWithNewEquivalentShape(cx, shape, nullptr, accessorShape);
+ shape = NativeObject::replaceWithNewEquivalentShape(cx, obj, shape, nullptr,
+ accessorShape);
if (!shape)
return nullptr;
- if (!updateLast && !obj->generateOwnShape(cx))
+ if (!updateLast && !NativeObject::generateOwnShape(cx, obj))
return nullptr;
/*
@@ -968,16 +967,15 @@ NativeObject::changeProperty(ExclusiveContext* cx, HandleNativeObject obj, Handl
return newShape;
}
-bool
-NativeObject::removeProperty(ExclusiveContext* cx, jsid id_)
+/* static */ bool
+NativeObject::removeProperty(ExclusiveContext* cx, HandleNativeObject obj, jsid id_)
{
RootedId id(cx, id_);
- RootedNativeObject self(cx, this);
AutoKeepShapeTables keep(cx);
ShapeTable::Entry* entry;
RootedShape shape(cx);
- if (!Shape::search(cx, lastProperty(), id, keep, shape.address(), &entry))
+ if (!Shape::search(cx, obj->lastProperty(), id, keep, shape.address(), &entry))
return false;
if (!shape)
@@ -987,10 +985,10 @@ NativeObject::removeProperty(ExclusiveContext* cx, jsid id_)
* If shape is not the last property added, or the last property cannot
* be removed, switch to dictionary mode.
*/
- if (!self->inDictionaryMode() && (shape != self->lastProperty() || !self->canRemoveLastProperty())) {
- if (!self->toDictionaryMode(cx))
+ if (!obj->inDictionaryMode() && (shape != obj->lastProperty() || !obj->canRemoveLastProperty())) {
+ if (!toDictionaryMode(cx, obj))
return false;
- ShapeTable* table = self->lastProperty()->maybeTable(keep);
+ ShapeTable* table = obj->lastProperty()->maybeTable(keep);
MOZ_ASSERT(table);
entry = &table->search<MaybeAdding::NotAdding>(shape->propid(), keep);
shape = entry->shape();
@@ -1004,21 +1002,21 @@ NativeObject::removeProperty(ExclusiveContext* cx, jsid id_)
* the object or table, so the remaining removal is infallible.
*/
RootedShape spare(cx);
- if (self->inDictionaryMode()) {
+ if (obj->inDictionaryMode()) {
/* For simplicity, always allocate an accessor shape for now. */
spare = Allocate<AccessorShape>(cx);
if (!spare)
return false;
new (spare) Shape(shape->base()->unowned(), 0);
- if (shape == self->lastProperty()) {
+ if (shape == obj->lastProperty()) {
/*
* Get an up to date unowned base shape for the new last property
* when removing the dictionary's last property. Information in
* base shapes for non-last properties may be out of sync with the
* object's state.
*/
- RootedShape previous(cx, self->lastProperty()->parent);
- StackBaseShape base(self->lastProperty()->base());
+ RootedShape previous(cx, obj->lastProperty()->parent);
+ StackBaseShape base(obj->lastProperty()->base());
BaseShape* nbase = BaseShape::getUnowned(cx, base);
if (!nbase)
return false;
@@ -1028,7 +1026,7 @@ NativeObject::removeProperty(ExclusiveContext* cx, jsid id_)
/* If shape has a slot, free its slot number. */
if (shape->hasSlot()) {
- self->freeSlot(cx, shape->slot());
+ obj->freeSlot(cx, shape->slot());
if (cx->isJSContext())
++cx->asJSContext()->runtime()->propertyRemovals;
}
@@ -1038,8 +1036,8 @@ NativeObject::removeProperty(ExclusiveContext* cx, jsid id_)
* doubly linked list, hashed by lastProperty()->table. So we can edit the
* list and hash in place.
*/
- if (self->inDictionaryMode()) {
- ShapeTable* table = self->lastProperty()->maybeTable(keep);
+ if (obj->inDictionaryMode()) {
+ ShapeTable* table = obj->lastProperty()->maybeTable(keep);
MOZ_ASSERT(table);
if (entry->hadCollision()) {
@@ -1056,23 +1054,23 @@ NativeObject::removeProperty(ExclusiveContext* cx, jsid id_)
* checks not to alter significantly the complexity of the
* delete in debug builds, see bug 534493.
*/
- Shape* aprop = self->lastProperty();
+ Shape* aprop = obj->lastProperty();
for (int n = 50; --n >= 0 && aprop->parent; aprop = aprop->parent)
- MOZ_ASSERT_IF(aprop != shape, self->contains(cx, aprop));
+ MOZ_ASSERT_IF(aprop != shape, obj->contains(cx, aprop));
#endif
}
{
/* Remove shape from its non-circular doubly linked list. */
- Shape* oldLastProp = self->lastProperty();
- shape->removeFromDictionary(self);
+ Shape* oldLastProp = obj->lastProperty();
+ shape->removeFromDictionary(obj);
/* Hand off table from the old to new last property. */
- oldLastProp->handoffTableTo(self->lastProperty());
+ oldLastProp->handoffTableTo(obj->lastProperty());
}
/* Generate a new shape for the object, infallibly. */
- JS_ALWAYS_TRUE(self->generateOwnShape(cx, spare));
+ JS_ALWAYS_TRUE(NativeObject::generateOwnShape(cx, obj, spare));
/* Consider shrinking table if its load factor is <= .25. */
uint32_t size = table->capacity();
@@ -1085,11 +1083,11 @@ NativeObject::removeProperty(ExclusiveContext* cx, jsid id_)
* lazily make via a later hashify the exact table for the new property
* lineage.
*/
- MOZ_ASSERT(shape == self->lastProperty());
- self->removeLastProperty(cx);
+ MOZ_ASSERT(shape == obj->lastProperty());
+ obj->removeLastProperty(cx);
}
- self->checkShapeConsistency();
+ obj->checkShapeConsistency();
return true;
}
@@ -1133,35 +1131,30 @@ NativeObject::rollbackProperties(ExclusiveContext* cx, HandleNativeObject obj, u
if (slot < slotSpan)
break;
}
- if (!obj->removeProperty(cx, obj->lastProperty()->propid()))
+ if (!NativeObject::removeProperty(cx, obj, obj->lastProperty()->propid()))
return false;
}
return true;
}
-Shape*
-NativeObject::replaceWithNewEquivalentShape(ExclusiveContext* cx, Shape* oldShape, Shape* newShape,
- bool accessorShape)
+/* static */ Shape*
+NativeObject::replaceWithNewEquivalentShape(ExclusiveContext* cx, HandleNativeObject obj,
+ Shape* oldShape, Shape* newShape, bool accessorShape)
{
MOZ_ASSERT(cx->isInsideCurrentZone(oldShape));
- MOZ_ASSERT_IF(oldShape != lastProperty(),
- inDictionaryMode() && lookup(cx, oldShape->propidRef()) == oldShape);
-
- NativeObject* self = this;
+ MOZ_ASSERT_IF(oldShape != obj->lastProperty(),
+ obj->inDictionaryMode() && obj->lookup(cx, oldShape->propidRef()) == oldShape);
- if (!inDictionaryMode()) {
- RootedNativeObject selfRoot(cx, self);
+ if (!obj->inDictionaryMode()) {
RootedShape newRoot(cx, newShape);
- if (!toDictionaryMode(cx))
+ if (!toDictionaryMode(cx, obj))
return nullptr;
- oldShape = selfRoot->lastProperty();
- self = selfRoot;
+ oldShape = obj->lastProperty();
newShape = newRoot;
}
if (!newShape) {
- RootedNativeObject selfRoot(cx, self);
RootedShape oldRoot(cx, oldShape);
newShape = (oldShape->isAccessorShape() || accessorShape)
? Allocate<AccessorShape>(cx)
@@ -1169,12 +1162,11 @@ NativeObject::replaceWithNewEquivalentShape(ExclusiveContext* cx, Shape* oldShap
if (!newShape)
return nullptr;
new (newShape) Shape(oldRoot->base()->unowned(), 0);
- self = selfRoot;
oldShape = oldRoot;
}
AutoCheckCannotGC nogc;
- ShapeTable* table = self->lastProperty()->ensureTableForDictionary(cx, nogc);
+ ShapeTable* table = obj->lastProperty()->ensureTableForDictionary(cx, nogc);
if (!table)
return nullptr;
@@ -1187,12 +1179,12 @@ NativeObject::replaceWithNewEquivalentShape(ExclusiveContext* cx, Shape* oldShap
* enumeration order (see bug 601399).
*/
StackShape nshape(oldShape);
- newShape->initDictionaryShape(nshape, self->numFixedSlots(), oldShape->listp);
+ newShape->initDictionaryShape(nshape, obj->numFixedSlots(), oldShape->listp);
MOZ_ASSERT(newShape->parent == oldShape);
- oldShape->removeFromDictionary(self);
+ oldShape->removeFromDictionary(obj);
- if (newShape == self->lastProperty())
+ if (newShape == obj->lastProperty())
oldShape->handoffTableTo(newShape);
if (entry)
@@ -1200,63 +1192,63 @@ NativeObject::replaceWithNewEquivalentShape(ExclusiveContext* cx, Shape* oldShap
return newShape;
}
-bool
-NativeObject::shadowingShapeChange(ExclusiveContext* cx, const Shape& shape)
+/* static */ bool
+NativeObject::shadowingShapeChange(ExclusiveContext* cx, HandleNativeObject obj, const Shape& shape)
{
- return generateOwnShape(cx);
+ return generateOwnShape(cx, obj);
}
-bool
-JSObject::setFlags(ExclusiveContext* cx, BaseShape::Flag flags, GenerateShape generateShape)
+/* static */ bool
+JSObject::setFlags(ExclusiveContext* cx, HandleObject obj, BaseShape::Flag flags,
+ GenerateShape generateShape)
{
- if (hasAllFlags(flags))
+ if (obj->hasAllFlags(flags))
return true;
- RootedObject self(cx, this);
-
- Shape* existingShape = self->ensureShape(cx);
+ Shape* existingShape = obj->ensureShape(cx);
if (!existingShape)
return false;
- if (isNative() && as<NativeObject>().inDictionaryMode()) {
- if (generateShape == GENERATE_SHAPE && !as<NativeObject>().generateOwnShape(cx))
- return false;
- StackBaseShape base(self->as<NativeObject>().lastProperty());
+ if (obj->isNative() && obj->as<NativeObject>().inDictionaryMode()) {
+ if (generateShape == GENERATE_SHAPE) {
+ if (!NativeObject::generateOwnShape(cx, obj.as<NativeObject>()))
+ return false;
+ }
+ StackBaseShape base(obj->as<NativeObject>().lastProperty());
base.flags |= flags;
UnownedBaseShape* nbase = BaseShape::getUnowned(cx, base);
if (!nbase)
return false;
- self->as<NativeObject>().lastProperty()->base()->adoptUnowned(nbase);
+ obj->as<NativeObject>().lastProperty()->base()->adoptUnowned(nbase);
return true;
}
- Shape* newShape = Shape::setObjectFlags(cx, flags, self->taggedProto(), existingShape);
+ Shape* newShape = Shape::setObjectFlags(cx, flags, obj->taggedProto(), existingShape);
if (!newShape)
return false;
- // The success of the |JSObject::ensureShape| call above means that |self|
+ // The success of the |JSObject::ensureShape| call above means that |obj|
// can be assumed to have a shape.
- self->as<ShapedObject>().setShape(newShape);
+ obj->as<ShapedObject>().setShape(newShape);
return true;
}
-bool
-NativeObject::clearFlag(ExclusiveContext* cx, BaseShape::Flag flag)
+/* static */ bool
+NativeObject::clearFlag(ExclusiveContext* cx, HandleNativeObject obj, BaseShape::Flag flag)
{
- MOZ_ASSERT(inDictionaryMode());
+ MOZ_ASSERT(obj->inDictionaryMode());
- RootedNativeObject self(cx, &as<NativeObject>());
- MOZ_ASSERT(self->lastProperty()->getObjectFlags() & flag);
+ MOZ_ASSERT(obj->lastProperty()->getObjectFlags() & flag);
- StackBaseShape base(self->lastProperty());
+ StackBaseShape base(obj->lastProperty());
base.flags &= ~flag;
UnownedBaseShape* nbase = BaseShape::getUnowned(cx, base);
if (!nbase)
return false;
- self->lastProperty()->base()->adoptUnowned(nbase);
+ obj->lastProperty()->base()->adoptUnowned(nbase);
return true;
}
diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h
index 978798aaa..fd6d843e0 100644
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -905,9 +905,6 @@ class Shape : public gc::TenuredCell
setter() == rawSetter;
}
- bool set(JSContext* cx, HandleNativeObject obj, HandleObject receiver, MutableHandleValue vp,
- ObjectOpResult& result);
-
BaseShape* base() const { return base_.get(); }
bool hasSlot() const {
diff --git a/js/src/vm/SharedArrayObject.cpp b/js/src/vm/SharedArrayObject.cpp
index c69306aac..0dff41201 100644
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -366,7 +366,8 @@ static const Class SharedArrayBufferObjectProtoClass = {
static JSObject*
CreateSharedArrayBufferPrototype(JSContext* cx, JSProtoKey key)
{
- return cx->global()->createBlankPrototype(cx, &SharedArrayBufferObjectProtoClass);
+ return GlobalObject::createBlankPrototype(cx, cx->global(),
+ &SharedArrayBufferObjectProtoClass);
}
static const ClassOps SharedArrayBufferObjectClassOps = {
diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h
index a51c0aa14..11a19d175 100644
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -306,7 +306,7 @@ InterpreterStack::pushInlineFrame(JSContext* cx, InterpreterRegs& regs, const Ca
MOZ_ASSERT(regs.sp == args.end());
MOZ_ASSERT(callee->nonLazyScript() == script);
- script->ensureNonLazyCanonicalFunction(cx);
+ script->ensureNonLazyCanonicalFunction();
InterpreterFrame* prev = regs.fp();
jsbytecode* prevpc = regs.pc;
@@ -336,13 +336,13 @@ InterpreterStack::resumeGeneratorCallFrame(JSContext* cx, InterpreterRegs& regs,
HandleObject envChain)
{
MOZ_ASSERT(callee->isGenerator());
- RootedScript script(cx, callee->getOrCreateScript(cx));
+ RootedScript script(cx, JSFunction::getOrCreateScript(cx, callee));
InterpreterFrame* prev = regs.fp();
jsbytecode* prevpc = regs.pc;
Value* prevsp = regs.sp;
MOZ_ASSERT(prev);
- script->ensureNonLazyCanonicalFunction(cx);
+ script->ensureNonLazyCanonicalFunction();
LifoAlloc::Mark mark = allocator_.mark();
diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp
index 87e95c893..c5f2cf5f3 100644
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -82,7 +82,7 @@ InterpreterFrame::isNonGlobalEvalFrame() const
return isEvalFrame() && script()->bodyScope()->as<EvalScope>().isNonGlobal();
}
-JSObject*
+ArrayObject*
InterpreterFrame::createRestParameter(JSContext* cx)
{
MOZ_ASSERT(script()->hasRest());
diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h
index 552738d89..23e621344 100644
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -523,7 +523,7 @@ class InterpreterFrame
ArgumentsObject& argsObj() const;
void initArgsObj(ArgumentsObject& argsobj);
- JSObject* createRestParameter(JSContext* cx);
+ ArrayObject* createRestParameter(JSContext* cx);
/*
* Environment chain
@@ -1006,6 +1006,17 @@ class InvokeArgs : public detail::GenericArgsBase<NO_CONSTRUCT>
explicit InvokeArgs(JSContext* cx) : Base(cx) {}
};
+/** Function call args of statically-unknown count. */
+class InvokeArgsMaybeIgnoresReturnValue : public detail::GenericArgsBase<NO_CONSTRUCT>
+{
+ using Base = detail::GenericArgsBase<NO_CONSTRUCT>;
+
+ public:
+ explicit InvokeArgsMaybeIgnoresReturnValue(JSContext* cx, bool ignoresReturnValue) : Base(cx) {
+ this->ignoresReturnValue_ = ignoresReturnValue;
+ }
+};
+
/** Function call args of statically-known count. */
template <size_t N>
class FixedInvokeArgs : public detail::FixedArgsBase<NO_CONSTRUCT, N>
diff --git a/js/src/vm/StringObject-inl.h b/js/src/vm/StringObject-inl.h
index 5fc1656f6..38191fc7a 100644
--- a/js/src/vm/StringObject-inl.h
+++ b/js/src/vm/StringObject-inl.h
@@ -15,31 +15,29 @@
namespace js {
-inline bool
-StringObject::init(JSContext* cx, HandleString str)
+/* static */ inline bool
+StringObject::init(JSContext* cx, Handle<StringObject*> obj, HandleString str)
{
- MOZ_ASSERT(numFixedSlots() == 2);
+ MOZ_ASSERT(obj->numFixedSlots() == 2);
- Rooted<StringObject*> self(cx, this);
-
- if (!EmptyShape::ensureInitialCustomShape<StringObject>(cx, self))
+ if (!EmptyShape::ensureInitialCustomShape<StringObject>(cx, obj))
return false;
- MOZ_ASSERT(self->lookup(cx, NameToId(cx->names().length))->slot() == LENGTH_SLOT);
+ MOZ_ASSERT(obj->lookup(cx, NameToId(cx->names().length))->slot() == LENGTH_SLOT);
- self->setStringThis(str);
+ obj->setStringThis(str);
return true;
}
-inline StringObject*
+/* static */ inline StringObject*
StringObject::create(JSContext* cx, HandleString str, HandleObject proto, NewObjectKind newKind)
{
JSObject* obj = NewObjectWithClassProto(cx, &class_, proto, newKind);
if (!obj)
return nullptr;
Rooted<StringObject*> strobj(cx, &obj->as<StringObject>());
- if (!strobj->init(cx, str))
+ if (!StringObject::init(cx, strobj, str))
return nullptr;
return strobj;
}
diff --git a/js/src/vm/StringObject.h b/js/src/vm/StringObject.h
index 119e3d9fa..561e0478a 100644
--- a/js/src/vm/StringObject.h
+++ b/js/src/vm/StringObject.h
@@ -56,7 +56,7 @@ class StringObject : public NativeObject
}
private:
- inline bool init(JSContext* cx, HandleString str);
+ static inline bool init(JSContext* cx, Handle<StringObject*> obj, HandleString str);
void setStringThis(JSString* str) {
MOZ_ASSERT(getReservedSlot(PRIMITIVE_VALUE_SLOT).isUndefined());
diff --git a/js/src/vm/TypeInference-inl.h b/js/src/vm/TypeInference-inl.h
index da47fa898..2af252cea 100644
--- a/js/src/vm/TypeInference-inl.h
+++ b/js/src/vm/TypeInference-inl.h
@@ -23,7 +23,6 @@
#include "vm/SharedArrayObject.h"
#include "vm/StringObject.h"
#include "vm/TypedArrayObject.h"
-#include "vm/UnboxedObject.h"
#include "jscntxtinlines.h"
@@ -285,10 +284,6 @@ TypeIdString(jsid id)
*/
struct AutoEnterAnalysis
{
- // For use when initializing an UnboxedLayout. The UniquePtr's destructor
- // must run when GC is not suppressed.
- UniquePtr<UnboxedLayout> unboxedLayoutToCleanUp;
-
// Prevent GC activity in the middle of analysis.
gc::AutoSuppressGC suppressGC;
diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp
index 9e0342382..7c2c0194e 100644
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -35,7 +35,6 @@
#include "vm/Opcodes.h"
#include "vm/Shape.h"
#include "vm/Time.h"
-#include "vm/UnboxedObject.h"
#include "jsatominlines.h"
#include "jsscriptinlines.h"
@@ -297,9 +296,6 @@ js::ObjectGroupHasProperty(JSContext* cx, ObjectGroup* group, jsid id, const Val
return true;
}
}
- JSObject* obj = &value.toObject();
- if (!obj->hasLazyGroup() && obj->group()->maybeOriginalUnboxedGroup())
- return true;
}
if (!types->hasType(type)) {
@@ -1323,7 +1319,8 @@ js::EnsureTrackPropertyTypes(JSContext* cx, JSObject* obj, jsid id)
AutoEnterAnalysis enter(cx);
if (obj->hasLazyGroup()) {
AutoEnterOOMUnsafeRegion oomUnsafe;
- if (!obj->getGroup(cx)) {
+ RootedObject objRoot(cx, obj);
+ if (!JSObject::getGroup(cx, objRoot)) {
oomUnsafe.crash("Could not allocate ObjectGroup in EnsureTrackPropertyTypes");
return;
}
@@ -1342,9 +1339,12 @@ HeapTypeSetKey::instantiate(JSContext* cx)
{
if (maybeTypes())
return true;
- if (object()->isSingleton() && !object()->singleton()->getGroup(cx)) {
- cx->clearPendingException();
- return false;
+ if (object()->isSingleton()) {
+ RootedObject obj(cx, object()->singleton());
+ if (!JSObject::getGroup(cx, obj)) {
+ cx->clearPendingException();
+ return false;
+ }
}
JSObject* obj = object()->isSingleton() ? object()->singleton() : nullptr;
maybeTypes_ = object()->maybeGroup()->getProperty(cx, obj, id());
@@ -1944,33 +1944,6 @@ class ConstraintDataFreezeObjectForTypedArrayData
}
};
-// Constraint which triggers recompilation if an unboxed object in some group
-// is converted to a native object.
-class ConstraintDataFreezeObjectForUnboxedConvertedToNative
-{
- public:
- ConstraintDataFreezeObjectForUnboxedConvertedToNative()
- {}
-
- const char* kind() { return "freezeObjectForUnboxedConvertedToNative"; }
-
- bool invalidateOnNewType(TypeSet::Type type) { return false; }
- bool invalidateOnNewPropertyState(TypeSet* property) { return false; }
- bool invalidateOnNewObjectState(ObjectGroup* group) {
- return group->unboxedLayout().nativeGroup() != nullptr;
- }
-
- bool constraintHolds(JSContext* cx,
- const HeapTypeSetKey& property, TemporaryTypeSet* expected)
- {
- return !invalidateOnNewObjectState(property.object()->maybeGroup());
- }
-
- bool shouldSweep() { return false; }
-
- JSCompartment* maybeCompartment() { return nullptr; }
-};
-
} /* anonymous namespace */
void
@@ -2505,8 +2478,6 @@ TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList* constraints, jsid
bool
js::ClassCanHaveExtraProperties(const Class* clasp)
{
- if (clasp == &UnboxedPlainObject::class_ || clasp == &UnboxedArrayObject::class_)
- return false;
return clasp->getResolve()
|| clasp->getOpsLookupProperty()
|| clasp->getOpsGetProperty()
@@ -2805,15 +2776,6 @@ js::AddTypePropertyId(ExclusiveContext* cx, ObjectGroup* group, JSObject* obj, j
// from acquiring the fully initialized group.
if (group->newScript() && group->newScript()->initializedGroup())
AddTypePropertyId(cx, group->newScript()->initializedGroup(), nullptr, id, type);
-
- // Maintain equivalent type information for unboxed object groups and their
- // corresponding native group. Since type sets might contain the unboxed
- // group but not the native group, this ensures optimizations based on the
- // unboxed group are valid for the native group.
- if (group->maybeUnboxedLayout() && group->maybeUnboxedLayout()->nativeGroup())
- AddTypePropertyId(cx, group->maybeUnboxedLayout()->nativeGroup(), nullptr, id, type);
- if (ObjectGroup* unboxedGroup = group->maybeOriginalUnboxedGroup())
- AddTypePropertyId(cx, unboxedGroup, nullptr, id, type);
}
void
@@ -2885,12 +2847,6 @@ ObjectGroup::setFlags(ExclusiveContext* cx, ObjectGroupFlags flags)
// acquired properties analysis.
if (newScript() && newScript()->initializedGroup())
newScript()->initializedGroup()->setFlags(cx, flags);
-
- // Propagate flag changes between unboxed and corresponding native groups.
- if (maybeUnboxedLayout() && maybeUnboxedLayout()->nativeGroup())
- maybeUnboxedLayout()->nativeGroup()->setFlags(cx, flags);
- if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup())
- unboxedGroup->setFlags(cx, flags);
}
void
@@ -2923,23 +2879,6 @@ ObjectGroup::markUnknown(ExclusiveContext* cx)
prop->types.setNonDataProperty(cx);
}
}
-
- if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup())
- MarkObjectGroupUnknownProperties(cx, unboxedGroup);
- if (maybeUnboxedLayout() && maybeUnboxedLayout()->nativeGroup())
- MarkObjectGroupUnknownProperties(cx, maybeUnboxedLayout()->nativeGroup());
- if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup())
- MarkObjectGroupUnknownProperties(cx, unboxedGroup);
-}
-
-TypeNewScript*
-ObjectGroup::anyNewScript()
-{
- if (newScript())
- return newScript();
- if (maybeUnboxedLayout())
- return unboxedLayout().newScript();
- return nullptr;
}
void
@@ -2949,7 +2888,7 @@ ObjectGroup::detachNewScript(bool writeBarrier, ObjectGroup* replacement)
// analyzed, remove it from the newObjectGroups table so that it will not be
// produced by calling 'new' on the associated function anymore.
// The TypeNewScript is not actually destroyed.
- TypeNewScript* newScript = anyNewScript();
+ TypeNewScript* newScript = this->newScript();
MOZ_ASSERT(newScript);
if (newScript->analyzed()) {
@@ -2968,10 +2907,7 @@ ObjectGroup::detachNewScript(bool writeBarrier, ObjectGroup* replacement)
MOZ_ASSERT(!replacement);
}
- if (this->newScript())
- setAddendum(Addendum_None, nullptr, writeBarrier);
- else
- unboxedLayout().setNewScript(nullptr, writeBarrier);
+ setAddendum(Addendum_None, nullptr, writeBarrier);
}
void
@@ -2982,7 +2918,7 @@ ObjectGroup::maybeClearNewScriptOnOOM()
if (!isMarked())
return;
- TypeNewScript* newScript = anyNewScript();
+ TypeNewScript* newScript = this->newScript();
if (!newScript)
return;
@@ -2997,7 +2933,7 @@ ObjectGroup::maybeClearNewScriptOnOOM()
void
ObjectGroup::clearNewScript(ExclusiveContext* cx, ObjectGroup* replacement /* = nullptr*/)
{
- TypeNewScript* newScript = anyNewScript();
+ TypeNewScript* newScript = this->newScript();
if (!newScript)
return;
@@ -3009,7 +2945,8 @@ ObjectGroup::clearNewScript(ExclusiveContext* cx, ObjectGroup* replacement /* =
// Mark the constructing function as having its 'new' script cleared, so we
// will not try to construct another one later.
- if (!newScript->function()->setNewScriptCleared(cx))
+ RootedFunction fun(cx, newScript->function());
+ if (!JSObject::setNewScriptCleared(cx, fun))
cx->recoverFromOutOfMemory();
}
@@ -3156,7 +3093,7 @@ js::AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx, ObjectGroup* gr
*/
RootedObject proto(cx, group->proto().toObjectOrNull());
while (proto) {
- ObjectGroup* protoGroup = proto->getGroup(cx);
+ ObjectGroup* protoGroup = JSObject::getGroup(cx, proto);
if (!protoGroup) {
cx->recoverFromOutOfMemory();
return false;
@@ -3393,7 +3330,7 @@ JSFunction::setTypeForScriptedFunction(ExclusiveContext* cx, HandleFunction fun,
/////////////////////////////////////////////////////////////////////
void
-PreliminaryObjectArray::registerNewObject(JSObject* res)
+PreliminaryObjectArray::registerNewObject(PlainObject* res)
{
// The preliminary object pointers are weak, and won't be swept properly
// during nursery collections, so the preliminary objects need to be
@@ -3411,7 +3348,7 @@ PreliminaryObjectArray::registerNewObject(JSObject* res)
}
void
-PreliminaryObjectArray::unregisterObject(JSObject* obj)
+PreliminaryObjectArray::unregisterObject(PlainObject* obj)
{
for (size_t i = 0; i < COUNT; i++) {
if (objects[i] == obj) {
@@ -3451,22 +3388,6 @@ PreliminaryObjectArray::sweep()
for (size_t i = 0; i < COUNT; i++) {
JSObject** ptr = &objects[i];
if (*ptr && IsAboutToBeFinalizedUnbarriered(ptr)) {
- // Before we clear this reference, change the object's group to the
- // Object.prototype group. This is done to ensure JSObject::finalize
- // sees a NativeObject Class even if we change the current group's
- // Class to one of the unboxed object classes in the meantime. If
- // the compartment's global is dead, we don't do anything as the
- // group's Class is not going to change in that case.
- JSObject* obj = *ptr;
- GlobalObject* global = obj->compartment()->unsafeUnbarrieredMaybeGlobal();
- if (global && !obj->isSingleton()) {
- JSObject* objectProto = GetBuiltinPrototypePure(global, JSProto_Object);
- obj->setGroup(objectProto->groupRaw());
- MOZ_ASSERT(obj->is<NativeObject>());
- MOZ_ASSERT(obj->getClass() == objectProto->getClass());
- MOZ_ASSERT(!obj->getClass()->hasFinalize());
- }
-
*ptr = nullptr;
}
}
@@ -3566,16 +3487,11 @@ PreliminaryObjectArrayWithTemplate::maybeAnalyze(ExclusiveContext* cx, ObjectGro
}
}
- if (group->maybeUnboxedLayout())
- return;
-
- if (shape()) {
- // We weren't able to use an unboxed layout, but since the preliminary
- // objects still reflect the template object's properties, and all
- // objects in the future will be created with those properties, the
- // properties can be marked as definite for objects in the group.
- group->addDefiniteProperties(cx, shape());
- }
+ // Since the preliminary objects still reflect the template object's
+ // properties, and all objects in the future will be created with those
+ // properties, the properties can be marked as definitive for objects in
+ // the group.
+ group->addDefiniteProperties(cx, shape());
}
/////////////////////////////////////////////////////////////////////
@@ -3589,7 +3505,6 @@ TypeNewScript::make(JSContext* cx, ObjectGroup* group, JSFunction* fun)
{
MOZ_ASSERT(cx->zone()->types.activeAnalysis);
MOZ_ASSERT(!group->newScript());
- MOZ_ASSERT(!group->maybeUnboxedLayout());
// rollbackPartiallyInitializedObjects expects function_ to be
// canonicalized.
@@ -3802,7 +3717,8 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate,
Vector<Initializer> initializerVector(cx);
RootedPlainObject templateRoot(cx, templateObject());
- if (!jit::AnalyzeNewScriptDefiniteProperties(cx, function(), group, templateRoot, &initializerVector))
+ RootedFunction fun(cx, function());
+ if (!jit::AnalyzeNewScriptDefiniteProperties(cx, fun, group, templateRoot, &initializerVector))
return false;
if (!group->newScript())
@@ -3852,27 +3768,6 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate,
js_delete(preliminaryObjects);
preliminaryObjects = nullptr;
- if (group->maybeUnboxedLayout()) {
- // An unboxed layout was constructed for the group, and this has already
- // been hooked into it.
- MOZ_ASSERT(group->unboxedLayout().newScript() == this);
- destroyNewScript.group = nullptr;
-
- // Clear out the template object, which is not used for TypeNewScripts
- // with an unboxed layout. Currently it is a mutant object with a
- // non-native group and native shape, so make it safe for GC by changing
- // its group to the default for its prototype.
- AutoEnterOOMUnsafeRegion oomUnsafe;
- ObjectGroup* plainGroup = ObjectGroup::defaultNewGroup(cx, &PlainObject::class_,
- group->proto());
- if (!plainGroup)
- oomUnsafe.crash("TypeNewScript::maybeAnalyze");
- templateObject_->setGroup(plainGroup);
- templateObject_ = nullptr;
-
- return true;
- }
-
if (prefixShape->slotSpan() == templateObject()->slotSpan()) {
// The definite properties analysis found exactly the properties that
// are held in common by the preliminary objects. No further analysis
@@ -3968,12 +3863,6 @@ TypeNewScript::rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* g
continue;
}
- if (thisv.toObject().is<UnboxedPlainObject>()) {
- AutoEnterOOMUnsafeRegion oomUnsafe;
- if (!UnboxedPlainObject::convertToNative(cx, &thisv.toObject()))
- oomUnsafe.crash("rollbackPartiallyInitializedObjects");
- }
-
// Found a matching frame.
RootedPlainObject obj(cx, &thisv.toObject().as<PlainObject>());
@@ -4167,12 +4056,6 @@ ConstraintTypeSet::sweep(Zone* zone, AutoClearTypeInferenceStateOnOOM& oom)
// Object sets containing objects with unknown properties might
// not be complete. Mark the type set as unknown, which it will
// be treated as during Ion compilation.
- //
- // Note that we don't have to do this when the type set might
- // be missing the native group corresponding to an unboxed
- // object group. In this case, the native group points to the
- // unboxed object group via its addendum, so as long as objects
- // with either group exist, neither group will be finalized.
flags |= TYPE_FLAG_ANYOBJECT;
clearObjects();
objectCount = 0;
@@ -4256,21 +4139,6 @@ ObjectGroup::sweep(AutoClearTypeInferenceStateOnOOM* oom)
Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
- if (maybeUnboxedLayout()) {
- // Remove unboxed layouts that are about to be finalized from the
- // compartment wide list while we are still on the main thread.
- ObjectGroup* group = this;
- if (IsAboutToBeFinalizedUnbarriered(&group))
- unboxedLayout().detachFromCompartment();
-
- if (unboxedLayout().newScript())
- unboxedLayout().newScript()->sweep();
-
- // Discard constructor code to avoid holding onto ExecutablePools.
- if (zone()->isGCCompacting())
- unboxedLayout().setConstructorCode(nullptr);
- }
-
if (maybePreliminaryObjects())
maybePreliminaryObjects()->sweep();
diff --git a/js/src/vm/TypeInference.h b/js/src/vm/TypeInference.h
index 04fed448c..94ce7e871 100644
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -814,8 +814,8 @@ class PreliminaryObjectArray
public:
PreliminaryObjectArray() = default;
- void registerNewObject(JSObject* res);
- void unregisterObject(JSObject* obj);
+ void registerNewObject(PlainObject* res);
+ void unregisterObject(PlainObject* obj);
JSObject* get(size_t i) const {
MOZ_ASSERT(i < COUNT);
diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp
index ae97be0de..8b0302917 100644
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -361,7 +361,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
return nullptr;
const Class* clasp = TypedArrayObject::protoClassForType(ArrayTypeID());
- return global->createBlankPrototypeInheriting(cx, clasp, typedArrayProto);
+ return GlobalObject::createBlankPrototypeInheriting(cx, global, clasp, typedArrayProto);
}
static JSObject*
@@ -1892,7 +1892,7 @@ DataViewObject::constructWrapped(JSContext* cx, HandleObject bufobj, const CallA
Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal());
if (!proto) {
- proto = global->getOrCreateDataViewPrototype(cx);
+ proto = GlobalObject::getOrCreateDataViewPrototype(cx, global);
if (!proto)
return false;
}
@@ -2892,12 +2892,13 @@ DataViewObject::initClass(JSContext* cx)
if (global->isStandardClassResolved(JSProto_DataView))
return true;
- RootedNativeObject proto(cx, global->createBlankPrototype(cx, &DataViewObject::protoClass));
+ RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global,
+ &DataViewObject::protoClass));
if (!proto)
return false;
- RootedFunction ctor(cx, global->createConstructor(cx, DataViewObject::class_constructor,
- cx->names().DataView, 3));
+ RootedFunction ctor(cx, GlobalObject::createConstructor(cx, DataViewObject::class_constructor,
+ cx->names().DataView, 3));
if (!ctor)
return false;
diff --git a/js/src/vm/UnboxedObject-inl.h b/js/src/vm/UnboxedObject-inl.h
deleted file mode 100644
index 93ad7bf28..000000000
--- a/js/src/vm/UnboxedObject-inl.h
+++ /dev/null
@@ -1,840 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef vm_UnboxedObject_inl_h
-#define vm_UnboxedObject_inl_h
-
-#include "vm/UnboxedObject.h"
-
-#include "gc/StoreBuffer-inl.h"
-#include "vm/ArrayObject-inl.h"
-#include "vm/NativeObject-inl.h"
-
-namespace js {
-
-static inline Value
-GetUnboxedValue(uint8_t* p, JSValueType type, bool maybeUninitialized)
-{
- switch (type) {
- case JSVAL_TYPE_BOOLEAN:
- return BooleanValue(*p != 0);
-
- case JSVAL_TYPE_INT32:
- return Int32Value(*reinterpret_cast<int32_t*>(p));
-
- case JSVAL_TYPE_DOUBLE: {
- // During unboxed plain object creation, non-GC thing properties are
- // left uninitialized. This is normally fine, since the properties will
- // be filled in shortly, but if they are read before that happens we
- // need to make sure that doubles are canonical.
- double d = *reinterpret_cast<double*>(p);
- if (maybeUninitialized)
- return DoubleValue(JS::CanonicalizeNaN(d));
- return DoubleValue(d);
- }
-
- case JSVAL_TYPE_STRING:
- return StringValue(*reinterpret_cast<JSString**>(p));
-
- case JSVAL_TYPE_OBJECT:
- return ObjectOrNullValue(*reinterpret_cast<JSObject**>(p));
-
- default:
- MOZ_CRASH("Invalid type for unboxed value");
- }
-}
-
-static inline void
-SetUnboxedValueNoTypeChange(JSObject* unboxedObject,
- uint8_t* p, JSValueType type, const Value& v,
- bool preBarrier)
-{
- switch (type) {
- case JSVAL_TYPE_BOOLEAN:
- *p = v.toBoolean();
- return;
-
- case JSVAL_TYPE_INT32:
- *reinterpret_cast<int32_t*>(p) = v.toInt32();
- return;
-
- case JSVAL_TYPE_DOUBLE:
- *reinterpret_cast<double*>(p) = v.toNumber();
- return;
-
- case JSVAL_TYPE_STRING: {
- MOZ_ASSERT(!IsInsideNursery(v.toString()));
- JSString** np = reinterpret_cast<JSString**>(p);
- if (preBarrier)
- JSString::writeBarrierPre(*np);
- *np = v.toString();
- return;
- }
-
- case JSVAL_TYPE_OBJECT: {
- JSObject** np = reinterpret_cast<JSObject**>(p);
-
- // Manually trigger post barriers on the whole object. If we treat
- // the pointer as a HeapPtrObject we will get confused later if the
- // object is converted to its native representation.
- JSObject* obj = v.toObjectOrNull();
- if (IsInsideNursery(obj) && !IsInsideNursery(unboxedObject)) {
- JSRuntime* rt = unboxedObject->runtimeFromMainThread();
- rt->gc.storeBuffer.putWholeCell(unboxedObject);
- }
-
- if (preBarrier)
- JSObject::writeBarrierPre(*np);
- *np = obj;
- return;
- }
-
- default:
- MOZ_CRASH("Invalid type for unboxed value");
- }
-}
-
-static inline bool
-SetUnboxedValue(ExclusiveContext* cx, JSObject* unboxedObject, jsid id,
- uint8_t* p, JSValueType type, const Value& v, bool preBarrier)
-{
- switch (type) {
- case JSVAL_TYPE_BOOLEAN:
- if (v.isBoolean()) {
- *p = v.toBoolean();
- return true;
- }
- return false;
-
- case JSVAL_TYPE_INT32:
- if (v.isInt32()) {
- *reinterpret_cast<int32_t*>(p) = v.toInt32();
- return true;
- }
- return false;
-
- case JSVAL_TYPE_DOUBLE:
- if (v.isNumber()) {
- *reinterpret_cast<double*>(p) = v.toNumber();
- return true;
- }
- return false;
-
- case JSVAL_TYPE_STRING:
- if (v.isString()) {
- MOZ_ASSERT(!IsInsideNursery(v.toString()));
- JSString** np = reinterpret_cast<JSString**>(p);
- if (preBarrier)
- JSString::writeBarrierPre(*np);
- *np = v.toString();
- return true;
- }
- return false;
-
- case JSVAL_TYPE_OBJECT:
- if (v.isObjectOrNull()) {
- JSObject** np = reinterpret_cast<JSObject**>(p);
-
- // Update property types when writing object properties. Types for
- // other properties were captured when the unboxed layout was
- // created.
- AddTypePropertyId(cx, unboxedObject, id, v);
-
- // As above, trigger post barriers on the whole object.
- JSObject* obj = v.toObjectOrNull();
- if (IsInsideNursery(v.toObjectOrNull()) && !IsInsideNursery(unboxedObject)) {
- JSRuntime* rt = unboxedObject->runtimeFromMainThread();
- rt->gc.storeBuffer.putWholeCell(unboxedObject);
- }
-
- if (preBarrier)
- JSObject::writeBarrierPre(*np);
- *np = obj;
- return true;
- }
- return false;
-
- default:
- MOZ_CRASH("Invalid type for unboxed value");
- }
-}
-
-/////////////////////////////////////////////////////////////////////
-// UnboxedPlainObject
-/////////////////////////////////////////////////////////////////////
-
-inline const UnboxedLayout&
-UnboxedPlainObject::layout() const
-{
- return group()->unboxedLayout();
-}
-
-/////////////////////////////////////////////////////////////////////
-// UnboxedArrayObject
-/////////////////////////////////////////////////////////////////////
-
-inline const UnboxedLayout&
-UnboxedArrayObject::layout() const
-{
- return group()->unboxedLayout();
-}
-
-inline void
-UnboxedArrayObject::setLength(ExclusiveContext* cx, uint32_t length)
-{
- if (length > INT32_MAX) {
- // Track objects with overflowing lengths in type information.
- MarkObjectGroupFlags(cx, this, OBJECT_FLAG_LENGTH_OVERFLOW);
- }
-
- length_ = length;
-}
-
-inline void
-UnboxedArrayObject::setInitializedLength(uint32_t initlen)
-{
- if (initlen < initializedLength()) {
- switch (elementType()) {
- case JSVAL_TYPE_STRING:
- for (size_t i = initlen; i < initializedLength(); i++)
- triggerPreBarrier<JSVAL_TYPE_STRING>(i);
- break;
- case JSVAL_TYPE_OBJECT:
- for (size_t i = initlen; i < initializedLength(); i++)
- triggerPreBarrier<JSVAL_TYPE_OBJECT>(i);
- break;
- default:
- MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(elementType()));
- }
- }
- setInitializedLengthNoBarrier(initlen);
-}
-
-template <JSValueType Type>
-inline bool
-UnboxedArrayObject::setElementSpecific(ExclusiveContext* cx, size_t index, const Value& v)
-{
- MOZ_ASSERT(index < initializedLength());
- MOZ_ASSERT(Type == elementType());
- uint8_t* p = elements() + index * UnboxedTypeSize(Type);
- return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ true);
-}
-
-template <JSValueType Type>
-inline void
-UnboxedArrayObject::setElementNoTypeChangeSpecific(size_t index, const Value& v)
-{
- MOZ_ASSERT(index < initializedLength());
- MOZ_ASSERT(Type == elementType());
- uint8_t* p = elements() + index * UnboxedTypeSize(Type);
- return SetUnboxedValueNoTypeChange(this, p, elementType(), v, /* preBarrier = */ true);
-}
-
-template <JSValueType Type>
-inline bool
-UnboxedArrayObject::initElementSpecific(ExclusiveContext* cx, size_t index, const Value& v)
-{
- MOZ_ASSERT(index < initializedLength());
- MOZ_ASSERT(Type == elementType());
- uint8_t* p = elements() + index * UnboxedTypeSize(Type);
- return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ false);
-}
-
-template <JSValueType Type>
-inline void
-UnboxedArrayObject::initElementNoTypeChangeSpecific(size_t index, const Value& v)
-{
- MOZ_ASSERT(index < initializedLength());
- MOZ_ASSERT(Type == elementType());
- uint8_t* p = elements() + index * UnboxedTypeSize(Type);
- return SetUnboxedValueNoTypeChange(this, p, elementType(), v, /* preBarrier = */ false);
-}
-
-template <JSValueType Type>
-inline Value
-UnboxedArrayObject::getElementSpecific(size_t index)
-{
- MOZ_ASSERT(index < initializedLength());
- MOZ_ASSERT(Type == elementType());
- uint8_t* p = elements() + index * UnboxedTypeSize(Type);
- return GetUnboxedValue(p, Type, /* maybeUninitialized = */ false);
-}
-
-template <JSValueType Type>
-inline void
-UnboxedArrayObject::triggerPreBarrier(size_t index)
-{
- MOZ_ASSERT(UnboxedTypeNeedsPreBarrier(Type));
-
- uint8_t* p = elements() + index * UnboxedTypeSize(Type);
-
- switch (Type) {
- case JSVAL_TYPE_STRING: {
- JSString** np = reinterpret_cast<JSString**>(p);
- JSString::writeBarrierPre(*np);
- break;
- }
-
- case JSVAL_TYPE_OBJECT: {
- JSObject** np = reinterpret_cast<JSObject**>(p);
- JSObject::writeBarrierPre(*np);
- break;
- }
-
- default:
- MOZ_CRASH("Bad type");
- }
-}
-
-/////////////////////////////////////////////////////////////////////
-// Combined methods for NativeObject and UnboxedArrayObject accesses.
-/////////////////////////////////////////////////////////////////////
-
-static inline bool
-HasAnyBoxedOrUnboxedDenseElements(JSObject* obj)
-{
- return obj->isNative() || obj->is<UnboxedArrayObject>();
-}
-
-static inline size_t
-GetAnyBoxedOrUnboxedInitializedLength(JSObject* obj)
-{
- if (obj->isNative())
- return obj->as<NativeObject>().getDenseInitializedLength();
- if (obj->is<UnboxedArrayObject>())
- return obj->as<UnboxedArrayObject>().initializedLength();
- return 0;
-}
-
-static inline size_t
-GetAnyBoxedOrUnboxedCapacity(JSObject* obj)
-{
- if (obj->isNative())
- return obj->as<NativeObject>().getDenseCapacity();
- if (obj->is<UnboxedArrayObject>())
- return obj->as<UnboxedArrayObject>().capacity();
- return 0;
-}
-
-static inline Value
-GetAnyBoxedOrUnboxedDenseElement(JSObject* obj, size_t index)
-{
- if (obj->isNative())
- return obj->as<NativeObject>().getDenseElement(index);
- return obj->as<UnboxedArrayObject>().getElement(index);
-}
-
-static inline size_t
-GetAnyBoxedOrUnboxedArrayLength(JSObject* obj)
-{
- if (obj->is<ArrayObject>())
- return obj->as<ArrayObject>().length();
- return obj->as<UnboxedArrayObject>().length();
-}
-
-static inline void
-SetAnyBoxedOrUnboxedArrayLength(JSContext* cx, JSObject* obj, size_t length)
-{
- if (obj->is<ArrayObject>()) {
- MOZ_ASSERT(length >= obj->as<ArrayObject>().length());
- obj->as<ArrayObject>().setLength(cx, length);
- } else {
- MOZ_ASSERT(length >= obj->as<UnboxedArrayObject>().length());
- obj->as<UnboxedArrayObject>().setLength(cx, length);
- }
-}
-
-static inline bool
-SetAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value)
-{
- if (obj->isNative()) {
- obj->as<NativeObject>().setDenseElementWithType(cx, index, value);
- return true;
- }
- return obj->as<UnboxedArrayObject>().setElement(cx, index, value);
-}
-
-static inline bool
-InitAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value)
-{
- if (obj->isNative()) {
- obj->as<NativeObject>().initDenseElementWithType(cx, index, value);
- return true;
- }
- return obj->as<UnboxedArrayObject>().initElement(cx, index, value);
-}
-
-/////////////////////////////////////////////////////////////////////
-// Template methods for NativeObject and UnboxedArrayObject accesses.
-/////////////////////////////////////////////////////////////////////
-
-static inline JSValueType
-GetBoxedOrUnboxedType(JSObject* obj)
-{
- if (obj->isNative())
- return JSVAL_TYPE_MAGIC;
- return obj->as<UnboxedArrayObject>().elementType();
-}
-
-template <JSValueType Type>
-static inline bool
-HasBoxedOrUnboxedDenseElements(JSObject* obj)
-{
- if (Type == JSVAL_TYPE_MAGIC)
- return obj->isNative();
- return obj->is<UnboxedArrayObject>() && obj->as<UnboxedArrayObject>().elementType() == Type;
-}
-
-template <JSValueType Type>
-static inline size_t
-GetBoxedOrUnboxedInitializedLength(JSObject* obj)
-{
- if (Type == JSVAL_TYPE_MAGIC)
- return obj->as<NativeObject>().getDenseInitializedLength();
- return obj->as<UnboxedArrayObject>().initializedLength();
-}
-
-template <JSValueType Type>
-static inline DenseElementResult
-SetBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen)
-{
- size_t oldInitlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
- if (Type == JSVAL_TYPE_MAGIC) {
- obj->as<NativeObject>().setDenseInitializedLength(initlen);
- if (initlen < oldInitlen)
- obj->as<NativeObject>().shrinkElements(cx, initlen);
- } else {
- obj->as<UnboxedArrayObject>().setInitializedLength(initlen);
- if (initlen < oldInitlen)
- obj->as<UnboxedArrayObject>().shrinkElements(cx, initlen);
- }
- return DenseElementResult::Success;
-}
-
-template <JSValueType Type>
-static inline size_t
-GetBoxedOrUnboxedCapacity(JSObject* obj)
-{
- if (Type == JSVAL_TYPE_MAGIC)
- return obj->as<NativeObject>().getDenseCapacity();
- return obj->as<UnboxedArrayObject>().capacity();
-}
-
-template <JSValueType Type>
-static inline Value
-GetBoxedOrUnboxedDenseElement(JSObject* obj, size_t index)
-{
- if (Type == JSVAL_TYPE_MAGIC)
- return obj->as<NativeObject>().getDenseElement(index);
- return obj->as<UnboxedArrayObject>().getElementSpecific<Type>(index);
-}
-
-template <JSValueType Type>
-static inline void
-SetBoxedOrUnboxedDenseElementNoTypeChange(JSObject* obj, size_t index, const Value& value)
-{
- if (Type == JSVAL_TYPE_MAGIC)
- obj->as<NativeObject>().setDenseElement(index, value);
- else
- obj->as<UnboxedArrayObject>().setElementNoTypeChangeSpecific<Type>(index, value);
-}
-
-template <JSValueType Type>
-static inline bool
-SetBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value)
-{
- if (Type == JSVAL_TYPE_MAGIC) {
- obj->as<NativeObject>().setDenseElementWithType(cx, index, value);
- return true;
- }
- return obj->as<UnboxedArrayObject>().setElementSpecific<Type>(cx, index, value);
-}
-
-template <JSValueType Type>
-static inline DenseElementResult
-EnsureBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count)
-{
- if (Type == JSVAL_TYPE_MAGIC) {
- if (!obj->as<ArrayObject>().ensureElements(cx, count))
- return DenseElementResult::Failure;
- } else {
- if (obj->as<UnboxedArrayObject>().capacity() < count) {
- if (!obj->as<UnboxedArrayObject>().growElements(cx, count))
- return DenseElementResult::Failure;
- }
- }
- return DenseElementResult::Success;
-}
-
-template <JSValueType Type>
-static inline DenseElementResult
-SetOrExtendBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj,
- uint32_t start, const Value* vp, uint32_t count,
- ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update)
-{
- if (Type == JSVAL_TYPE_MAGIC) {
- NativeObject* nobj = &obj->as<NativeObject>();
-
- if (nobj->denseElementsAreFrozen())
- return DenseElementResult::Incomplete;
-
- if (obj->is<ArrayObject>() &&
- !obj->as<ArrayObject>().lengthIsWritable() &&
- start + count >= obj->as<ArrayObject>().length())
- {
- return DenseElementResult::Incomplete;
- }
-
- DenseElementResult result = nobj->ensureDenseElements(cx, start, count);
- if (result != DenseElementResult::Success)
- return result;
-
- if (obj->is<ArrayObject>() && start + count >= obj->as<ArrayObject>().length())
- obj->as<ArrayObject>().setLengthInt32(start + count);
-
- if (updateTypes == ShouldUpdateTypes::DontUpdate && !nobj->shouldConvertDoubleElements()) {
- nobj->copyDenseElements(start, vp, count);
- } else {
- for (size_t i = 0; i < count; i++)
- nobj->setDenseElementWithType(cx, start + i, vp[i]);
- }
-
- return DenseElementResult::Success;
- }
-
- UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>();
-
- if (start > nobj->initializedLength())
- return DenseElementResult::Incomplete;
-
- if (start + count >= UnboxedArrayObject::MaximumCapacity)
- return DenseElementResult::Incomplete;
-
- if (start + count > nobj->capacity() && !nobj->growElements(cx, start + count))
- return DenseElementResult::Failure;
-
- size_t oldInitlen = nobj->initializedLength();
-
- // Overwrite any existing elements covered by the new range. If we fail
- // after this point due to some incompatible type being written to the
- // object's elements, afterwards the contents will be different from when
- // we started. The caller must retry the operation using a generic path,
- // which will overwrite the already-modified elements as well as the ones
- // that were left alone.
- size_t i = 0;
- if (updateTypes == ShouldUpdateTypes::DontUpdate) {
- for (size_t j = start; i < count && j < oldInitlen; i++, j++)
- nobj->setElementNoTypeChangeSpecific<Type>(j, vp[i]);
- } else {
- for (size_t j = start; i < count && j < oldInitlen; i++, j++) {
- if (!nobj->setElementSpecific<Type>(cx, j, vp[i]))
- return DenseElementResult::Incomplete;
- }
- }
-
- if (i != count) {
- obj->as<UnboxedArrayObject>().setInitializedLength(start + count);
- if (updateTypes == ShouldUpdateTypes::DontUpdate) {
- for (; i < count; i++)
- nobj->initElementNoTypeChangeSpecific<Type>(start + i, vp[i]);
- } else {
- for (; i < count; i++) {
- if (!nobj->initElementSpecific<Type>(cx, start + i, vp[i])) {
- nobj->setInitializedLengthNoBarrier(oldInitlen);
- return DenseElementResult::Incomplete;
- }
- }
- }
- }
-
- if (start + count >= nobj->length())
- nobj->setLength(cx, start + count);
-
- return DenseElementResult::Success;
-}
-
-template <JSValueType Type>
-static inline DenseElementResult
-MoveBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, uint32_t dstStart, uint32_t srcStart,
- uint32_t length)
-{
- MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(obj));
-
- if (Type == JSVAL_TYPE_MAGIC) {
- if (obj->as<NativeObject>().denseElementsAreFrozen())
- return DenseElementResult::Incomplete;
-
- if (!obj->as<NativeObject>().maybeCopyElementsForWrite(cx))
- return DenseElementResult::Failure;
- obj->as<NativeObject>().moveDenseElements(dstStart, srcStart, length);
- } else {
- uint8_t* data = obj->as<UnboxedArrayObject>().elements();
- size_t elementSize = UnboxedTypeSize(Type);
-
- if (UnboxedTypeNeedsPreBarrier(Type) &&
- JS::shadow::Zone::asShadowZone(obj->zone())->needsIncrementalBarrier())
- {
- // Trigger pre barriers on any elements we are overwriting. See
- // NativeObject::moveDenseElements. No post barrier is needed as
- // only whole cell post barriers are used with unboxed objects.
- for (size_t i = 0; i < length; i++)
- obj->as<UnboxedArrayObject>().triggerPreBarrier<Type>(dstStart + i);
- }
-
- memmove(data + dstStart * elementSize,
- data + srcStart * elementSize,
- length * elementSize);
- }
-
- return DenseElementResult::Success;
-}
-
-template <JSValueType DstType, JSValueType SrcType>
-static inline DenseElementResult
-CopyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
- uint32_t dstStart, uint32_t srcStart, uint32_t length)
-{
- MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<SrcType>(src));
- MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<DstType>(dst));
- MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<DstType>(dst) == dstStart);
- MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<SrcType>(src) >= srcStart + length);
- MOZ_ASSERT(GetBoxedOrUnboxedCapacity<DstType>(dst) >= dstStart + length);
-
- SetBoxedOrUnboxedInitializedLength<DstType>(cx, dst, dstStart + length);
-
- if (DstType == JSVAL_TYPE_MAGIC) {
- if (SrcType == JSVAL_TYPE_MAGIC) {
- const Value* vp = src->as<NativeObject>().getDenseElements() + srcStart;
- dst->as<NativeObject>().initDenseElements(dstStart, vp, length);
- } else {
- for (size_t i = 0; i < length; i++) {
- Value v = GetBoxedOrUnboxedDenseElement<SrcType>(src, srcStart + i);
- dst->as<NativeObject>().initDenseElement(dstStart + i, v);
- }
- }
- } else if (DstType == SrcType) {
- uint8_t* dstData = dst->as<UnboxedArrayObject>().elements();
- uint8_t* srcData = src->as<UnboxedArrayObject>().elements();
- size_t elementSize = UnboxedTypeSize(DstType);
-
- memcpy(dstData + dstStart * elementSize,
- srcData + srcStart * elementSize,
- length * elementSize);
-
- // Add a store buffer entry if we might have copied a nursery pointer to dst.
- if (UnboxedTypeNeedsPostBarrier(DstType) && !IsInsideNursery(dst))
- dst->runtimeFromMainThread()->gc.storeBuffer.putWholeCell(dst);
- } else if (DstType == JSVAL_TYPE_DOUBLE && SrcType == JSVAL_TYPE_INT32) {
- uint8_t* dstData = dst->as<UnboxedArrayObject>().elements();
- uint8_t* srcData = src->as<UnboxedArrayObject>().elements();
-
- for (size_t i = 0; i < length; i++) {
- int32_t v = *reinterpret_cast<int32_t*>(srcData + (srcStart + i) * sizeof(int32_t));
- *reinterpret_cast<double*>(dstData + (dstStart + i) * sizeof(double)) = v;
- }
- } else {
- for (size_t i = 0; i < length; i++) {
- Value v = GetBoxedOrUnboxedDenseElement<SrcType>(src, srcStart + i);
- dst->as<UnboxedArrayObject>().initElementNoTypeChangeSpecific<DstType>(dstStart + i, v);
- }
- }
-
- return DenseElementResult::Success;
-}
-
-/////////////////////////////////////////////////////////////////////
-// Dispatch to specialized methods based on the type of an object.
-/////////////////////////////////////////////////////////////////////
-
-// Goop to fix MSVC. See DispatchTraceKindTyped in TraceKind.h.
-// The clang-cl front end defines _MSC_VER, but still requires the explicit
-// template declaration, so we must test for __clang__ here as well.
-#if defined(_MSC_VER) && !defined(__clang__)
-# define DEPENDENT_TEMPLATE_HINT
-#else
-# define DEPENDENT_TEMPLATE_HINT template
-#endif
-
-// Function to dispatch a method specialized to whatever boxed or unboxed dense
-// elements which an input object has.
-template <typename F>
-DenseElementResult
-CallBoxedOrUnboxedSpecialization(F f, JSObject* obj)
-{
- if (!HasAnyBoxedOrUnboxedDenseElements(obj))
- return DenseElementResult::Incomplete;
- switch (GetBoxedOrUnboxedType(obj)) {
- case JSVAL_TYPE_MAGIC:
- return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_MAGIC>();
- case JSVAL_TYPE_BOOLEAN:
- return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_BOOLEAN>();
- case JSVAL_TYPE_INT32:
- return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_INT32>();
- case JSVAL_TYPE_DOUBLE:
- return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_DOUBLE>();
- case JSVAL_TYPE_STRING:
- return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_STRING>();
- case JSVAL_TYPE_OBJECT:
- return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_OBJECT>();
- default:
- MOZ_CRASH();
- }
-}
-
-// As above, except the specialization can reflect the unboxed type of two objects.
-template <typename F>
-DenseElementResult
-CallBoxedOrUnboxedSpecialization(F f, JSObject* obj1, JSObject* obj2)
-{
- if (!HasAnyBoxedOrUnboxedDenseElements(obj1) || !HasAnyBoxedOrUnboxedDenseElements(obj2))
- return DenseElementResult::Incomplete;
-
-#define SPECIALIZE_OBJ2(TYPE) \
- switch (GetBoxedOrUnboxedType(obj2)) { \
- case JSVAL_TYPE_MAGIC: \
- return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_MAGIC>(); \
- case JSVAL_TYPE_BOOLEAN: \
- return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_BOOLEAN>(); \
- case JSVAL_TYPE_INT32: \
- return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_INT32>(); \
- case JSVAL_TYPE_DOUBLE: \
- return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_DOUBLE>(); \
- case JSVAL_TYPE_STRING: \
- return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_STRING>(); \
- case JSVAL_TYPE_OBJECT: \
- return f. DEPENDENT_TEMPLATE_HINT operator()<TYPE, JSVAL_TYPE_OBJECT>(); \
- default: \
- MOZ_CRASH(); \
- }
-
- switch (GetBoxedOrUnboxedType(obj1)) {
- case JSVAL_TYPE_MAGIC:
- SPECIALIZE_OBJ2(JSVAL_TYPE_MAGIC)
- case JSVAL_TYPE_BOOLEAN:
- SPECIALIZE_OBJ2(JSVAL_TYPE_BOOLEAN)
- case JSVAL_TYPE_INT32:
- SPECIALIZE_OBJ2(JSVAL_TYPE_INT32)
- case JSVAL_TYPE_DOUBLE:
- SPECIALIZE_OBJ2(JSVAL_TYPE_DOUBLE)
- case JSVAL_TYPE_STRING:
- SPECIALIZE_OBJ2(JSVAL_TYPE_STRING)
- case JSVAL_TYPE_OBJECT:
- SPECIALIZE_OBJ2(JSVAL_TYPE_OBJECT)
- default:
- MOZ_CRASH();
- }
-
-#undef SPECIALIZE_OBJ2
-}
-
-#undef DEPENDENT_TEMPLATE_HINT
-
-#define DefineBoxedOrUnboxedFunctor1(Signature, A) \
-struct Signature ## Functor { \
- A a; \
- explicit Signature ## Functor(A a) \
- : a(a) \
- {} \
- template <JSValueType Type> \
- DenseElementResult operator()() { \
- return Signature<Type>(a); \
- } \
-}
-
-#define DefineBoxedOrUnboxedFunctor3(Signature, A, B, C) \
-struct Signature ## Functor { \
- A a; B b; C c; \
- Signature ## Functor(A a, B b, C c) \
- : a(a), b(b), c(c) \
- {} \
- template <JSValueType Type> \
- DenseElementResult operator()() { \
- return Signature<Type>(a, b, c); \
- } \
-}
-
-#define DefineBoxedOrUnboxedFunctor4(Signature, A, B, C, D) \
-struct Signature ## Functor { \
- A a; B b; C c; D d; \
- Signature ## Functor(A a, B b, C c, D d) \
- : a(a), b(b), c(c), d(d) \
- {} \
- template <JSValueType Type> \
- DenseElementResult operator()() { \
- return Signature<Type>(a, b, c, d); \
- } \
-}
-
-#define DefineBoxedOrUnboxedFunctorPair4(Signature, A, B, C, D) \
-struct Signature ## Functor { \
- A a; B b; C c; D d; \
- Signature ## Functor(A a, B b, C c, D d) \
- : a(a), b(b), c(c), d(d) \
- {} \
- template <JSValueType TypeOne, JSValueType TypeTwo> \
- DenseElementResult operator()() { \
- return Signature<TypeOne, TypeTwo>(a, b, c, d); \
- } \
-}
-
-#define DefineBoxedOrUnboxedFunctor5(Signature, A, B, C, D, E) \
-struct Signature ## Functor { \
- A a; B b; C c; D d; E e; \
- Signature ## Functor(A a, B b, C c, D d, E e) \
- : a(a), b(b), c(c), d(d), e(e) \
- {} \
- template <JSValueType Type> \
- DenseElementResult operator()() { \
- return Signature<Type>(a, b, c, d, e); \
- } \
-}
-
-#define DefineBoxedOrUnboxedFunctor6(Signature, A, B, C, D, E, F) \
-struct Signature ## Functor { \
- A a; B b; C c; D d; E e; F f; \
- Signature ## Functor(A a, B b, C c, D d, E e, F f) \
- : a(a), b(b), c(c), d(d), e(e), f(f) \
- {} \
- template <JSValueType Type> \
- DenseElementResult operator()() { \
- return Signature<Type>(a, b, c, d, e, f); \
- } \
-}
-
-#define DefineBoxedOrUnboxedFunctorPair6(Signature, A, B, C, D, E, F) \
-struct Signature ## Functor { \
- A a; B b; C c; D d; E e; F f; \
- Signature ## Functor(A a, B b, C c, D d, E e, F f) \
- : a(a), b(b), c(c), d(d), e(e), f(f) \
- {} \
- template <JSValueType TypeOne, JSValueType TypeTwo> \
- DenseElementResult operator()() { \
- return Signature<TypeOne, TypeTwo>(a, b, c, d, e, f); \
- } \
-}
-
-DenseElementResult
-SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj,
- uint32_t start, const Value* vp, uint32_t count,
- ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update);
-
-DenseElementResult
-MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
- uint32_t dstStart, uint32_t srcStart, uint32_t length);
-
-DenseElementResult
-CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
- uint32_t dstStart, uint32_t srcStart, uint32_t length);
-
-void
-SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen);
-
-DenseElementResult
-EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count);
-
-} // namespace js
-
-#endif // vm_UnboxedObject_inl_h
diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp
deleted file mode 100644
index d7ad91de4..000000000
--- a/js/src/vm/UnboxedObject.cpp
+++ /dev/null
@@ -1,1752 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "vm/UnboxedObject-inl.h"
-
-#include "jit/BaselineIC.h"
-#include "jit/ExecutableAllocator.h"
-#include "jit/JitCommon.h"
-#include "jit/Linker.h"
-
-#include "jsobjinlines.h"
-
-#include "gc/Nursery-inl.h"
-#include "jit/MacroAssembler-inl.h"
-#include "vm/Shape-inl.h"
-
-using mozilla::ArrayLength;
-using mozilla::DebugOnly;
-using mozilla::PodCopy;
-
-using namespace js;
-
-/////////////////////////////////////////////////////////////////////
-// UnboxedLayout
-/////////////////////////////////////////////////////////////////////
-
-void
-UnboxedLayout::trace(JSTracer* trc)
-{
- for (size_t i = 0; i < properties_.length(); i++)
- TraceManuallyBarrieredEdge(trc, &properties_[i].name, "unboxed_layout_name");
-
- if (newScript())
- newScript()->trace(trc);
-
- TraceNullableEdge(trc, &nativeGroup_, "unboxed_layout_nativeGroup");
- TraceNullableEdge(trc, &nativeShape_, "unboxed_layout_nativeShape");
- TraceNullableEdge(trc, &allocationScript_, "unboxed_layout_allocationScript");
- TraceNullableEdge(trc, &replacementGroup_, "unboxed_layout_replacementGroup");
- TraceNullableEdge(trc, &constructorCode_, "unboxed_layout_constructorCode");
-}
-
-size_t
-UnboxedLayout::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
-{
- return mallocSizeOf(this)
- + properties_.sizeOfExcludingThis(mallocSizeOf)
- + (newScript() ? newScript()->sizeOfIncludingThis(mallocSizeOf) : 0)
- + mallocSizeOf(traceList());
-}
-
-void
-UnboxedLayout::setNewScript(TypeNewScript* newScript, bool writeBarrier /* = true */)
-{
- if (newScript_ && writeBarrier)
- TypeNewScript::writeBarrierPre(newScript_);
- newScript_ = newScript;
-}
-
-// Constructor code returns a 0x1 value to indicate the constructor code should
-// be cleared.
-static const uintptr_t CLEAR_CONSTRUCTOR_CODE_TOKEN = 0x1;
-
-/* static */ bool
-UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group)
-{
- gc::AutoSuppressGC suppress(cx);
-
- using namespace jit;
-
- if (!cx->compartment()->ensureJitCompartmentExists(cx))
- return false;
-
- UnboxedLayout& layout = group->unboxedLayout();
- MOZ_ASSERT(!layout.constructorCode());
-
- UnboxedPlainObject* templateObject = UnboxedPlainObject::create(cx, group, TenuredObject);
- if (!templateObject)
- return false;
-
- JitContext jitContext(cx, nullptr);
-
- MacroAssembler masm;
-
- Register propertiesReg, newKindReg;
-#ifdef JS_CODEGEN_X86
- propertiesReg = eax;
- newKindReg = ecx;
- masm.loadPtr(Address(masm.getStackPointer(), sizeof(void*)), propertiesReg);
- masm.loadPtr(Address(masm.getStackPointer(), 2 * sizeof(void*)), newKindReg);
-#else
- propertiesReg = IntArgReg0;
- newKindReg = IntArgReg1;
-#endif
-
-#ifdef JS_CODEGEN_ARM64
- // ARM64 communicates stack address via sp, but uses a pseudo-sp for addressing.
- masm.initStackPtr();
-#endif
-
- MOZ_ASSERT(propertiesReg.volatile_());
- MOZ_ASSERT(newKindReg.volatile_());
-
- AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
- regs.take(propertiesReg);
- regs.take(newKindReg);
- Register object = regs.takeAny(), scratch1 = regs.takeAny(), scratch2 = regs.takeAny();
-
- LiveGeneralRegisterSet savedNonVolatileRegisters = SavedNonVolatileRegisters(regs);
- masm.PushRegsInMask(savedNonVolatileRegisters);
-
- // The scratch double register might be used by MacroAssembler methods.
- if (ScratchDoubleReg.volatile_())
- masm.push(ScratchDoubleReg);
-
- Label failure, tenuredObject, allocated;
- masm.branch32(Assembler::NotEqual, newKindReg, Imm32(GenericObject), &tenuredObject);
- masm.branchTest32(Assembler::NonZero, AbsoluteAddress(group->addressOfFlags()),
- Imm32(OBJECT_FLAG_PRE_TENURE), &tenuredObject);
-
- // Allocate an object in the nursery
- masm.createGCObject(object, scratch1, templateObject, gc::DefaultHeap, &failure,
- /* initFixedSlots = */ false);
-
- masm.jump(&allocated);
- masm.bind(&tenuredObject);
-
- // Allocate an object in the tenured heap.
- masm.createGCObject(object, scratch1, templateObject, gc::TenuredHeap, &failure,
- /* initFixedSlots = */ false);
-
- // If any of the properties being stored are in the nursery, add a store
- // buffer entry for the new object.
- Label postBarrier;
- for (size_t i = 0; i < layout.properties().length(); i++) {
- const UnboxedLayout::Property& property = layout.properties()[i];
- if (property.type == JSVAL_TYPE_OBJECT) {
- Address valueAddress(propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value));
- Label notObject;
- masm.branchTestObject(Assembler::NotEqual, valueAddress, &notObject);
- Register valueObject = masm.extractObject(valueAddress, scratch1);
- masm.branchPtrInNurseryChunk(Assembler::Equal, valueObject, scratch2, &postBarrier);
- masm.bind(&notObject);
- }
- }
-
- masm.jump(&allocated);
- masm.bind(&postBarrier);
-
- LiveGeneralRegisterSet liveVolatileRegisters;
- liveVolatileRegisters.add(propertiesReg);
- if (object.volatile_())
- liveVolatileRegisters.add(object);
- masm.PushRegsInMask(liveVolatileRegisters);
-
- masm.mov(ImmPtr(cx->runtime()), scratch1);
- masm.setupUnalignedABICall(scratch2);
- masm.passABIArg(scratch1);
- masm.passABIArg(object);
- masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier));
-
- masm.PopRegsInMask(liveVolatileRegisters);
-
- masm.bind(&allocated);
-
- ValueOperand valueOperand;
-#ifdef JS_NUNBOX32
- valueOperand = ValueOperand(scratch1, scratch2);
-#else
- valueOperand = ValueOperand(scratch1);
-#endif
-
- Label failureStoreOther, failureStoreObject;
-
- for (size_t i = 0; i < layout.properties().length(); i++) {
- const UnboxedLayout::Property& property = layout.properties()[i];
- Address valueAddress(propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value));
- Address targetAddress(object, UnboxedPlainObject::offsetOfData() + property.offset);
-
- masm.loadValue(valueAddress, valueOperand);
-
- if (property.type == JSVAL_TYPE_OBJECT) {
- HeapTypeSet* types = group->maybeGetProperty(IdToTypeId(NameToId(property.name)));
-
- Label notObject;
- masm.branchTestObject(Assembler::NotEqual, valueOperand,
- types->mightBeMIRType(MIRType::Null) ? &notObject : &failureStoreObject);
-
- Register payloadReg = masm.extractObject(valueOperand, scratch1);
-
- if (!types->hasType(TypeSet::AnyObjectType())) {
- Register scratch = (payloadReg == scratch1) ? scratch2 : scratch1;
- masm.guardObjectType(payloadReg, types, scratch, &failureStoreObject);
- }
-
- masm.storeUnboxedProperty(targetAddress, JSVAL_TYPE_OBJECT,
- TypedOrValueRegister(MIRType::Object,
- AnyRegister(payloadReg)), nullptr);
-
- if (notObject.used()) {
- Label done;
- masm.jump(&done);
- masm.bind(&notObject);
- masm.branchTestNull(Assembler::NotEqual, valueOperand, &failureStoreOther);
- masm.storeUnboxedProperty(targetAddress, JSVAL_TYPE_OBJECT, NullValue(), nullptr);
- masm.bind(&done);
- }
- } else {
- masm.storeUnboxedProperty(targetAddress, property.type,
- ConstantOrRegister(valueOperand), &failureStoreOther);
- }
- }
-
- Label done;
- masm.bind(&done);
-
- if (object != ReturnReg)
- masm.movePtr(object, ReturnReg);
-
- // Restore non-volatile registers which were saved on entry.
- if (ScratchDoubleReg.volatile_())
- masm.pop(ScratchDoubleReg);
- masm.PopRegsInMask(savedNonVolatileRegisters);
-
- masm.abiret();
-
- masm.bind(&failureStoreOther);
-
- // There was a failure while storing a value which cannot be stored at all
- // in the unboxed object. Initialize the object so it is safe for GC and
- // return null.
- masm.initUnboxedObjectContents(object, templateObject);
-
- masm.bind(&failure);
-
- masm.movePtr(ImmWord(0), object);
- masm.jump(&done);
-
- masm.bind(&failureStoreObject);
-
- // There was a failure while storing a value to an object slot of the
- // unboxed object. If the value is storable, the failure occurred due to
- // incomplete type information in the object, so return a token to trigger
- // regeneration of the jitcode after a new object is created in the VM.
- {
- Label isObject;
- masm.branchTestObject(Assembler::Equal, valueOperand, &isObject);
- masm.branchTestNull(Assembler::NotEqual, valueOperand, &failureStoreOther);
- masm.bind(&isObject);
- }
-
- // Initialize the object so it is safe for GC.
- masm.initUnboxedObjectContents(object, templateObject);
-
- masm.movePtr(ImmWord(CLEAR_CONSTRUCTOR_CODE_TOKEN), object);
- masm.jump(&done);
-
- Linker linker(masm);
- AutoFlushICache afc("UnboxedObject");
- JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
- if (!code)
- return false;
-
- layout.setConstructorCode(code);
- return true;
-}
-
-void
-UnboxedLayout::detachFromCompartment()
-{
- if (isInList())
- remove();
-}
-
-/////////////////////////////////////////////////////////////////////
-// UnboxedPlainObject
-/////////////////////////////////////////////////////////////////////
-
-bool
-UnboxedPlainObject::setValue(ExclusiveContext* cx, const UnboxedLayout::Property& property,
- const Value& v)
-{
- uint8_t* p = &data_[property.offset];
- return SetUnboxedValue(cx, this, NameToId(property.name), p, property.type, v,
- /* preBarrier = */ true);
-}
-
-Value
-UnboxedPlainObject::getValue(const UnboxedLayout::Property& property,
- bool maybeUninitialized /* = false */)
-{
- uint8_t* p = &data_[property.offset];
- return GetUnboxedValue(p, property.type, maybeUninitialized);
-}
-
-void
-UnboxedPlainObject::trace(JSTracer* trc, JSObject* obj)
-{
- if (obj->as<UnboxedPlainObject>().expando_) {
- TraceManuallyBarrieredEdge(trc,
- reinterpret_cast<NativeObject**>(&obj->as<UnboxedPlainObject>().expando_),
- "unboxed_expando");
- }
-
- const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layoutDontCheckGeneration();
- const int32_t* list = layout.traceList();
- if (!list)
- return;
-
- uint8_t* data = obj->as<UnboxedPlainObject>().data();
- while (*list != -1) {
- GCPtrString* heap = reinterpret_cast<GCPtrString*>(data + *list);
- TraceEdge(trc, heap, "unboxed_string");
- list++;
- }
- list++;
- while (*list != -1) {
- GCPtrObject* heap = reinterpret_cast<GCPtrObject*>(data + *list);
- TraceNullableEdge(trc, heap, "unboxed_object");
- list++;
- }
-
- // Unboxed objects don't have Values to trace.
- MOZ_ASSERT(*(list + 1) == -1);
-}
-
-/* static */ UnboxedExpandoObject*
-UnboxedPlainObject::ensureExpando(JSContext* cx, Handle<UnboxedPlainObject*> obj)
-{
- if (obj->expando_)
- return obj->expando_;
-
- UnboxedExpandoObject* expando =
- NewObjectWithGivenProto<UnboxedExpandoObject>(cx, nullptr, gc::AllocKind::OBJECT4);
- if (!expando)
- return nullptr;
-
- // Don't track property types for expando objects. This allows Baseline
- // and Ion AddSlot ICs to guard on the unboxed group without guarding on
- // the expando group.
- MarkObjectGroupUnknownProperties(cx, expando->group());
-
- // If the expando is tenured then the original object must also be tenured.
- // Otherwise barriers triggered on the original object for writes to the
- // expando (as can happen in the JIT) won't see the tenured->nursery edge.
- // See WholeCellEdges::mark.
- MOZ_ASSERT_IF(!IsInsideNursery(expando), !IsInsideNursery(obj));
-
- // As with setValue(), we need to manually trigger post barriers on the
- // whole object. If we treat the field as a GCPtrObject and later
- // convert the object to its native representation, we will end up with a
- // corrupted store buffer entry.
- if (IsInsideNursery(expando) && !IsInsideNursery(obj))
- cx->runtime()->gc.storeBuffer.putWholeCell(obj);
-
- obj->expando_ = expando;
- return expando;
-}
-
-bool
-UnboxedPlainObject::containsUnboxedOrExpandoProperty(ExclusiveContext* cx, jsid id) const
-{
- if (layout().lookup(id))
- return true;
-
- if (maybeExpando() && maybeExpando()->containsShapeOrElement(cx, id))
- return true;
-
- return false;
-}
-
-static bool
-PropagatePropertyTypes(JSContext* cx, jsid id, ObjectGroup* oldGroup, ObjectGroup* newGroup)
-{
- HeapTypeSet* typeProperty = oldGroup->maybeGetProperty(id);
- TypeSet::TypeList types;
- if (!typeProperty->enumerateTypes(&types)) {
- ReportOutOfMemory(cx);
- return false;
- }
- for (size_t j = 0; j < types.length(); j++)
- AddTypePropertyId(cx, newGroup, nullptr, id, types[j]);
- return true;
-}
-
-static PlainObject*
-MakeReplacementTemplateObject(JSContext* cx, HandleObjectGroup group, const UnboxedLayout &layout)
-{
- PlainObject* obj = NewObjectWithGroup<PlainObject>(cx, group, layout.getAllocKind(),
- TenuredObject);
- if (!obj)
- return nullptr;
-
- for (size_t i = 0; i < layout.properties().length(); i++) {
- const UnboxedLayout::Property& property = layout.properties()[i];
- if (!obj->addDataProperty(cx, NameToId(property.name), i, JSPROP_ENUMERATE))
- return nullptr;
- MOZ_ASSERT(obj->slotSpan() == i + 1);
- MOZ_ASSERT(!obj->inDictionaryMode());
- }
-
- return obj;
-}
-
-/* static */ bool
-UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group)
-{
- AutoEnterAnalysis enter(cx);
-
- UnboxedLayout& layout = group->unboxedLayout();
- Rooted<TaggedProto> proto(cx, group->proto());
-
- MOZ_ASSERT(!layout.nativeGroup());
-
- RootedObjectGroup replacementGroup(cx);
-
- const Class* clasp = layout.isArray() ? &ArrayObject::class_ : &PlainObject::class_;
-
- // Immediately clear any new script on the group. This is done by replacing
- // the existing new script with one for a replacement default new group.
- // This is done so that the size of the replacment group's objects is the
- // same as that for the unboxed group, so that we do not see polymorphic
- // slot accesses later on for sites that see converted objects from this
- // group and objects that were allocated using the replacement new group.
- if (layout.newScript()) {
- MOZ_ASSERT(!layout.isArray());
-
- replacementGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto);
- if (!replacementGroup)
- return false;
-
- PlainObject* templateObject = MakeReplacementTemplateObject(cx, replacementGroup, layout);
- if (!templateObject)
- return false;
-
- TypeNewScript* replacementNewScript =
- TypeNewScript::makeNativeVersion(cx, layout.newScript(), templateObject);
- if (!replacementNewScript)
- return false;
-
- replacementGroup->setNewScript(replacementNewScript);
- gc::TraceTypeNewScript(replacementGroup);
-
- group->clearNewScript(cx, replacementGroup);
- }
-
- // Similarly, if this group is keyed to an allocation site, replace its
- // entry with a new group that has no unboxed layout.
- if (layout.allocationScript()) {
- RootedScript script(cx, layout.allocationScript());
- jsbytecode* pc = layout.allocationPc();
-
- replacementGroup = ObjectGroupCompartment::makeGroup(cx, clasp, proto);
- if (!replacementGroup)
- return false;
-
- PlainObject* templateObject = &script->getObject(pc)->as<PlainObject>();
- replacementGroup->addDefiniteProperties(cx, templateObject->lastProperty());
-
- JSProtoKey key = layout.isArray() ? JSProto_Array : JSProto_Object;
- cx->compartment()->objectGroups.replaceAllocationSiteGroup(script, pc, key,
- replacementGroup);
-
- // Clear any baseline information at this opcode which might use the old group.
- if (script->hasBaselineScript()) {
- jit::ICEntry& entry = script->baselineScript()->icEntryFromPCOffset(script->pcToOffset(pc));
- jit::ICFallbackStub* fallback = entry.fallbackStub();
- for (jit::ICStubIterator iter = fallback->beginChain(); !iter.atEnd(); iter++)
- iter.unlink(cx);
- if (fallback->isNewObject_Fallback())
- fallback->toNewObject_Fallback()->setTemplateObject(nullptr);
- else if (fallback->isNewArray_Fallback())
- fallback->toNewArray_Fallback()->setTemplateGroup(replacementGroup);
- }
- }
-
- size_t nfixed = layout.isArray() ? 0 : gc::GetGCKindSlots(layout.getAllocKind());
-
- if (layout.isArray()) {
- // The length shape to use for arrays is cached via a modified initial
- // shape for array objects. Create an array now to make sure this entry
- // is instantiated.
- if (!NewDenseEmptyArray(cx))
- return false;
- }
-
- RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, proto, nfixed, 0));
- if (!shape)
- return false;
-
- MOZ_ASSERT_IF(layout.isArray(), !shape->isEmptyShape() && shape->slotSpan() == 0);
-
- // Add shapes for each property, if this is for a plain object.
- for (size_t i = 0; i < layout.properties().length(); i++) {
- const UnboxedLayout::Property& property = layout.properties()[i];
-
- Rooted<StackShape> child(cx, StackShape(shape->base()->unowned(), NameToId(property.name),
- i, JSPROP_ENUMERATE, 0));
- shape = cx->zone()->propertyTree.getChild(cx, shape, child);
- if (!shape)
- return false;
- }
-
- ObjectGroup* nativeGroup =
- ObjectGroupCompartment::makeGroup(cx, clasp, proto,
- group->flags() & OBJECT_FLAG_DYNAMIC_MASK);
- if (!nativeGroup)
- return false;
-
- // No sense propagating if we don't know what we started with.
- if (!group->unknownProperties()) {
- // Propagate all property types from the old group to the new group.
- if (layout.isArray()) {
- if (!PropagatePropertyTypes(cx, JSID_VOID, group, nativeGroup))
- return false;
- } else {
- for (size_t i = 0; i < layout.properties().length(); i++) {
- const UnboxedLayout::Property& property = layout.properties()[i];
- jsid id = NameToId(property.name);
- if (!PropagatePropertyTypes(cx, id, group, nativeGroup))
- return false;
-
- // If we are OOM we may not be able to propagate properties.
- if (nativeGroup->unknownProperties())
- break;
-
- HeapTypeSet* nativeProperty = nativeGroup->maybeGetProperty(id);
- if (nativeProperty && nativeProperty->canSetDefinite(i))
- nativeProperty->setDefinite(i);
- }
- }
- } else {
- // If we skip, though, the new group had better agree.
- MOZ_ASSERT(nativeGroup->unknownProperties());
- }
-
- layout.nativeGroup_ = nativeGroup;
- layout.nativeShape_ = shape;
- layout.replacementGroup_ = replacementGroup;
-
- nativeGroup->setOriginalUnboxedGroup(group);
-
- group->markStateChange(cx);
-
- return true;
-}
-
-/* static */ bool
-UnboxedPlainObject::convertToNative(JSContext* cx, JSObject* obj)
-{
- const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
- UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
-
- if (!layout.nativeGroup()) {
- if (!UnboxedLayout::makeNativeGroup(cx, obj->group()))
- return false;
-
- // makeNativeGroup can reentrantly invoke this method.
- if (obj->is<PlainObject>())
- return true;
- }
-
- AutoValueVector values(cx);
- for (size_t i = 0; i < layout.properties().length(); i++) {
- // We might be reading properties off the object which have not been
- // initialized yet. Make sure any double values we read here are
- // canonicalized.
- if (!values.append(obj->as<UnboxedPlainObject>().getValue(layout.properties()[i], true)))
- return false;
- }
-
- // We are eliminating the expando edge with the conversion, so trigger a
- // pre barrier.
- JSObject::writeBarrierPre(expando);
-
- // Additionally trigger a post barrier on the expando itself. Whole cell
- // store buffer entries can be added on the original unboxed object for
- // writes to the expando (see WholeCellEdges::trace), so after conversion
- // we need to make sure the expando itself will still be traced.
- if (expando && !IsInsideNursery(expando))
- cx->runtime()->gc.storeBuffer.putWholeCell(expando);
-
- obj->setGroup(layout.nativeGroup());
- obj->as<PlainObject>().setLastPropertyMakeNative(cx, layout.nativeShape());
-
- for (size_t i = 0; i < values.length(); i++)
- obj->as<PlainObject>().initSlotUnchecked(i, values[i]);
-
- if (expando) {
- // Add properties from the expando object to the object, in order.
- // Suppress GC here, so that callers don't need to worry about this
- // method collecting. The stuff below can only fail due to OOM, in
- // which case the object will not have been completely filled back in.
- gc::AutoSuppressGC suppress(cx);
-
- Vector<jsid> ids(cx);
- for (Shape::Range<NoGC> r(expando->lastProperty()); !r.empty(); r.popFront()) {
- if (!ids.append(r.front().propid()))
- return false;
- }
- for (size_t i = 0; i < expando->getDenseInitializedLength(); i++) {
- if (!expando->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE)) {
- if (!ids.append(INT_TO_JSID(i)))
- return false;
- }
- }
- ::Reverse(ids.begin(), ids.end());
-
- RootedPlainObject nobj(cx, &obj->as<PlainObject>());
- Rooted<UnboxedExpandoObject*> nexpando(cx, expando);
- RootedId id(cx);
- Rooted<PropertyDescriptor> desc(cx);
- for (size_t i = 0; i < ids.length(); i++) {
- id = ids[i];
- if (!GetOwnPropertyDescriptor(cx, nexpando, id, &desc))
- return false;
- ObjectOpResult result;
- if (!DefineProperty(cx, nobj, id, desc, result))
- return false;
- MOZ_ASSERT(result.ok());
- }
- }
-
- return true;
-}
-
-/* static */
-UnboxedPlainObject*
-UnboxedPlainObject::create(ExclusiveContext* cx, HandleObjectGroup group, NewObjectKind newKind)
-{
- AutoSetNewObjectMetadata metadata(cx);
-
- MOZ_ASSERT(group->clasp() == &class_);
- gc::AllocKind allocKind = group->unboxedLayout().getAllocKind();
-
- UnboxedPlainObject* res =
- NewObjectWithGroup<UnboxedPlainObject>(cx, group, allocKind, newKind);
- if (!res)
- return nullptr;
-
- // Overwrite the dummy shape which was written to the object's expando field.
- res->initExpando();
-
- // Initialize reference fields of the object. All fields in the object will
- // be overwritten shortly, but references need to be safe for the GC.
- const int32_t* list = res->layout().traceList();
- if (list) {
- uint8_t* data = res->data();
- while (*list != -1) {
- GCPtrString* heap = reinterpret_cast<GCPtrString*>(data + *list);
- heap->init(cx->names().empty);
- list++;
- }
- list++;
- while (*list != -1) {
- GCPtrObject* heap = reinterpret_cast<GCPtrObject*>(data + *list);
- heap->init(nullptr);
- list++;
- }
- // Unboxed objects don't have Values to initialize.
- MOZ_ASSERT(*(list + 1) == -1);
- }
-
- return res;
-}
-
-/* static */ JSObject*
-UnboxedPlainObject::createWithProperties(ExclusiveContext* cx, HandleObjectGroup group,
- NewObjectKind newKind, IdValuePair* properties)
-{
- MOZ_ASSERT(newKind == GenericObject || newKind == TenuredObject);
-
- UnboxedLayout& layout = group->unboxedLayout();
-
- if (layout.constructorCode()) {
- MOZ_ASSERT(cx->isJSContext());
-
- typedef JSObject* (*ConstructorCodeSignature)(IdValuePair*, NewObjectKind);
- ConstructorCodeSignature function =
- reinterpret_cast<ConstructorCodeSignature>(layout.constructorCode()->raw());
-
- JSObject* obj;
- {
- JS::AutoSuppressGCAnalysis nogc;
- obj = reinterpret_cast<JSObject*>(CALL_GENERATED_2(function, properties, newKind));
- }
- if (obj > reinterpret_cast<JSObject*>(CLEAR_CONSTRUCTOR_CODE_TOKEN))
- return obj;
-
- if (obj == reinterpret_cast<JSObject*>(CLEAR_CONSTRUCTOR_CODE_TOKEN))
- layout.setConstructorCode(nullptr);
- }
-
- UnboxedPlainObject* obj = UnboxedPlainObject::create(cx, group, newKind);
- if (!obj)
- return nullptr;
-
- for (size_t i = 0; i < layout.properties().length(); i++) {
- if (!obj->setValue(cx, layout.properties()[i], properties[i].value))
- return NewPlainObjectWithProperties(cx, properties, layout.properties().length(), newKind);
- }
-
-#ifndef JS_CODEGEN_NONE
- if (cx->isJSContext() &&
- !group->unknownProperties() &&
- !layout.constructorCode() &&
- cx->asJSContext()->runtime()->jitSupportsFloatingPoint &&
- jit::CanLikelyAllocateMoreExecutableMemory())
- {
- if (!UnboxedLayout::makeConstructorCode(cx->asJSContext(), group))
- return nullptr;
- }
-#endif
-
- return obj;
-}
-
-/* static */ bool
-UnboxedPlainObject::obj_lookupProperty(JSContext* cx, HandleObject obj,
- HandleId id, MutableHandleObject objp,
- MutableHandleShape propp)
-{
- if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
- MarkNonNativePropertyFound<CanGC>(propp);
- objp.set(obj);
- return true;
- }
-
- RootedObject proto(cx, obj->staticPrototype());
- if (!proto) {
- objp.set(nullptr);
- propp.set(nullptr);
- return true;
- }
-
- return LookupProperty(cx, proto, id, objp, propp);
-}
-
-/* static */ bool
-UnboxedPlainObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
- Handle<PropertyDescriptor> desc,
- ObjectOpResult& result)
-{
- const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
-
- if (const UnboxedLayout::Property* property = layout.lookup(id)) {
- if (!desc.getter() && !desc.setter() && desc.attributes() == JSPROP_ENUMERATE) {
- // This define is equivalent to setting an existing property.
- if (obj->as<UnboxedPlainObject>().setValue(cx, *property, desc.value()))
- return result.succeed();
- }
-
- // Trying to incompatibly redefine an existing property requires the
- // object to be converted to a native object.
- if (!convertToNative(cx, obj))
- return false;
-
- return DefineProperty(cx, obj, id, desc, result);
- }
-
- // Define the property on the expando object.
- Rooted<UnboxedExpandoObject*> expando(cx, ensureExpando(cx, obj.as<UnboxedPlainObject>()));
- if (!expando)
- return false;
-
- // Update property types on the unboxed object as well.
- AddTypePropertyId(cx, obj, id, desc.value());
-
- return DefineProperty(cx, expando, id, desc, result);
-}
-
-/* static */ bool
-UnboxedPlainObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
-{
- if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
- *foundp = true;
- return true;
- }
-
- RootedObject proto(cx, obj->staticPrototype());
- if (!proto) {
- *foundp = false;
- return true;
- }
-
- return HasProperty(cx, proto, id, foundp);
-}
-
-/* static */ bool
-UnboxedPlainObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
- HandleId id, MutableHandleValue vp)
-{
- const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
-
- if (const UnboxedLayout::Property* property = layout.lookup(id)) {
- vp.set(obj->as<UnboxedPlainObject>().getValue(*property));
- return true;
- }
-
- if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
- if (expando->containsShapeOrElement(cx, id)) {
- RootedObject nexpando(cx, expando);
- return GetProperty(cx, nexpando, receiver, id, vp);
- }
- }
-
- RootedObject proto(cx, obj->staticPrototype());
- if (!proto) {
- vp.setUndefined();
- return true;
- }
-
- return GetProperty(cx, proto, receiver, id, vp);
-}
-
-/* static */ bool
-UnboxedPlainObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
- HandleValue receiver, ObjectOpResult& result)
-{
- const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
-
- if (const UnboxedLayout::Property* property = layout.lookup(id)) {
- if (receiver.isObject() && obj == &receiver.toObject()) {
- if (obj->as<UnboxedPlainObject>().setValue(cx, *property, v))
- return result.succeed();
-
- if (!convertToNative(cx, obj))
- return false;
- return SetProperty(cx, obj, id, v, receiver, result);
- }
-
- return SetPropertyByDefining(cx, id, v, receiver, result);
- }
-
- if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
- if (expando->containsShapeOrElement(cx, id)) {
- // Update property types on the unboxed object as well.
- AddTypePropertyId(cx, obj, id, v);
-
- RootedObject nexpando(cx, expando);
- return SetProperty(cx, nexpando, id, v, receiver, result);
- }
- }
-
- return SetPropertyOnProto(cx, obj, id, v, receiver, result);
-}
-
-/* static */ bool
-UnboxedPlainObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
- MutableHandle<PropertyDescriptor> desc)
-{
- const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
-
- if (const UnboxedLayout::Property* property = layout.lookup(id)) {
- desc.value().set(obj->as<UnboxedPlainObject>().getValue(*property));
- desc.setAttributes(JSPROP_ENUMERATE);
- desc.object().set(obj);
- return true;
- }
-
- if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
- if (expando->containsShapeOrElement(cx, id)) {
- RootedObject nexpando(cx, expando);
- if (!GetOwnPropertyDescriptor(cx, nexpando, id, desc))
- return false;
- if (desc.object() == nexpando)
- desc.object().set(obj);
- return true;
- }
- }
-
- desc.object().set(nullptr);
- return true;
-}
-
-/* static */ bool
-UnboxedPlainObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
- ObjectOpResult& result)
-{
- if (!convertToNative(cx, obj))
- return false;
- return DeleteProperty(cx, obj, id, result);
-}
-
-/* static */ bool
-UnboxedPlainObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable)
-{
- if (!convertToNative(cx, obj))
- return false;
- return WatchProperty(cx, obj, id, callable);
-}
-
-/* static */ bool
-UnboxedPlainObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
- bool enumerableOnly)
-{
- // Ignore expando properties here, they are special-cased by the property
- // enumeration code.
-
- const UnboxedLayout::PropertyVector& unboxed = obj->as<UnboxedPlainObject>().layout().properties();
- for (size_t i = 0; i < unboxed.length(); i++) {
- if (!properties.append(NameToId(unboxed[i].name)))
- return false;
- }
-
- return true;
-}
-
-const Class UnboxedExpandoObject::class_ = {
- "UnboxedExpandoObject",
- 0
-};
-
-static const ClassOps UnboxedPlainObjectClassOps = {
- nullptr, /* addProperty */
- nullptr, /* delProperty */
- nullptr, /* getProperty */
- nullptr, /* setProperty */
- nullptr, /* enumerate */
- nullptr, /* resolve */
- nullptr, /* mayResolve */
- nullptr, /* finalize */
- nullptr, /* call */
- nullptr, /* hasInstance */
- nullptr, /* construct */
- UnboxedPlainObject::trace,
-};
-
-static const ObjectOps UnboxedPlainObjectObjectOps = {
- UnboxedPlainObject::obj_lookupProperty,
- UnboxedPlainObject::obj_defineProperty,
- UnboxedPlainObject::obj_hasProperty,
- UnboxedPlainObject::obj_getProperty,
- UnboxedPlainObject::obj_setProperty,
- UnboxedPlainObject::obj_getOwnPropertyDescriptor,
- UnboxedPlainObject::obj_deleteProperty,
- UnboxedPlainObject::obj_watch,
- nullptr, /* No unwatch needed, as watch() converts the object to native */
- nullptr, /* getElements */
- UnboxedPlainObject::obj_enumerate,
- nullptr /* funToString */
-};
-
-const Class UnboxedPlainObject::class_ = {
- js_Object_str,
- Class::NON_NATIVE |
- JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
- JSCLASS_DELAY_METADATA_BUILDER,
- &UnboxedPlainObjectClassOps,
- JS_NULL_CLASS_SPEC,
- JS_NULL_CLASS_EXT,
- &UnboxedPlainObjectObjectOps
-};
-
-/////////////////////////////////////////////////////////////////////
-// UnboxedArrayObject
-/////////////////////////////////////////////////////////////////////
-
-template <JSValueType Type>
-DenseElementResult
-AppendUnboxedDenseElements(UnboxedArrayObject* obj, uint32_t initlen,
- MutableHandle<GCVector<Value>> values)
-{
- for (size_t i = 0; i < initlen; i++)
- values.infallibleAppend(obj->getElementSpecific<Type>(i));
- return DenseElementResult::Success;
-}
-
-DefineBoxedOrUnboxedFunctor3(AppendUnboxedDenseElements,
- UnboxedArrayObject*, uint32_t, MutableHandle<GCVector<Value>>);
-
-/* static */ bool
-UnboxedArrayObject::convertToNativeWithGroup(ExclusiveContext* cx, JSObject* obj,
- ObjectGroup* group, Shape* shape)
-{
- size_t length = obj->as<UnboxedArrayObject>().length();
- size_t initlen = obj->as<UnboxedArrayObject>().initializedLength();
-
- Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
- if (!values.reserve(initlen))
- return false;
-
- AppendUnboxedDenseElementsFunctor functor(&obj->as<UnboxedArrayObject>(), initlen, &values);
- DebugOnly<DenseElementResult> result = CallBoxedOrUnboxedSpecialization(functor, obj);
- MOZ_ASSERT(result.value == DenseElementResult::Success);
-
- obj->setGroup(group);
-
- ArrayObject* aobj = &obj->as<ArrayObject>();
- aobj->setLastPropertyMakeNative(cx, shape);
-
- // Make sure there is at least one element, so that this array does not
- // use emptyObjectElements / emptyObjectElementsShared.
- if (!aobj->ensureElements(cx, Max<size_t>(initlen, 1)))
- return false;
-
- MOZ_ASSERT(!aobj->getDenseInitializedLength());
- aobj->setDenseInitializedLength(initlen);
- aobj->initDenseElements(0, values.begin(), initlen);
- aobj->setLengthInt32(length);
-
- return true;
-}
-
-/* static */ bool
-UnboxedArrayObject::convertToNative(JSContext* cx, JSObject* obj)
-{
- const UnboxedLayout& layout = obj->as<UnboxedArrayObject>().layout();
-
- if (!layout.nativeGroup()) {
- if (!UnboxedLayout::makeNativeGroup(cx, obj->group()))
- return false;
- }
-
- return convertToNativeWithGroup(cx, obj, layout.nativeGroup(), layout.nativeShape());
-}
-
-bool
-UnboxedArrayObject::convertInt32ToDouble(ExclusiveContext* cx, ObjectGroup* group)
-{
- MOZ_ASSERT(elementType() == JSVAL_TYPE_INT32);
- MOZ_ASSERT(group->unboxedLayout().elementType() == JSVAL_TYPE_DOUBLE);
-
- Vector<int32_t> values(cx);
- if (!values.reserve(initializedLength()))
- return false;
- for (size_t i = 0; i < initializedLength(); i++)
- values.infallibleAppend(getElementSpecific<JSVAL_TYPE_INT32>(i).toInt32());
-
- uint8_t* newElements;
- if (hasInlineElements()) {
- newElements = AllocateObjectBuffer<uint8_t>(cx, this, capacity() * sizeof(double));
- } else {
- newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(),
- capacity() * sizeof(int32_t),
- capacity() * sizeof(double));
- }
- if (!newElements)
- return false;
-
- setGroup(group);
- elements_ = newElements;
-
- for (size_t i = 0; i < initializedLength(); i++)
- setElementNoTypeChangeSpecific<JSVAL_TYPE_DOUBLE>(i, DoubleValue(values[i]));
-
- return true;
-}
-
-/* static */ UnboxedArrayObject*
-UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32_t length,
- NewObjectKind newKind, uint32_t maxLength)
-{
- MOZ_ASSERT(length <= MaximumCapacity);
-
- MOZ_ASSERT(group->clasp() == &class_);
- uint32_t elementSize = UnboxedTypeSize(group->unboxedLayout().elementType());
- uint32_t capacity = Min(length, maxLength);
- uint32_t nbytes = offsetOfInlineElements() + elementSize * capacity;
-
- UnboxedArrayObject* res;
- if (nbytes <= JSObject::MAX_BYTE_SIZE) {
- gc::AllocKind allocKind = gc::GetGCObjectKindForBytes(nbytes);
-
- // If there was no provided length information, pick an allocation kind
- // to accommodate small arrays (as is done for normal native arrays).
- if (capacity == 0)
- allocKind = gc::AllocKind::OBJECT8;
-
- res = NewObjectWithGroup<UnboxedArrayObject>(cx, group, allocKind, newKind);
- if (!res)
- return nullptr;
- res->setInitializedLengthNoBarrier(0);
- res->setInlineElements();
-
- size_t actualCapacity = (GetGCKindBytes(allocKind) - offsetOfInlineElements()) / elementSize;
- MOZ_ASSERT(actualCapacity >= capacity);
- res->setCapacityIndex(exactCapacityIndex(actualCapacity));
- } else {
- res = NewObjectWithGroup<UnboxedArrayObject>(cx, group, gc::AllocKind::OBJECT0, newKind);
- if (!res)
- return nullptr;
- res->setInitializedLengthNoBarrier(0);
-
- uint32_t capacityIndex = (capacity == length)
- ? CapacityMatchesLengthIndex
- : chooseCapacityIndex(capacity, length);
- uint32_t actualCapacity = computeCapacity(capacityIndex, length);
-
- res->elements_ = AllocateObjectBuffer<uint8_t>(cx, res, actualCapacity * elementSize);
- if (!res->elements_) {
- // Make the object safe for GC.
- res->setInlineElements();
- return nullptr;
- }
-
- res->setCapacityIndex(capacityIndex);
- }
-
- res->setLength(cx, length);
- return res;
-}
-
-bool
-UnboxedArrayObject::setElement(ExclusiveContext* cx, size_t index, const Value& v)
-{
- MOZ_ASSERT(index < initializedLength());
- uint8_t* p = elements() + index * elementSize();
- return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ true);
-}
-
-bool
-UnboxedArrayObject::initElement(ExclusiveContext* cx, size_t index, const Value& v)
-{
- MOZ_ASSERT(index < initializedLength());
- uint8_t* p = elements() + index * elementSize();
- return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ false);
-}
-
-void
-UnboxedArrayObject::initElementNoTypeChange(size_t index, const Value& v)
-{
- MOZ_ASSERT(index < initializedLength());
- uint8_t* p = elements() + index * elementSize();
- if (UnboxedTypeNeedsPreBarrier(elementType()))
- *reinterpret_cast<void**>(p) = nullptr;
- SetUnboxedValueNoTypeChange(this, p, elementType(), v, /* preBarrier = */ false);
-}
-
-Value
-UnboxedArrayObject::getElement(size_t index)
-{
- MOZ_ASSERT(index < initializedLength());
- uint8_t* p = elements() + index * elementSize();
- return GetUnboxedValue(p, elementType(), /* maybeUninitialized = */ false);
-}
-
-/* static */ void
-UnboxedArrayObject::trace(JSTracer* trc, JSObject* obj)
-{
- JSValueType type = obj->as<UnboxedArrayObject>().elementType();
- if (!UnboxedTypeNeedsPreBarrier(type))
- return;
-
- MOZ_ASSERT(obj->as<UnboxedArrayObject>().elementSize() == sizeof(uintptr_t));
- size_t initlen = obj->as<UnboxedArrayObject>().initializedLength();
- void** elements = reinterpret_cast<void**>(obj->as<UnboxedArrayObject>().elements());
-
- switch (type) {
- case JSVAL_TYPE_OBJECT:
- for (size_t i = 0; i < initlen; i++) {
- GCPtrObject* heap = reinterpret_cast<GCPtrObject*>(elements + i);
- TraceNullableEdge(trc, heap, "unboxed_object");
- }
- break;
-
- case JSVAL_TYPE_STRING:
- for (size_t i = 0; i < initlen; i++) {
- GCPtrString* heap = reinterpret_cast<GCPtrString*>(elements + i);
- TraceEdge(trc, heap, "unboxed_string");
- }
- break;
-
- default:
- MOZ_CRASH();
- }
-}
-
-/* static */ void
-UnboxedArrayObject::objectMoved(JSObject* obj, const JSObject* old)
-{
- UnboxedArrayObject& dst = obj->as<UnboxedArrayObject>();
- const UnboxedArrayObject& src = old->as<UnboxedArrayObject>();
-
- // Fix up possible inline data pointer.
- if (src.hasInlineElements())
- dst.setInlineElements();
-}
-
-/* static */ void
-UnboxedArrayObject::finalize(FreeOp* fop, JSObject* obj)
-{
- MOZ_ASSERT(!IsInsideNursery(obj));
- if (!obj->as<UnboxedArrayObject>().hasInlineElements())
- js_free(obj->as<UnboxedArrayObject>().elements());
-}
-
-/* static */ size_t
-UnboxedArrayObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src,
- gc::AllocKind allocKind)
-{
- UnboxedArrayObject* ndst = &dst->as<UnboxedArrayObject>();
- UnboxedArrayObject* nsrc = &src->as<UnboxedArrayObject>();
- MOZ_ASSERT(ndst->elements() == nsrc->elements());
-
- Nursery& nursery = trc->runtime()->gc.nursery;
-
- if (!nursery.isInside(nsrc->elements())) {
- nursery.removeMallocedBuffer(nsrc->elements());
- return 0;
- }
-
- // Determine if we can use inline data for the target array. If this is
- // possible, the nursery will have picked an allocation size that is large
- // enough.
- size_t nbytes = nsrc->capacity() * nsrc->elementSize();
- if (offsetOfInlineElements() + nbytes <= GetGCKindBytes(allocKind)) {
- ndst->setInlineElements();
- } else {
- MOZ_ASSERT(allocKind == gc::AllocKind::OBJECT0);
-
- AutoEnterOOMUnsafeRegion oomUnsafe;
- uint8_t* data = nsrc->zone()->pod_malloc<uint8_t>(nbytes);
- if (!data)
- oomUnsafe.crash("Failed to allocate unboxed array elements while tenuring.");
- ndst->elements_ = data;
- }
-
- PodCopy(ndst->elements(), nsrc->elements(), nsrc->initializedLength() * nsrc->elementSize());
-
- // Set a forwarding pointer for the element buffers in case they were
- // preserved on the stack by Ion.
- bool direct = nsrc->capacity() * nsrc->elementSize() >= sizeof(uintptr_t);
- nursery.maybeSetForwardingPointer(trc, nsrc->elements(), ndst->elements(), direct);
-
- return ndst->hasInlineElements() ? 0 : nbytes;
-}
-
-// Possible capacities for unboxed arrays. Some of these capacities might seem
-// a little weird, but were chosen to allow the inline data of objects of each
-// size to be fully utilized for arrays of the various types on both 32 bit and
-// 64 bit platforms.
-//
-// To find the possible inline capacities, the following script was used:
-//
-// var fixedSlotCapacities = [0, 2, 4, 8, 12, 16];
-// var dataSizes = [1, 4, 8];
-// var header32 = 4 * 2 + 4 * 2;
-// var header64 = 8 * 2 + 4 * 2;
-//
-// for (var i = 0; i < fixedSlotCapacities.length; i++) {
-// var nfixed = fixedSlotCapacities[i];
-// var size32 = 4 * 4 + 8 * nfixed - header32;
-// var size64 = 8 * 4 + 8 * nfixed - header64;
-// for (var j = 0; j < dataSizes.length; j++) {
-// print(size32 / dataSizes[j]);
-// print(size64 / dataSizes[j]);
-// }
-// }
-//
-/* static */ const uint32_t
-UnboxedArrayObject::CapacityArray[] = {
- UINT32_MAX, // For CapacityMatchesLengthIndex.
- 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 13, 16, 17, 18, 24, 26, 32, 34, 40, 64, 72, 96, 104, 128, 136,
- 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288,
- 1048576, 2097152, 3145728, 4194304, 5242880, 6291456, 7340032, 8388608, 9437184, 11534336,
- 13631488, 15728640, 17825792, 20971520, 24117248, 27262976, 31457280, 35651584, 40894464,
- 46137344, 52428800, 59768832, MaximumCapacity
-};
-
-static const uint32_t
-Pow2CapacityIndexes[] = {
- 2, // 1
- 3, // 2
- 5, // 4
- 8, // 8
- 13, // 16
- 18, // 32
- 21, // 64
- 25, // 128
- 27, // 256
- 28, // 512
- 29, // 1024
- 30, // 2048
- 31, // 4096
- 32, // 8192
- 33, // 16384
- 34, // 32768
- 35, // 65536
- 36, // 131072
- 37, // 262144
- 38, // 524288
- 39 // 1048576
-};
-
-static const uint32_t MebiCapacityIndex = 39;
-
-/* static */ uint32_t
-UnboxedArrayObject::chooseCapacityIndex(uint32_t capacity, uint32_t length)
-{
- // Note: the structure and behavior of this method follow along with
- // NativeObject::goodAllocated. Changes to the allocation strategy in one
- // should generally be matched by the other.
-
- // Make sure we have enough space to store all possible values for the capacity index.
- // This ought to be a static_assert, but MSVC doesn't like that.
- MOZ_ASSERT(mozilla::ArrayLength(CapacityArray) - 1 <= (CapacityMask >> CapacityShift));
-
- // The caller should have ensured the capacity is possible for an unboxed array.
- MOZ_ASSERT(capacity <= MaximumCapacity);
-
- static const uint32_t Mebi = 1024 * 1024;
-
- if (capacity <= Mebi) {
- capacity = mozilla::RoundUpPow2(capacity);
-
- // When the required capacity is close to the array length, then round
- // up to the array length itself, as for NativeObject.
- if (length >= capacity && capacity > (length / 3) * 2)
- return CapacityMatchesLengthIndex;
-
- if (capacity < MinimumDynamicCapacity)
- capacity = MinimumDynamicCapacity;
-
- uint32_t bit = mozilla::FloorLog2Size(capacity);
- MOZ_ASSERT(capacity == uint32_t(1 << bit));
- MOZ_ASSERT(bit <= 20);
- MOZ_ASSERT(mozilla::ArrayLength(Pow2CapacityIndexes) == 21);
-
- uint32_t index = Pow2CapacityIndexes[bit];
- MOZ_ASSERT(CapacityArray[index] == capacity);
-
- return index;
- }
-
- MOZ_ASSERT(CapacityArray[MebiCapacityIndex] == Mebi);
-
- for (uint32_t i = MebiCapacityIndex + 1;; i++) {
- if (CapacityArray[i] >= capacity)
- return i;
- }
-
- MOZ_CRASH("Invalid capacity");
-}
-
-/* static */ uint32_t
-UnboxedArrayObject::exactCapacityIndex(uint32_t capacity)
-{
- for (size_t i = CapacityMatchesLengthIndex + 1; i < ArrayLength(CapacityArray); i++) {
- if (CapacityArray[i] == capacity)
- return i;
- }
- MOZ_CRASH();
-}
-
-bool
-UnboxedArrayObject::growElements(ExclusiveContext* cx, size_t cap)
-{
- // The caller should have checked if this capacity is possible for an
- // unboxed array, so the only way this call can fail is from OOM.
- MOZ_ASSERT(cap <= MaximumCapacity);
-
- uint32_t oldCapacity = capacity();
- uint32_t newCapacityIndex = chooseCapacityIndex(cap, length());
- uint32_t newCapacity = computeCapacity(newCapacityIndex, length());
-
- MOZ_ASSERT(oldCapacity < cap);
- MOZ_ASSERT(cap <= newCapacity);
-
- // The allocation size computation below cannot have integer overflows.
- JS_STATIC_ASSERT(MaximumCapacity < UINT32_MAX / sizeof(double));
-
- uint8_t* newElements;
- if (hasInlineElements()) {
- newElements = AllocateObjectBuffer<uint8_t>(cx, this, newCapacity * elementSize());
- if (!newElements)
- return false;
- js_memcpy(newElements, elements(), initializedLength() * elementSize());
- } else {
- newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(),
- oldCapacity * elementSize(),
- newCapacity * elementSize());
- if (!newElements)
- return false;
- }
-
- elements_ = newElements;
- setCapacityIndex(newCapacityIndex);
-
- return true;
-}
-
-void
-UnboxedArrayObject::shrinkElements(ExclusiveContext* cx, size_t cap)
-{
- if (hasInlineElements())
- return;
-
- uint32_t oldCapacity = capacity();
- uint32_t newCapacityIndex = chooseCapacityIndex(cap, 0);
- uint32_t newCapacity = computeCapacity(newCapacityIndex, 0);
-
- MOZ_ASSERT(cap < oldCapacity);
- MOZ_ASSERT(cap <= newCapacity);
-
- if (newCapacity >= oldCapacity)
- return;
-
- uint8_t* newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(),
- oldCapacity * elementSize(),
- newCapacity * elementSize());
- if (!newElements)
- return;
-
- elements_ = newElements;
- setCapacityIndex(newCapacityIndex);
-}
-
-bool
-UnboxedArrayObject::containsProperty(ExclusiveContext* cx, jsid id)
-{
- if (JSID_IS_INT(id) && uint32_t(JSID_TO_INT(id)) < initializedLength())
- return true;
- if (JSID_IS_ATOM(id) && JSID_TO_ATOM(id) == cx->names().length)
- return true;
- return false;
-}
-
-/* static */ bool
-UnboxedArrayObject::obj_lookupProperty(JSContext* cx, HandleObject obj,
- HandleId id, MutableHandleObject objp,
- MutableHandleShape propp)
-{
- if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
- MarkNonNativePropertyFound<CanGC>(propp);
- objp.set(obj);
- return true;
- }
-
- RootedObject proto(cx, obj->staticPrototype());
- if (!proto) {
- objp.set(nullptr);
- propp.set(nullptr);
- return true;
- }
-
- return LookupProperty(cx, proto, id, objp, propp);
-}
-
-/* static */ bool
-UnboxedArrayObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
- Handle<PropertyDescriptor> desc,
- ObjectOpResult& result)
-{
- if (JSID_IS_INT(id) && !desc.getter() && !desc.setter() && desc.attributes() == JSPROP_ENUMERATE) {
- UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>();
-
- uint32_t index = JSID_TO_INT(id);
- if (index < nobj->initializedLength()) {
- if (nobj->setElement(cx, index, desc.value()))
- return result.succeed();
- } else if (index == nobj->initializedLength() && index < MaximumCapacity) {
- if (nobj->initializedLength() == nobj->capacity()) {
- if (!nobj->growElements(cx, index + 1))
- return false;
- }
- nobj->setInitializedLength(index + 1);
- if (nobj->initElement(cx, index, desc.value())) {
- if (nobj->length() <= index)
- nobj->setLengthInt32(index + 1);
- return result.succeed();
- }
- nobj->setInitializedLengthNoBarrier(index);
- }
- }
-
- if (!convertToNative(cx, obj))
- return false;
-
- return DefineProperty(cx, obj, id, desc, result);
-}
-
-/* static */ bool
-UnboxedArrayObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
-{
- if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
- *foundp = true;
- return true;
- }
-
- RootedObject proto(cx, obj->staticPrototype());
- if (!proto) {
- *foundp = false;
- return true;
- }
-
- return HasProperty(cx, proto, id, foundp);
-}
-
-/* static */ bool
-UnboxedArrayObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
- HandleId id, MutableHandleValue vp)
-{
- if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
- if (JSID_IS_INT(id))
- vp.set(obj->as<UnboxedArrayObject>().getElement(JSID_TO_INT(id)));
- else
- vp.set(Int32Value(obj->as<UnboxedArrayObject>().length()));
- return true;
- }
-
- RootedObject proto(cx, obj->staticPrototype());
- if (!proto) {
- vp.setUndefined();
- return true;
- }
-
- return GetProperty(cx, proto, receiver, id, vp);
-}
-
-/* static */ bool
-UnboxedArrayObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
- HandleValue receiver, ObjectOpResult& result)
-{
- if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
- if (receiver.isObject() && obj == &receiver.toObject()) {
- if (JSID_IS_INT(id)) {
- if (obj->as<UnboxedArrayObject>().setElement(cx, JSID_TO_INT(id), v))
- return result.succeed();
- } else {
- uint32_t len;
- if (!CanonicalizeArrayLengthValue(cx, v, &len))
- return false;
- UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>();
- if (len < nobj->initializedLength()) {
- nobj->setInitializedLength(len);
- nobj->shrinkElements(cx, len);
- }
- nobj->setLength(cx, len);
- return result.succeed();
- }
-
- if (!convertToNative(cx, obj))
- return false;
- return SetProperty(cx, obj, id, v, receiver, result);
- }
-
- return SetPropertyByDefining(cx, id, v, receiver, result);
- }
-
- return SetPropertyOnProto(cx, obj, id, v, receiver, result);
-}
-
-/* static */ bool
-UnboxedArrayObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
- MutableHandle<PropertyDescriptor> desc)
-{
- if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
- if (JSID_IS_INT(id)) {
- desc.value().set(obj->as<UnboxedArrayObject>().getElement(JSID_TO_INT(id)));
- desc.setAttributes(JSPROP_ENUMERATE);
- } else {
- desc.value().set(Int32Value(obj->as<UnboxedArrayObject>().length()));
- desc.setAttributes(JSPROP_PERMANENT);
- }
- desc.object().set(obj);
- return true;
- }
-
- desc.object().set(nullptr);
- return true;
-}
-
-/* static */ bool
-UnboxedArrayObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
- ObjectOpResult& result)
-{
- if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
- size_t initlen = obj->as<UnboxedArrayObject>().initializedLength();
- if (JSID_IS_INT(id) && JSID_TO_INT(id) == int32_t(initlen - 1)) {
- obj->as<UnboxedArrayObject>().setInitializedLength(initlen - 1);
- obj->as<UnboxedArrayObject>().shrinkElements(cx, initlen - 1);
- return result.succeed();
- }
- }
-
- if (!convertToNative(cx, obj))
- return false;
- return DeleteProperty(cx, obj, id, result);
-}
-
-/* static */ bool
-UnboxedArrayObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable)
-{
- if (!convertToNative(cx, obj))
- return false;
- return WatchProperty(cx, obj, id, callable);
-}
-
-/* static */ bool
-UnboxedArrayObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
- bool enumerableOnly)
-{
- for (size_t i = 0; i < obj->as<UnboxedArrayObject>().initializedLength(); i++) {
- if (!properties.append(INT_TO_JSID(i)))
- return false;
- }
-
- if (!enumerableOnly && !properties.append(NameToId(cx->names().length)))
- return false;
-
- return true;
-}
-
-static const ClassOps UnboxedArrayObjectClassOps = {
- nullptr, /* addProperty */
- nullptr, /* delProperty */
- nullptr, /* getProperty */
- nullptr, /* setProperty */
- nullptr, /* enumerate */
- nullptr, /* resolve */
- nullptr, /* mayResolve */
- UnboxedArrayObject::finalize,
- nullptr, /* call */
- nullptr, /* hasInstance */
- nullptr, /* construct */
- UnboxedArrayObject::trace,
-};
-
-static const ClassExtension UnboxedArrayObjectClassExtension = {
- nullptr, /* weakmapKeyDelegateOp */
- UnboxedArrayObject::objectMoved
-};
-
-static const ObjectOps UnboxedArrayObjectObjectOps = {
- UnboxedArrayObject::obj_lookupProperty,
- UnboxedArrayObject::obj_defineProperty,
- UnboxedArrayObject::obj_hasProperty,
- UnboxedArrayObject::obj_getProperty,
- UnboxedArrayObject::obj_setProperty,
- UnboxedArrayObject::obj_getOwnPropertyDescriptor,
- UnboxedArrayObject::obj_deleteProperty,
- UnboxedArrayObject::obj_watch,
- nullptr, /* No unwatch needed, as watch() converts the object to native */
- nullptr, /* getElements */
- UnboxedArrayObject::obj_enumerate,
- nullptr /* funToString */
-};
-
-const Class UnboxedArrayObject::class_ = {
- "Array",
- Class::NON_NATIVE |
- JSCLASS_SKIP_NURSERY_FINALIZE |
- JSCLASS_BACKGROUND_FINALIZE,
- &UnboxedArrayObjectClassOps,
- JS_NULL_CLASS_SPEC,
- &UnboxedArrayObjectClassExtension,
- &UnboxedArrayObjectObjectOps
-};
-
-/////////////////////////////////////////////////////////////////////
-// API
-/////////////////////////////////////////////////////////////////////
-
-static inline Value
-NextValue(Handle<GCVector<Value>> values, size_t* valueCursor)
-{
- return values[(*valueCursor)++];
-}
-
-void
-UnboxedArrayObject::fillAfterConvert(ExclusiveContext* cx,
- Handle<GCVector<Value>> values, size_t* valueCursor)
-{
- MOZ_ASSERT(CapacityArray[1] == 0);
- setCapacityIndex(1);
- setInitializedLengthNoBarrier(0);
- setInlineElements();
-
- setLength(cx, NextValue(values, valueCursor).toInt32());
-
- int32_t initlen = NextValue(values, valueCursor).toInt32();
- if (!initlen)
- return;
-
- AutoEnterOOMUnsafeRegion oomUnsafe;
- if (!growElements(cx, initlen))
- oomUnsafe.crash("UnboxedArrayObject::fillAfterConvert");
-
- setInitializedLength(initlen);
-
- for (size_t i = 0; i < size_t(initlen); i++)
- JS_ALWAYS_TRUE(initElement(cx, i, NextValue(values, valueCursor)));
-}
-
-void
-UnboxedPlainObject::fillAfterConvert(ExclusiveContext* cx,
- Handle<GCVector<Value>> values, size_t* valueCursor)
-{
- initExpando();
- memset(data(), 0, layout().size());
- for (size_t i = 0; i < layout().properties().length(); i++)
- JS_ALWAYS_TRUE(setValue(cx, layout().properties()[i], NextValue(values, valueCursor)));
-}
-
-DefineBoxedOrUnboxedFunctor6(SetOrExtendBoxedOrUnboxedDenseElements,
- ExclusiveContext*, JSObject*, uint32_t, const Value*, uint32_t,
- ShouldUpdateTypes);
-
-DenseElementResult
-js::SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj,
- uint32_t start, const Value* vp, uint32_t count,
- ShouldUpdateTypes updateTypes)
-{
- SetOrExtendBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, start, vp, count, updateTypes);
- return CallBoxedOrUnboxedSpecialization(functor, obj);
-};
-
-DefineBoxedOrUnboxedFunctor5(MoveBoxedOrUnboxedDenseElements,
- JSContext*, JSObject*, uint32_t, uint32_t, uint32_t);
-
-DenseElementResult
-js::MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
- uint32_t dstStart, uint32_t srcStart, uint32_t length)
-{
- MoveBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, dstStart, srcStart, length);
- return CallBoxedOrUnboxedSpecialization(functor, obj);
-}
-
-DefineBoxedOrUnboxedFunctorPair6(CopyBoxedOrUnboxedDenseElements,
- JSContext*, JSObject*, JSObject*, uint32_t, uint32_t, uint32_t);
-
-DenseElementResult
-js::CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
- uint32_t dstStart, uint32_t srcStart, uint32_t length)
-{
- CopyBoxedOrUnboxedDenseElementsFunctor functor(cx, dst, src, dstStart, srcStart, length);
- return CallBoxedOrUnboxedSpecialization(functor, dst, src);
-}
-
-DefineBoxedOrUnboxedFunctor3(SetBoxedOrUnboxedInitializedLength,
- JSContext*, JSObject*, size_t);
-
-void
-js::SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen)
-{
- SetBoxedOrUnboxedInitializedLengthFunctor functor(cx, obj, initlen);
- JS_ALWAYS_TRUE(CallBoxedOrUnboxedSpecialization(functor, obj) == DenseElementResult::Success);
-}
-
-DefineBoxedOrUnboxedFunctor3(EnsureBoxedOrUnboxedDenseElements,
- JSContext*, JSObject*, size_t);
-
-DenseElementResult
-js::EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t initlen)
-{
- EnsureBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, initlen);
- return CallBoxedOrUnboxedSpecialization(functor, obj);
-}
diff --git a/js/src/vm/UnboxedObject.h b/js/src/vm/UnboxedObject.h
deleted file mode 100644
index 779dd14c7..000000000
--- a/js/src/vm/UnboxedObject.h
+++ /dev/null
@@ -1,524 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef vm_UnboxedObject_h
-#define vm_UnboxedObject_h
-
-#include "jsgc.h"
-#include "jsobj.h"
-
-#include "vm/Runtime.h"
-#include "vm/TypeInference.h"
-
-namespace js {
-
-// Memory required for an unboxed value of a given type. Returns zero for types
-// which can't be used for unboxed objects.
-static inline size_t
-UnboxedTypeSize(JSValueType type)
-{
- switch (type) {
- case JSVAL_TYPE_BOOLEAN: return 1;
- case JSVAL_TYPE_INT32: return 4;
- case JSVAL_TYPE_DOUBLE: return 8;
- case JSVAL_TYPE_STRING: return sizeof(void*);
- case JSVAL_TYPE_OBJECT: return sizeof(void*);
- default: return 0;
- }
-}
-
-static inline bool
-UnboxedTypeNeedsPreBarrier(JSValueType type)
-{
- return type == JSVAL_TYPE_STRING || type == JSVAL_TYPE_OBJECT;
-}
-
-static inline bool
-UnboxedTypeNeedsPostBarrier(JSValueType type)
-{
- return type == JSVAL_TYPE_OBJECT;
-}
-
-// Class tracking information specific to unboxed objects.
-class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
-{
- public:
- struct Property {
- PropertyName* name;
- uint32_t offset;
- JSValueType type;
-
- Property()
- : name(nullptr), offset(UINT32_MAX), type(JSVAL_TYPE_MAGIC)
- {}
- };
-
- typedef Vector<Property, 0, SystemAllocPolicy> PropertyVector;
-
- private:
- // If objects in this group have ever been converted to native objects,
- // these store the corresponding native group and initial shape for such
- // objects. Type information for this object is reflected in nativeGroup.
- GCPtrObjectGroup nativeGroup_;
- GCPtrShape nativeShape_;
-
- // Any script/pc which the associated group is created for.
- GCPtrScript allocationScript_;
- jsbytecode* allocationPc_;
-
- // If nativeGroup is set and this object originally had a TypeNewScript or
- // was keyed to an allocation site, this points to the group which replaced
- // this one. This link is only needed to keep the replacement group from
- // being GC'ed. If it were GC'ed and a new one regenerated later, that new
- // group might have a different allocation kind from this group.
- GCPtrObjectGroup replacementGroup_;
-
- // The following members are only used for unboxed plain objects.
-
- // All properties on objects with this layout, in enumeration order.
- PropertyVector properties_;
-
- // Byte size of the data for objects with this layout.
- size_t size_;
-
- // Any 'new' script information associated with this layout.
- TypeNewScript* newScript_;
-
- // List for use in tracing objects with this layout. This has the same
- // structure as the trace list on a TypeDescr.
- int32_t* traceList_;
-
- // If this layout has been used to construct script or JSON constant
- // objects, this code might be filled in to more quickly fill in objects
- // from an array of values.
- GCPtrJitCode constructorCode_;
-
- // The following members are only used for unboxed arrays.
-
- // The type of array elements.
- JSValueType elementType_;
-
- public:
- UnboxedLayout()
- : nativeGroup_(nullptr), nativeShape_(nullptr),
- allocationScript_(nullptr), allocationPc_(nullptr), replacementGroup_(nullptr),
- size_(0), newScript_(nullptr), traceList_(nullptr), constructorCode_(nullptr),
- elementType_(JSVAL_TYPE_MAGIC)
- {}
-
- bool initProperties(const PropertyVector& properties, size_t size) {
- size_ = size;
- return properties_.appendAll(properties);
- }
-
- void initArray(JSValueType elementType) {
- elementType_ = elementType;
- }
-
- ~UnboxedLayout() {
- if (newScript_)
- newScript_->clear();
- js_delete(newScript_);
- js_free(traceList_);
-
- nativeGroup_.init(nullptr);
- nativeShape_.init(nullptr);
- replacementGroup_.init(nullptr);
- constructorCode_.init(nullptr);
- }
-
- bool isArray() const {
- return elementType_ != JSVAL_TYPE_MAGIC;
- }
-
- void detachFromCompartment();
-
- const PropertyVector& properties() const {
- return properties_;
- }
-
- TypeNewScript* newScript() const {
- return newScript_;
- }
-
- void setNewScript(TypeNewScript* newScript, bool writeBarrier = true);
-
- JSScript* allocationScript() const {
- return allocationScript_;
- }
-
- jsbytecode* allocationPc() const {
- return allocationPc_;
- }
-
- void setAllocationSite(JSScript* script, jsbytecode* pc) {
- allocationScript_ = script;
- allocationPc_ = pc;
- }
-
- const int32_t* traceList() const {
- return traceList_;
- }
-
- void setTraceList(int32_t* traceList) {
- traceList_ = traceList;
- }
-
- const Property* lookup(JSAtom* atom) const {
- for (size_t i = 0; i < properties_.length(); i++) {
- if (properties_[i].name == atom)
- return &properties_[i];
- }
- return nullptr;
- }
-
- const Property* lookup(jsid id) const {
- if (JSID_IS_STRING(id))
- return lookup(JSID_TO_ATOM(id));
- return nullptr;
- }
-
- size_t size() const {
- return size_;
- }
-
- ObjectGroup* nativeGroup() const {
- return nativeGroup_;
- }
-
- Shape* nativeShape() const {
- return nativeShape_;
- }
-
- jit::JitCode* constructorCode() const {
- return constructorCode_;
- }
-
- void setConstructorCode(jit::JitCode* code) {
- constructorCode_ = code;
- }
-
- JSValueType elementType() const {
- return elementType_;
- }
-
- inline gc::AllocKind getAllocKind() const;
-
- void trace(JSTracer* trc);
-
- size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
-
- static bool makeNativeGroup(JSContext* cx, ObjectGroup* group);
- static bool makeConstructorCode(JSContext* cx, HandleObjectGroup group);
-};
-
-// Class for expando objects holding extra properties given to an unboxed plain
-// object. These objects behave identically to normal native plain objects, and
-// have a separate Class to distinguish them for memory usage reporting.
-class UnboxedExpandoObject : public NativeObject
-{
- public:
- static const Class class_;
-};
-
-// Class for a plain object using an unboxed representation. The physical
-// layout of these objects is identical to that of an InlineTypedObject, though
-// these objects use an UnboxedLayout instead of a TypeDescr to keep track of
-// how their properties are stored.
-class UnboxedPlainObject : public JSObject
-{
- // Optional object which stores extra properties on this object. This is
- // not automatically barriered to avoid problems if the object is converted
- // to a native. See ensureExpando().
- UnboxedExpandoObject* expando_;
-
- // Start of the inline data, which immediately follows the group and extra properties.
- uint8_t data_[1];
-
- public:
- static const Class class_;
-
- static bool obj_lookupProperty(JSContext* cx, HandleObject obj,
- HandleId id, MutableHandleObject objp,
- MutableHandleShape propp);
-
- static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
- Handle<PropertyDescriptor> desc,
- ObjectOpResult& result);
-
- static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp);
-
- static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
- HandleId id, MutableHandleValue vp);
-
- static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
- HandleValue receiver, ObjectOpResult& result);
-
- static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
- MutableHandle<PropertyDescriptor> desc);
-
- static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
- ObjectOpResult& result);
-
- static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
- bool enumerableOnly);
- static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable);
-
- inline const UnboxedLayout& layout() const;
-
- const UnboxedLayout& layoutDontCheckGeneration() const {
- return group()->unboxedLayoutDontCheckGeneration();
- }
-
- uint8_t* data() {
- return &data_[0];
- }
-
- UnboxedExpandoObject* maybeExpando() const {
- return expando_;
- }
-
- void initExpando() {
- expando_ = nullptr;
- }
-
- // For use during GC.
- JSObject** addressOfExpando() {
- return reinterpret_cast<JSObject**>(&expando_);
- }
-
- bool containsUnboxedOrExpandoProperty(ExclusiveContext* cx, jsid id) const;
-
- static UnboxedExpandoObject* ensureExpando(JSContext* cx, Handle<UnboxedPlainObject*> obj);
-
- bool setValue(ExclusiveContext* cx, const UnboxedLayout::Property& property, const Value& v);
- Value getValue(const UnboxedLayout::Property& property, bool maybeUninitialized = false);
-
- static bool convertToNative(JSContext* cx, JSObject* obj);
- static UnboxedPlainObject* create(ExclusiveContext* cx, HandleObjectGroup group,
- NewObjectKind newKind);
- static JSObject* createWithProperties(ExclusiveContext* cx, HandleObjectGroup group,
- NewObjectKind newKind, IdValuePair* properties);
-
- void fillAfterConvert(ExclusiveContext* cx,
- Handle<GCVector<Value>> values, size_t* valueCursor);
-
- static void trace(JSTracer* trc, JSObject* object);
-
- static size_t offsetOfExpando() {
- return offsetof(UnboxedPlainObject, expando_);
- }
-
- static size_t offsetOfData() {
- return offsetof(UnboxedPlainObject, data_[0]);
- }
-};
-
-inline gc::AllocKind
-UnboxedLayout::getAllocKind() const
-{
- MOZ_ASSERT(size());
- return gc::GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + size());
-}
-
-// Class for an array object using an unboxed representation.
-class UnboxedArrayObject : public JSObject
-{
- // Elements pointer for the object.
- uint8_t* elements_;
-
- // The nominal array length. This always fits in an int32_t.
- uint32_t length_;
-
- // Value indicating the allocated capacity and initialized length of the
- // array. The top CapacityBits bits are an index into CapacityArray, which
- // indicates the elements capacity. The low InitializedLengthBits store the
- // initialized length of the array.
- uint32_t capacityIndexAndInitializedLength_;
-
- // If the elements are inline, they will point here.
- uint8_t inlineElements_[1];
-
- public:
- static const uint32_t CapacityBits = 6;
- static const uint32_t CapacityShift = 26;
-
- static const uint32_t CapacityMask = uint32_t(-1) << CapacityShift;
- static const uint32_t InitializedLengthMask = (1 << CapacityShift) - 1;
-
- static const uint32_t MaximumCapacity = InitializedLengthMask;
- static const uint32_t MinimumDynamicCapacity = 8;
-
- static const uint32_t CapacityArray[];
-
- // Capacity index which indicates the array's length is also its capacity.
- static const uint32_t CapacityMatchesLengthIndex = 0;
-
- private:
- static inline uint32_t computeCapacity(uint32_t index, uint32_t length) {
- if (index == CapacityMatchesLengthIndex)
- return length;
- return CapacityArray[index];
- }
-
- static uint32_t chooseCapacityIndex(uint32_t capacity, uint32_t length);
- static uint32_t exactCapacityIndex(uint32_t capacity);
-
- public:
- static const Class class_;
-
- static bool obj_lookupProperty(JSContext* cx, HandleObject obj,
- HandleId id, MutableHandleObject objp,
- MutableHandleShape propp);
-
- static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
- Handle<PropertyDescriptor> desc,
- ObjectOpResult& result);
-
- static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp);
-
- static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
- HandleId id, MutableHandleValue vp);
-
- static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
- HandleValue receiver, ObjectOpResult& result);
-
- static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
- MutableHandle<PropertyDescriptor> desc);
-
- static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
- ObjectOpResult& result);
-
- static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
- bool enumerableOnly);
- static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable);
-
- inline const UnboxedLayout& layout() const;
-
- const UnboxedLayout& layoutDontCheckGeneration() const {
- return group()->unboxedLayoutDontCheckGeneration();
- }
-
- JSValueType elementType() const {
- return layoutDontCheckGeneration().elementType();
- }
-
- uint32_t elementSize() const {
- return UnboxedTypeSize(elementType());
- }
-
- static bool convertToNative(JSContext* cx, JSObject* obj);
- static UnboxedArrayObject* create(ExclusiveContext* cx, HandleObjectGroup group,
- uint32_t length, NewObjectKind newKind,
- uint32_t maxLength = MaximumCapacity);
-
- static bool convertToNativeWithGroup(ExclusiveContext* cx, JSObject* obj,
- ObjectGroup* group, Shape* shape);
- bool convertInt32ToDouble(ExclusiveContext* cx, ObjectGroup* group);
-
- void fillAfterConvert(ExclusiveContext* cx,
- Handle<GCVector<Value>> values, size_t* valueCursor);
-
- static void trace(JSTracer* trc, JSObject* object);
- static void objectMoved(JSObject* obj, const JSObject* old);
- static void finalize(FreeOp* fop, JSObject* obj);
-
- static size_t objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src,
- gc::AllocKind allocKind);
-
- uint8_t* elements() {
- return elements_;
- }
-
- bool hasInlineElements() const {
- return elements_ == &inlineElements_[0];
- }
-
- uint32_t length() const {
- return length_;
- }
-
- uint32_t initializedLength() const {
- return capacityIndexAndInitializedLength_ & InitializedLengthMask;
- }
-
- uint32_t capacityIndex() const {
- return (capacityIndexAndInitializedLength_ & CapacityMask) >> CapacityShift;
- }
-
- uint32_t capacity() const {
- return computeCapacity(capacityIndex(), length());
- }
-
- bool containsProperty(ExclusiveContext* cx, jsid id);
-
- bool setElement(ExclusiveContext* cx, size_t index, const Value& v);
- bool initElement(ExclusiveContext* cx, size_t index, const Value& v);
- void initElementNoTypeChange(size_t index, const Value& v);
- Value getElement(size_t index);
-
- template <JSValueType Type> inline bool setElementSpecific(ExclusiveContext* cx, size_t index,
- const Value& v);
- template <JSValueType Type> inline void setElementNoTypeChangeSpecific(size_t index, const Value& v);
- template <JSValueType Type> inline bool initElementSpecific(ExclusiveContext* cx, size_t index,
- const Value& v);
- template <JSValueType Type> inline void initElementNoTypeChangeSpecific(size_t index, const Value& v);
- template <JSValueType Type> inline Value getElementSpecific(size_t index);
- template <JSValueType Type> inline void triggerPreBarrier(size_t index);
-
- bool growElements(ExclusiveContext* cx, size_t cap);
- void shrinkElements(ExclusiveContext* cx, size_t cap);
-
- static uint32_t offsetOfElements() {
- return offsetof(UnboxedArrayObject, elements_);
- }
- static uint32_t offsetOfLength() {
- return offsetof(UnboxedArrayObject, length_);
- }
- static uint32_t offsetOfCapacityIndexAndInitializedLength() {
- return offsetof(UnboxedArrayObject, capacityIndexAndInitializedLength_);
- }
- static uint32_t offsetOfInlineElements() {
- return offsetof(UnboxedArrayObject, inlineElements_);
- }
-
- void setLengthInt32(uint32_t length) {
- MOZ_ASSERT(length <= INT32_MAX);
- length_ = length;
- }
-
- inline void setLength(ExclusiveContext* cx, uint32_t len);
- inline void setInitializedLength(uint32_t initlen);
-
- inline void setInitializedLengthNoBarrier(uint32_t initlen) {
- MOZ_ASSERT(initlen <= InitializedLengthMask);
- capacityIndexAndInitializedLength_ =
- (capacityIndexAndInitializedLength_ & CapacityMask) | initlen;
- }
-
- private:
- void setInlineElements() {
- elements_ = &inlineElements_[0];
- }
-
- void setCapacityIndex(uint32_t index) {
- MOZ_ASSERT(index <= (CapacityMask >> CapacityShift));
- capacityIndexAndInitializedLength_ =
- (index << CapacityShift) | initializedLength();
- }
-};
-
-} // namespace js
-
-namespace JS {
-
-template <>
-struct DeletePolicy<js::UnboxedLayout> : public js::GCManagedDeletePolicy<js::UnboxedLayout>
-{};
-
-} /* namespace JS */
-
-#endif /* vm_UnboxedObject_h */
diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp
index a318d67a9..52b8eeed1 100644
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -34,6 +34,7 @@
#include "frontend/Parser.h"
#include "gc/Policy.h"
#include "js/MemoryMetrics.h"
+#include "vm/SelfHosting.h"
#include "vm/StringBuffer.h"
#include "vm/Time.h"
#include "vm/TypedArrayObject.h"
@@ -318,7 +319,7 @@ struct js::AsmJSMetadata : Metadata, AsmJSMetadataCacheablePod
// Function constructor, this will be the first character in the function
// source. Otherwise, it will be the opening parenthesis of the arguments
// list.
- uint32_t preludeStart;
+ uint32_t toStringStart;
uint32_t srcStart;
uint32_t srcBodyStart;
bool strict;
@@ -1759,7 +1760,7 @@ class MOZ_STACK_CLASS ModuleValidator
if (!asmJSMetadata_)
return false;
- asmJSMetadata_->preludeStart = moduleFunctionNode_->pn_funbox->preludeStart;
+ asmJSMetadata_->toStringStart = moduleFunctionNode_->pn_funbox->toStringStart;
asmJSMetadata_->srcStart = moduleFunctionNode_->pn_body->pn_pos.begin;
asmJSMetadata_->srcBodyStart = parser_.tokenStream.currentToken().pos.end;
asmJSMetadata_->strict = parser_.pc->sc()->strict() &&
@@ -3250,10 +3251,9 @@ CheckModuleLevelName(ModuleValidator& m, ParseNode* usepn, PropertyName* name)
static bool
CheckFunctionHead(ModuleValidator& m, ParseNode* fn)
{
- JSFunction* fun = FunctionObject(fn);
if (fn->pn_funbox->hasRest())
return m.fail(fn, "rest args not allowed");
- if (fun->isExprBody())
+ if (fn->pn_funbox->isExprBody())
return m.fail(fn, "expression closures not allowed");
if (fn->pn_funbox->hasDestructuringArgs)
return m.fail(fn, "destructuring args not allowed");
@@ -7051,13 +7051,13 @@ ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line)
TokenStream& tokenStream = m.tokenStream();
tokenStream.consumeKnownToken(TOK_FUNCTION, TokenStream::Operand);
- uint32_t preludeStart = tokenStream.currentToken().pos.begin;
+ uint32_t toStringStart = tokenStream.currentToken().pos.begin;
*line = tokenStream.srcCoords.lineNum(tokenStream.currentToken().pos.end);
TokenKind tk;
if (!tokenStream.getToken(&tk, TokenStream::Operand))
return false;
- if (tk != TOK_NAME && tk != TOK_YIELD)
+ if (!TokenKindIsPossibleIdentifier(tk))
return false; // The regular parser will throw a SyntaxError, no need to m.fail.
RootedPropertyName name(m.cx(), m.parser().bindingIdentifier(YieldIsName));
@@ -7074,7 +7074,7 @@ ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line)
ParseContext* outerpc = m.parser().pc;
Directives directives(outerpc);
- FunctionBox* funbox = m.parser().newFunctionBox(fn, fun, preludeStart, directives, NotGenerator,
+ FunctionBox* funbox = m.parser().newFunctionBox(fn, fun, toStringStart, directives, NotGenerator,
SyncFunction, /* tryAnnexB = */ false);
if (!funbox)
return false;
@@ -7466,6 +7466,20 @@ GetDataProperty(JSContext* cx, HandleValue objVal, ImmutablePropertyNamePtr fiel
}
static bool
+HasObjectValueOfMethodPure(JSObject* obj, JSContext* cx)
+{
+ Value v;
+ if (!GetPropertyPure(cx, obj, NameToId(cx->names().valueOf), &v))
+ return false;
+
+ JSFunction* fun;
+ if (!IsFunctionObject(v, &fun))
+ return false;
+
+ return IsSelfHostedFunctionWithName(fun, cx->names().Object_valueOf);
+}
+
+static bool
HasPureCoercion(JSContext* cx, HandleValue v)
{
// Unsigned SIMD types are not allowed in function signatures.
@@ -7479,10 +7493,10 @@ HasPureCoercion(JSContext* cx, HandleValue v)
// coercions are not observable and coercion via ToNumber/ToInt32
// definitely produces NaN/0. We should remove this special case later once
// most apps have been built with newer Emscripten.
- jsid toString = NameToId(cx->names().toString);
if (v.toObject().is<JSFunction>() &&
- HasObjectValueOf(&v.toObject(), cx) &&
- ClassMethodIsNative(cx, &v.toObject().as<JSFunction>(), &JSFunction::class_, toString, fun_toString))
+ HasNoToPrimitiveMethodPure(&v.toObject(), cx) &&
+ HasObjectValueOfMethodPure(&v.toObject(), cx) &&
+ HasNativeMethodPure(&v.toObject(), cx->names().toString, fun_toString, cx))
{
return true;
}
@@ -8057,7 +8071,7 @@ HandleInstantiationFailure(JSContext* cx, CallArgs args, const AsmJSMetadata& me
return false;
}
- uint32_t begin = metadata.preludeStart;
+ uint32_t begin = metadata.toStringStart;
uint32_t end = metadata.srcEndAfterCurly();
Rooted<JSFlatString*> src(cx, source->substringDontDeflate(cx, begin, end));
if (!src)
@@ -8540,7 +8554,7 @@ LookupAsmJSModuleInCache(ExclusiveContext* cx, AsmJSParser& parser, bool* loaded
return true;
// See AsmJSMetadata comment as well as ModuleValidator::init().
- asmJSMetadata->preludeStart = parser.pc->functionBox()->preludeStart;
+ asmJSMetadata->toStringStart = parser.pc->functionBox()->toStringStart;
asmJSMetadata->srcStart = parser.pc->functionBox()->functionNode->pn_body->pn_pos.begin;
asmJSMetadata->srcBodyStart = parser.tokenStream.currentToken().pos.end;
asmJSMetadata->strict = parser.pc->sc()->strict() && !parser.pc->sc()->hasExplicitUseStrict();
@@ -8838,7 +8852,7 @@ js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda
MOZ_ASSERT(IsAsmJSModule(fun));
const AsmJSMetadata& metadata = AsmJSModuleFunctionToModule(fun).metadata().asAsmJS();
- uint32_t begin = metadata.preludeStart;
+ uint32_t begin = metadata.toStringStart;
uint32_t end = metadata.srcEndAfterCurly();
ScriptSource* source = metadata.scriptSource.get();
diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp
index 0b030c844..8d4f575b2 100644
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -1659,7 +1659,7 @@ Reject(JSContext* cx, const CompileArgs& args, UniqueChars error, Handle<Promise
if (!cx->getPendingException(&rejectionValue))
return false;
- return promise->reject(cx, rejectionValue);
+ return PromiseObject::reject(cx, promise, rejectionValue);
}
RootedObject stack(cx, promise->allocationSite());
@@ -1687,7 +1687,7 @@ Reject(JSContext* cx, const CompileArgs& args, UniqueChars error, Handle<Promise
return false;
RootedValue rejectionValue(cx, ObjectValue(*errorObj));
- return promise->reject(cx, rejectionValue);
+ return PromiseObject::reject(cx, promise, rejectionValue);
}
static bool
@@ -1699,7 +1699,7 @@ ResolveCompilation(JSContext* cx, Module& module, Handle<PromiseObject*> promise
return false;
RootedValue resolutionValue(cx, ObjectValue(*moduleObj));
- return promise->resolve(cx, resolutionValue);
+ return PromiseObject::resolve(cx, promise, resolutionValue);
}
struct CompileTask : PromiseTask
@@ -1734,7 +1734,7 @@ RejectWithPendingException(JSContext* cx, Handle<PromiseObject*> promise)
if (!GetAndClearException(cx, &rejectionValue))
return false;
- return promise->reject(cx, rejectionValue);
+ return PromiseObject::reject(cx, promise, rejectionValue);
}
static bool
@@ -1822,7 +1822,7 @@ ResolveInstantiation(JSContext* cx, Module& module, HandleObject importObj,
return false;
val = ObjectValue(*resultObj);
- return promise->resolve(cx, val);
+ return PromiseObject::resolve(cx, promise, val);
}
struct InstantiateTask : CompileTask
@@ -1894,7 +1894,7 @@ WebAssembly_instantiate(JSContext* cx, unsigned argc, Value* vp)
return RejectWithPendingException(cx, promise, callArgs);
RootedValue resolutionValue(cx, ObjectValue(*instanceObj));
- if (!promise->resolve(cx, resolutionValue))
+ if (!PromiseObject::resolve(cx, promise, resolutionValue))
return false;
} else {
auto task = cx->make_unique<InstantiateTask>(cx, promise, importObj);
@@ -2018,7 +2018,7 @@ js::InitWebAssemblyClass(JSContext* cx, HandleObject obj)
Handle<GlobalObject*> global = obj.as<GlobalObject>();
MOZ_ASSERT(!global->isStandardClassResolved(JSProto_WebAssembly));
- RootedObject proto(cx, global->getOrCreateObjectPrototype(cx));
+ RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
if (!proto)
return nullptr;
diff --git a/js/xpconnect/idl/moz.build b/js/xpconnect/idl/moz.build
index 2438b1a5a..0808d3450 100644
--- a/js/xpconnect/idl/moz.build
+++ b/js/xpconnect/idl/moz.build
@@ -7,7 +7,6 @@
XPIDL_SOURCES += [
'mozIJSSubScriptLoader.idl',
'nsIAddonInterposition.idl',
- 'nsIScriptError.idl',
'nsIXPConnect.idl',
'nsIXPCScriptable.idl',
'xpccomponents.idl',
diff --git a/js/xpconnect/idl/nsIScriptError.idl b/js/xpconnect/idl/nsIScriptError.idl
deleted file mode 100644
index 468ca682f..000000000
--- a/js/xpconnect/idl/nsIScriptError.idl
+++ /dev/null
@@ -1,122 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* 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/. */
-
-/*
- * nsIConsoleMessage subclass for representing JavaScript errors and warnings.
- */
-
-
-#include "nsISupports.idl"
-#include "nsIConsoleMessage.idl"
-
-%{C++
-#include "nsStringGlue.h" // for nsDependentCString
-%}
-
-[scriptable, uuid(361be358-76f0-47aa-b37b-6ad833599e8d)]
-interface nsIScriptError : nsIConsoleMessage
-{
- /** pseudo-flag for default case */
- const unsigned long errorFlag = 0x0;
-
- /** message is warning */
- const unsigned long warningFlag = 0x1;
-
- /** exception was thrown for this case - exception-aware hosts can ignore */
- const unsigned long exceptionFlag = 0x2;
-
- // XXX check how strict is implemented these days.
- /** error or warning is due to strict option */
- const unsigned long strictFlag = 0x4;
-
- /** just a log message */
- const unsigned long infoFlag = 0x8;
-
- /**
- * The error message without any context/line number information.
- *
- * @note nsIConsoleMessage.message will return the error formatted
- * with file/line information.
- */
- readonly attribute AString errorMessage;
-
- readonly attribute AString sourceName;
- readonly attribute AString sourceLine;
- readonly attribute uint32_t lineNumber;
- readonly attribute uint32_t columnNumber;
- readonly attribute uint32_t flags;
-
- /**
- * Categories I know about -
- * XUL javascript
- * content javascript (both of these from nsDocShell, currently)
- * system javascript (errors in JS components and other system JS)
- */
- readonly attribute string category;
-
- /* Get the window id this was initialized with. Zero will be
- returned if init() was used instead of initWithWindowID(). */
- readonly attribute unsigned long long outerWindowID;
-
- /* Get the inner window id this was initialized with. Zero will be
- returned if init() was used instead of initWithWindowID(). */
- readonly attribute unsigned long long innerWindowID;
-
- readonly attribute boolean isFromPrivateWindow;
-
- attribute jsval stack;
-
- /**
- * The name of a template string, as found in js.msg, associated with the
- * error message.
- */
- attribute AString errorMessageName;
-
-
- void init(in AString message,
- in AString sourceName,
- in AString sourceLine,
- in uint32_t lineNumber,
- in uint32_t columnNumber,
- in uint32_t flags,
- in string category);
-
- /* This should be called instead of nsIScriptError.init to
- initialize with a window id. The window id should be for the
- inner window associated with this error. */
- void initWithWindowID(in AString message,
- in AString sourceName,
- in AString sourceLine,
- in uint32_t lineNumber,
- in uint32_t columnNumber,
- in uint32_t flags,
- in ACString category,
- in unsigned long long innerWindowID);
-%{C++
- // This overload allows passing a literal string for category.
- template<uint32_t N>
- nsresult InitWithWindowID(const nsAString& message,
- const nsAString& sourceName,
- const nsAString& sourceLine,
- uint32_t lineNumber,
- uint32_t columnNumber,
- uint32_t flags,
- const char (&c)[N],
- uint64_t aInnerWindowID)
- {
- nsDependentCString category(c, N - 1);
- return InitWithWindowID(message, sourceName, sourceLine, lineNumber,
- columnNumber, flags, category, aInnerWindowID);
- }
-%}
-
-};
-
-%{ C++
-#define NS_SCRIPTERROR_CID \
-{ 0x1950539a, 0x90f0, 0x4d22, { 0xb5, 0xaf, 0x71, 0x32, 0x9c, 0x68, 0xfa, 0x35 }}
-
-#define NS_SCRIPTERROR_CONTRACTID "@mozilla.org/scripterror;1"
-%}
diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp
index dbb63092e..07ce7460b 100644
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -34,9 +34,11 @@
#include "nsDOMClassInfo.h"
#include "ShimInterfaceInfo.h"
#include "nsIAddonInterposition.h"
+#include "nsIScriptError.h"
#include "nsISimpleEnumerator.h"
#include "nsPIDOMWindow.h"
#include "nsGlobalWindow.h"
+#include "nsScriptError.h"
using namespace mozilla;
using namespace JS;
diff --git a/js/xpconnect/src/XPCConvert.cpp b/js/xpconnect/src/XPCConvert.cpp
index 37932b452..77f09f4a5 100644
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -11,9 +11,11 @@
#include "xpcprivate.h"
#include "nsIAtom.h"
+#include "nsIScriptError.h"
#include "nsWrapperCache.h"
#include "nsJSUtils.h"
#include "nsQueryObject.h"
+#include "nsScriptError.h"
#include "WrapperFactory.h"
#include "nsWrapperCacheInlines.h"
diff --git a/js/xpconnect/src/XPCModule.h b/js/xpconnect/src/XPCModule.h
index d62764625..506e8945a 100644
--- a/js/xpconnect/src/XPCModule.h
+++ b/js/xpconnect/src/XPCModule.h
@@ -23,7 +23,6 @@
NS_GENERIC_FACTORY_CONSTRUCTOR(nsJSID)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIXPConnect,
nsXPConnect::GetSingleton)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsScriptError)
NS_GENERIC_FACTORY_CONSTRUCTOR(mozJSComponentLoader)
NS_GENERIC_FACTORY_CONSTRUCTOR(mozJSSubScriptLoader)
@@ -31,14 +30,12 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(mozJSSubScriptLoader)
NS_DEFINE_NAMED_CID(NS_JS_ID_CID);
NS_DEFINE_NAMED_CID(NS_XPCONNECT_CID);
NS_DEFINE_NAMED_CID(NS_XPCEXCEPTION_CID);
-NS_DEFINE_NAMED_CID(NS_SCRIPTERROR_CID);
NS_DEFINE_NAMED_CID(MOZJSCOMPONENTLOADER_CID);
NS_DEFINE_NAMED_CID(MOZ_JSSUBSCRIPTLOADER_CID);
#define XPCONNECT_CIDENTRIES \
{ &kNS_JS_ID_CID, false, nullptr, nsJSIDConstructor }, \
{ &kNS_XPCONNECT_CID, false, nullptr, nsIXPConnectConstructor }, \
- { &kNS_SCRIPTERROR_CID, false, nullptr, nsScriptErrorConstructor }, \
{ &kMOZJSCOMPONENTLOADER_CID, false, nullptr, mozJSComponentLoaderConstructor },\
{ &kMOZ_JSSUBSCRIPTLOADER_CID, false, nullptr, mozJSSubScriptLoaderConstructor },
@@ -46,7 +43,6 @@ NS_DEFINE_NAMED_CID(MOZ_JSSUBSCRIPTLOADER_CID);
{ XPC_ID_CONTRACTID, &kNS_JS_ID_CID }, \
{ XPC_XPCONNECT_CONTRACTID, &kNS_XPCONNECT_CID }, \
{ XPC_CONTEXT_STACK_CONTRACTID, &kNS_XPCONNECT_CID }, \
- { NS_SCRIPTERROR_CONTRACTID, &kNS_SCRIPTERROR_CID }, \
{ MOZJSCOMPONENTLOADER_CONTRACTID, &kMOZJSCOMPONENTLOADER_CID }, \
{ MOZJSSUBSCRIPTLOADER_CONTRACTID, &kMOZ_JSSUBSCRIPTLOADER_CID },
diff --git a/js/xpconnect/src/XPCWrappedJSClass.cpp b/js/xpconnect/src/XPCWrappedJSClass.cpp
index 2c9fd66bc..e90373e3d 100644
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -10,6 +10,7 @@
#include "jsprf.h"
#include "nsArrayEnumerator.h"
#include "nsContentUtils.h"
+#include "nsIScriptError.h"
#include "nsWrapperCache.h"
#include "AccessCheck.h"
#include "nsJSUtils.h"
diff --git a/js/xpconnect/src/XPCWrappedNativeInfo.cpp b/js/xpconnect/src/XPCWrappedNativeInfo.cpp
index 302454fb5..4b0330af6 100644
--- a/js/xpconnect/src/XPCWrappedNativeInfo.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeInfo.cpp
@@ -11,6 +11,7 @@
#include "mozilla/MemoryReporting.h"
#include "mozilla/XPTInterfaceInfoManager.h"
+#include "nsIScriptError.h"
#include "nsPrintfCString.h"
using namespace JS;
diff --git a/js/xpconnect/src/moz.build b/js/xpconnect/src/moz.build
index 7e787bb56..7d9cd5b37 100644
--- a/js/xpconnect/src/moz.build
+++ b/js/xpconnect/src/moz.build
@@ -14,8 +14,6 @@ EXPORTS += [
UNIFIED_SOURCES += [
'ExportHelpers.cpp',
- 'nsScriptError.cpp',
- 'nsScriptErrorWithStack.cpp',
'nsXPConnect.cpp',
'Sandbox.cpp',
'XPCCallContext.cpp',
@@ -58,6 +56,7 @@ LOCAL_INCLUDES += [
'../wrappers',
'/caps',
'/dom/base',
+ '/dom/bindings',
'/dom/html',
'/dom/svg',
'/dom/workers',
diff --git a/js/xpconnect/src/nsScriptError.cpp b/js/xpconnect/src/nsScriptError.cpp
deleted file mode 100644
index ff687bc44..000000000
--- a/js/xpconnect/src/nsScriptError.cpp
+++ /dev/null
@@ -1,345 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* vim: set ts=8 sts=4 et sw=4 tw=99: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/*
- * nsIScriptError implementation. Defined here, lacking a JS-specific
- * place to put XPCOM things.
- */
-
-#include "xpcprivate.h"
-#include "jsprf.h"
-#include "MainThreadUtils.h"
-#include "mozilla/Assertions.h"
-#include "nsGlobalWindow.h"
-#include "nsPIDOMWindow.h"
-#include "nsILoadContext.h"
-#include "nsIDocShell.h"
-#include "nsIScriptError.h"
-#include "nsISensitiveInfoHiddenURI.h"
-
-static_assert(nsIScriptError::errorFlag == JSREPORT_ERROR &&
- nsIScriptError::warningFlag == JSREPORT_WARNING &&
- nsIScriptError::exceptionFlag == JSREPORT_EXCEPTION &&
- nsIScriptError::strictFlag == JSREPORT_STRICT &&
- nsIScriptError::infoFlag == JSREPORT_USER_1,
- "flags should be consistent");
-
-nsScriptErrorBase::nsScriptErrorBase()
- : mMessage(),
- mMessageName(),
- mSourceName(),
- mLineNumber(0),
- mSourceLine(),
- mColumnNumber(0),
- mFlags(0),
- mCategory(),
- mOuterWindowID(0),
- mInnerWindowID(0),
- mTimeStamp(0),
- mInitializedOnMainThread(false),
- mIsFromPrivateWindow(false)
-{
-}
-
-nsScriptErrorBase::~nsScriptErrorBase() {}
-
-void
-nsScriptErrorBase::InitializeOnMainThread()
-{
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(!mInitializedOnMainThread);
-
- if (mInnerWindowID) {
- nsGlobalWindow* window =
- nsGlobalWindow::GetInnerWindowWithId(mInnerWindowID);
- if (window) {
- nsPIDOMWindowOuter* outer = window->GetOuterWindow();
- if (outer)
- mOuterWindowID = outer->WindowID();
-
- nsIDocShell* docShell = window->GetDocShell();
- nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
-
- if (loadContext) {
- // Never mark exceptions from chrome windows as having come from
- // private windows, since we always want them to be reported.
- nsIPrincipal* winPrincipal = window->GetPrincipal();
- mIsFromPrivateWindow = loadContext->UsePrivateBrowsing() &&
- !nsContentUtils::IsSystemPrincipal(winPrincipal);
- }
- }
- }
-
- mInitializedOnMainThread = true;
-}
-
-// nsIConsoleMessage methods
-NS_IMETHODIMP
-nsScriptErrorBase::GetMessageMoz(char16_t** result) {
- nsresult rv;
-
- nsAutoCString message;
- rv = ToString(message);
- if (NS_FAILED(rv))
- return rv;
-
- *result = UTF8ToNewUnicode(message);
- if (!*result)
- return NS_ERROR_OUT_OF_MEMORY;
-
- return NS_OK;
-}
-
-
-NS_IMETHODIMP
-nsScriptErrorBase::GetLogLevel(uint32_t* aLogLevel)
-{
- if (mFlags & (uint32_t)nsIScriptError::infoFlag) {
- *aLogLevel = nsIConsoleMessage::info;
- } else if (mFlags & (uint32_t)nsIScriptError::warningFlag) {
- *aLogLevel = nsIConsoleMessage::warn;
- } else {
- *aLogLevel = nsIConsoleMessage::error;
- }
- return NS_OK;
-}
-
-// nsIScriptError methods
-NS_IMETHODIMP
-nsScriptErrorBase::GetErrorMessage(nsAString& aResult) {
- aResult.Assign(mMessage);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsScriptErrorBase::GetSourceName(nsAString& aResult) {
- aResult.Assign(mSourceName);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsScriptErrorBase::GetSourceLine(nsAString& aResult) {
- aResult.Assign(mSourceLine);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsScriptErrorBase::GetLineNumber(uint32_t* result) {
- *result = mLineNumber;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsScriptErrorBase::GetColumnNumber(uint32_t* result) {
- *result = mColumnNumber;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsScriptErrorBase::GetFlags(uint32_t* result) {
- *result = mFlags;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsScriptErrorBase::GetCategory(char** result) {
- *result = ToNewCString(mCategory);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsScriptErrorBase::GetStack(JS::MutableHandleValue aStack) {
- aStack.setUndefined();
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsScriptErrorBase::SetStack(JS::HandleValue aStack) {
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsScriptErrorBase::GetErrorMessageName(nsAString& aErrorMessageName) {
- aErrorMessageName = mMessageName;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsScriptErrorBase::SetErrorMessageName(const nsAString& aErrorMessageName) {
- mMessageName = aErrorMessageName;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsScriptErrorBase::Init(const nsAString& message,
- const nsAString& sourceName,
- const nsAString& sourceLine,
- uint32_t lineNumber,
- uint32_t columnNumber,
- uint32_t flags,
- const char* category)
-{
- return InitWithWindowID(message, sourceName, sourceLine, lineNumber,
- columnNumber, flags,
- category ? nsDependentCString(category)
- : EmptyCString(),
- 0);
-}
-
-NS_IMETHODIMP
-nsScriptErrorBase::InitWithWindowID(const nsAString& message,
- const nsAString& sourceName,
- const nsAString& sourceLine,
- uint32_t lineNumber,
- uint32_t columnNumber,
- uint32_t flags,
- const nsACString& category,
- uint64_t aInnerWindowID)
-{
- mMessage.Assign(message);
-
- if (!sourceName.IsEmpty()) {
- mSourceName.Assign(sourceName);
-
- nsCOMPtr<nsIURI> uri;
- nsAutoCString pass;
- if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), sourceName)) &&
- NS_SUCCEEDED(uri->GetPassword(pass)) &&
- !pass.IsEmpty()) {
- nsCOMPtr<nsISensitiveInfoHiddenURI> safeUri =
- do_QueryInterface(uri);
-
- nsAutoCString loc;
- if (safeUri &&
- NS_SUCCEEDED(safeUri->GetSensitiveInfoHiddenSpec(loc))) {
- mSourceName.Assign(NS_ConvertUTF8toUTF16(loc));
- }
- }
- }
-
- mLineNumber = lineNumber;
- mSourceLine.Assign(sourceLine);
- mColumnNumber = columnNumber;
- mFlags = flags;
- mCategory = category;
- mTimeStamp = JS_Now() / 1000;
- mInnerWindowID = aInnerWindowID;
-
- if (aInnerWindowID && NS_IsMainThread()) {
- InitializeOnMainThread();
- }
-
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsScriptErrorBase::ToString(nsACString& /*UTF8*/ aResult)
-{
- static const char format0[] =
- "[%s: \"%s\" {file: \"%s\" line: %d column: %d source: \"%s\"}]";
- static const char format1[] =
- "[%s: \"%s\" {file: \"%s\" line: %d}]";
- static const char format2[] =
- "[%s: \"%s\"]";
-
- static const char error[] = "JavaScript Error";
- static const char warning[] = "JavaScript Warning";
-
- const char* severity = !(mFlags & JSREPORT_WARNING) ? error : warning;
-
- char* temp;
- char* tempMessage = nullptr;
- char* tempSourceName = nullptr;
- char* tempSourceLine = nullptr;
-
- if (!mMessage.IsEmpty())
- tempMessage = ToNewUTF8String(mMessage);
- if (!mSourceName.IsEmpty())
- // Use at most 512 characters from mSourceName.
- tempSourceName = ToNewUTF8String(StringHead(mSourceName, 512));
- if (!mSourceLine.IsEmpty())
- // Use at most 512 characters from mSourceLine.
- tempSourceLine = ToNewUTF8String(StringHead(mSourceLine, 512));
-
- if (nullptr != tempSourceName && nullptr != tempSourceLine)
- temp = JS_smprintf(format0,
- severity,
- tempMessage,
- tempSourceName,
- mLineNumber,
- mColumnNumber,
- tempSourceLine);
- else if (!mSourceName.IsEmpty())
- temp = JS_smprintf(format1,
- severity,
- tempMessage,
- tempSourceName,
- mLineNumber);
- else
- temp = JS_smprintf(format2,
- severity,
- tempMessage);
-
- if (nullptr != tempMessage)
- free(tempMessage);
- if (nullptr != tempSourceName)
- free(tempSourceName);
- if (nullptr != tempSourceLine)
- free(tempSourceLine);
-
- if (!temp)
- return NS_ERROR_OUT_OF_MEMORY;
-
- aResult.Assign(temp);
- JS_smprintf_free(temp);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsScriptErrorBase::GetOuterWindowID(uint64_t* aOuterWindowID)
-{
- NS_WARNING_ASSERTION(NS_IsMainThread() || mInitializedOnMainThread,
- "This can't be safely determined off the main thread, "
- "returning an inaccurate value!");
-
- if (!mInitializedOnMainThread && NS_IsMainThread()) {
- InitializeOnMainThread();
- }
-
- *aOuterWindowID = mOuterWindowID;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsScriptErrorBase::GetInnerWindowID(uint64_t* aInnerWindowID)
-{
- *aInnerWindowID = mInnerWindowID;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsScriptErrorBase::GetTimeStamp(int64_t* aTimeStamp)
-{
- *aTimeStamp = mTimeStamp;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsScriptErrorBase::GetIsFromPrivateWindow(bool* aIsFromPrivateWindow)
-{
- NS_WARNING_ASSERTION(NS_IsMainThread() || mInitializedOnMainThread,
- "This can't be safely determined off the main thread, "
- "returning an inaccurate value!");
-
- if (!mInitializedOnMainThread && NS_IsMainThread()) {
- InitializeOnMainThread();
- }
-
- *aIsFromPrivateWindow = mIsFromPrivateWindow;
- return NS_OK;
-}
-
-NS_IMPL_ISUPPORTS(nsScriptError, nsIConsoleMessage, nsIScriptError)
diff --git a/js/xpconnect/src/nsScriptErrorWithStack.cpp b/js/xpconnect/src/nsScriptErrorWithStack.cpp
deleted file mode 100644
index 50407da23..000000000
--- a/js/xpconnect/src/nsScriptErrorWithStack.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* vim: set ts=8 sts=4 et sw=4 tw=99: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/*
- * nsScriptErrorWithStack implementation.
- * a main-thread-only, cycle-collected subclass of nsScriptErrorBase
- * that can store a SavedFrame stack trace object.
- */
-
-#include "xpcprivate.h"
-#include "MainThreadUtils.h"
-#include "mozilla/Assertions.h"
-#include "nsGlobalWindow.h"
-#include "nsCycleCollectionParticipant.h"
-
-
-namespace {
-
-static nsCString
-FormatStackString(JSContext* cx, HandleObject aStack) {
- JS::RootedString formattedStack(cx);
-
- if (!JS::BuildStackString(cx, aStack, &formattedStack)) {
- return nsCString();
- }
-
- nsAutoJSString stackJSString;
- if (!stackJSString.init(cx, formattedStack)) {
- return nsCString();
- }
-
- return NS_ConvertUTF16toUTF8(stackJSString.get());
-}
-
-}
-
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(nsScriptErrorWithStack)
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsScriptErrorWithStack)
- tmp->mStack = nullptr;
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsScriptErrorWithStack)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsScriptErrorWithStack)
- NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mStack)
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(nsScriptErrorWithStack)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(nsScriptErrorWithStack)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsScriptErrorWithStack)
- NS_INTERFACE_MAP_ENTRY(nsISupports)
- NS_INTERFACE_MAP_ENTRY(nsIConsoleMessage)
- NS_INTERFACE_MAP_ENTRY(nsIScriptError)
-NS_INTERFACE_MAP_END
-
-nsScriptErrorWithStack::nsScriptErrorWithStack(JS::HandleObject aStack)
- : mStack(aStack)
-{
- MOZ_ASSERT(NS_IsMainThread(), "You can't use this class on workers.");
- mozilla::HoldJSObjects(this);
-}
-
-nsScriptErrorWithStack::~nsScriptErrorWithStack() {
- mozilla::DropJSObjects(this);
-}
-
-NS_IMETHODIMP
-nsScriptErrorWithStack::Init(const nsAString& message,
- const nsAString& sourceName,
- const nsAString& sourceLine,
- uint32_t lineNumber,
- uint32_t columnNumber,
- uint32_t flags,
- const char* category)
-{
- MOZ_CRASH("nsScriptErrorWithStack requires to be initialized with a document, by using InitWithWindowID");
-}
-
-NS_IMETHODIMP
-nsScriptErrorWithStack::GetStack(JS::MutableHandleValue aStack) {
- aStack.setObjectOrNull(mStack);
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsScriptErrorWithStack::ToString(nsACString& /*UTF8*/ aResult)
-{
- MOZ_ASSERT(NS_IsMainThread());
-
- nsCString message;
- nsresult rv = nsScriptErrorBase::ToString(message);
- NS_ENSURE_SUCCESS(rv, rv);
-
- if (!mStack) {
- aResult.Assign(message);
- return NS_OK;
- }
-
- AutoJSAPI jsapi;
- if (!jsapi.Init(mStack)) {
- return NS_ERROR_FAILURE;
- }
-
- JSContext* cx = jsapi.cx();
- RootedObject stack(cx, mStack);
- nsCString stackString = FormatStackString(cx, stack);
- nsCString combined = message + NS_LITERAL_CSTRING("\n") + stackString;
- aResult.Assign(combined);
-
- return NS_OK;
-}
diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp
index 0466175b1..0d1a6be0a 100644
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -33,7 +33,9 @@
#include "nsIObjectOutputStream.h"
#include "nsScriptSecurityManager.h"
#include "nsIPermissionManager.h"
+#include "nsIScriptError.h"
#include "nsContentUtils.h"
+#include "nsScriptError.h"
#include "jsfriendapi.h"
using namespace mozilla;
@@ -170,9 +172,31 @@ nsXPConnect::IsISupportsDescendant(nsIInterfaceInfo* info)
}
void
+xpc::ErrorBase::Init(JSErrorBase* aReport)
+{
+ if (!aReport->filename) {
+ mFileName.SetIsVoid(true);
+ } else {
+ mFileName.AssignWithConversion(aReport->filename);
+ }
+
+ mLineNumber = aReport->lineno;
+ mColumn = aReport->column;
+}
+
+void
+xpc::ErrorNote::Init(JSErrorNotes::Note* aNote)
+{
+ xpc::ErrorBase::Init(aNote);
+
+ ErrorNoteToMessageString(aNote, mErrorMsg);
+}
+
+void
xpc::ErrorReport::Init(JSErrorReport* aReport, const char* aToStringResult,
bool aIsChrome, uint64_t aWindowID)
{
+ xpc::ErrorBase::Init(aReport);
mCategory = aIsChrome ? NS_LITERAL_CSTRING("chrome javascript")
: NS_LITERAL_CSTRING("content javascript");
mWindowID = aWindowID;
@@ -182,12 +206,6 @@ xpc::ErrorReport::Init(JSErrorReport* aReport, const char* aToStringResult,
AppendUTF8toUTF16(aToStringResult, mErrorMsg);
}
- if (!aReport->filename) {
- mFileName.SetIsVoid(true);
- } else {
- mFileName.AssignWithConversion(aReport->filename);
- }
-
mSourceLine.Assign(aReport->linebuf(), aReport->linebufLength());
const JSErrorFormatString* efs = js::GetErrorMessage(nullptr, aReport->errorNumber);
@@ -197,10 +215,20 @@ xpc::ErrorReport::Init(JSErrorReport* aReport, const char* aToStringResult,
mErrorMsgName.AssignASCII(efs->name);
}
- mLineNumber = aReport->lineno;
- mColumn = aReport->column;
mFlags = aReport->flags;
mIsMuted = aReport->isMuted;
+
+ if (aReport->notes) {
+ if (!mNotes.SetLength(aReport->notes->length(), fallible)) {
+ return;
+ }
+
+ size_t i = 0;
+ for (auto&& note : *aReport->notes) {
+ mNotes.ElementAt(i).Init(note.get());
+ i++;
+ }
+ }
}
void
@@ -226,6 +254,59 @@ xpc::ErrorReport::Init(JSContext* aCx, mozilla::dom::Exception* aException,
static LazyLogModule gJSDiagnostics("JSDiagnostics");
void
+xpc::ErrorBase::AppendErrorDetailsTo(nsCString& error)
+{
+ error.Append(NS_LossyConvertUTF16toASCII(mFileName));
+ error.AppendLiteral(", line ");
+ error.AppendInt(mLineNumber, 10);
+ error.AppendLiteral(": ");
+ error.Append(NS_LossyConvertUTF16toASCII(mErrorMsg));
+}
+
+void
+xpc::ErrorNote::LogToStderr()
+{
+ if (!nsContentUtils::DOMWindowDumpEnabled()) {
+ return;
+ }
+
+ nsAutoCString error;
+ error.AssignLiteral("JavaScript note: ");
+ AppendErrorDetailsTo(error);
+
+ fprintf(stderr, "%s\n", error.get());
+ fflush(stderr);
+}
+
+void
+xpc::ErrorReport::LogToStderr()
+{
+ if (!nsContentUtils::DOMWindowDumpEnabled()) {
+ return;
+ }
+
+ nsAutoCString error;
+ error.AssignLiteral("JavaScript ");
+ if (JSREPORT_IS_STRICT(mFlags)) {
+ error.AppendLiteral("strict ");
+ }
+ if (JSREPORT_IS_WARNING(mFlags)) {
+ error.AppendLiteral("warning: ");
+ } else {
+ error.AppendLiteral("error: ");
+ }
+ AppendErrorDetailsTo(error);
+
+ fprintf(stderr, "%s\n", error.get());
+ fflush(stderr);
+
+ for (size_t i = 0, len = mNotes.Length(); i < len; i++) {
+ ErrorNote& note = mNotes[i];
+ note.LogToStderr();
+ }
+}
+
+void
xpc::ErrorReport::LogToConsole()
{
LogToConsoleWithStack(nullptr);
@@ -233,25 +314,7 @@ xpc::ErrorReport::LogToConsole()
void
xpc::ErrorReport::LogToConsoleWithStack(JS::HandleObject aStack)
{
- // Log to stdout.
- if (nsContentUtils::DOMWindowDumpEnabled()) {
- nsAutoCString error;
- error.AssignLiteral("JavaScript ");
- if (JSREPORT_IS_STRICT(mFlags))
- error.AppendLiteral("strict ");
- if (JSREPORT_IS_WARNING(mFlags))
- error.AppendLiteral("warning: ");
- else
- error.AppendLiteral("error: ");
- error.Append(NS_LossyConvertUTF16toASCII(mFileName));
- error.AppendLiteral(", line ");
- error.AppendInt(mLineNumber, 10);
- error.AppendLiteral(": ");
- error.Append(NS_LossyConvertUTF16toASCII(mErrorMsg));
-
- fprintf(stderr, "%s\n", error.get());
- fflush(stderr);
- }
+ LogToStderr();
MOZ_LOG(gJSDiagnostics,
JSREPORT_IS_WARNING(mFlags) ? LogLevel::Warning : LogLevel::Error,
@@ -263,8 +326,9 @@ xpc::ErrorReport::LogToConsoleWithStack(JS::HandleObject aStack)
// mechanisms.
nsCOMPtr<nsIConsoleService> consoleService =
do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+ NS_ENSURE_TRUE_VOID(consoleService);
- nsCOMPtr<nsIScriptError> errorObject;
+ RefPtr<nsScriptErrorBase> errorObject;
if (mWindowID && aStack) {
// Only set stack on messages related to a document
// As we cache messages in the console service,
@@ -275,18 +339,38 @@ xpc::ErrorReport::LogToConsoleWithStack(JS::HandleObject aStack)
errorObject = new nsScriptError();
}
errorObject->SetErrorMessageName(mErrorMsgName);
- NS_ENSURE_TRUE_VOID(consoleService);
nsresult rv = errorObject->InitWithWindowID(mErrorMsg, mFileName, mSourceLine,
mLineNumber, mColumn, mFlags,
mCategory, mWindowID);
NS_ENSURE_SUCCESS_VOID(rv);
+
+ for (size_t i = 0, len = mNotes.Length(); i < len; i++) {
+ ErrorNote& note = mNotes[i];
+
+ nsScriptErrorNote* noteObject = new nsScriptErrorNote();
+ noteObject->Init(note.mErrorMsg, note.mFileName,
+ note.mLineNumber, note.mColumn);
+ errorObject->AddNote(noteObject);
+ }
+
consoleService->LogMessage(errorObject);
}
/* static */
void
+xpc::ErrorNote::ErrorNoteToMessageString(JSErrorNotes::Note* aNote,
+ nsAString& aString)
+{
+ aString.Truncate();
+ if (aNote->message()) {
+ aString.Append(NS_ConvertUTF8toUTF16(aNote->message().c_str()));
+ }
+}
+
+/* static */
+void
xpc::ErrorReport::ErrorReportToMessageString(JSErrorReport* aReport,
nsAString& aString)
{
diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h
index 347b406eb..e55cc06e0 100644
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -128,7 +128,6 @@
#include "MainThreadUtils.h"
#include "nsIConsoleService.h"
-#include "nsIScriptError.h"
#include "nsIException.h"
#include "nsVariant.h"
@@ -2552,77 +2551,6 @@ extern char*
xpc_PrintJSStack(JSContext* cx, bool showArgs, bool showLocals,
bool showThisProps);
-/***************************************************************************/
-
-// Definition of nsScriptError, defined here because we lack a place to put
-// XPCOM objects associated with the JavaScript engine.
-class nsScriptErrorBase : public nsIScriptError {
-public:
- nsScriptErrorBase();
-
- // TODO - do something reasonable on getting null from these babies.
-
- NS_DECL_NSICONSOLEMESSAGE
- NS_DECL_NSISCRIPTERROR
-
-protected:
- virtual ~nsScriptErrorBase();
-
- void
- InitializeOnMainThread();
-
- nsString mMessage;
- nsString mMessageName;
- nsString mSourceName;
- uint32_t mLineNumber;
- nsString mSourceLine;
- uint32_t mColumnNumber;
- uint32_t mFlags;
- nsCString mCategory;
- // mOuterWindowID is set on the main thread from InitializeOnMainThread().
- uint64_t mOuterWindowID;
- uint64_t mInnerWindowID;
- int64_t mTimeStamp;
- // mInitializedOnMainThread and mIsFromPrivateWindow are set on the main
- // thread from InitializeOnMainThread().
- mozilla::Atomic<bool> mInitializedOnMainThread;
- bool mIsFromPrivateWindow;
-};
-
-class nsScriptError final : public nsScriptErrorBase {
-public:
- nsScriptError() {}
- NS_DECL_THREADSAFE_ISUPPORTS
-
-private:
- virtual ~nsScriptError() {}
-};
-
-class nsScriptErrorWithStack : public nsScriptErrorBase {
-public:
- explicit nsScriptErrorWithStack(JS::HandleObject);
-
- NS_DECL_CYCLE_COLLECTING_ISUPPORTS
- NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsScriptErrorWithStack)
-
- NS_IMETHOD Init(const nsAString& message,
- const nsAString& sourceName,
- const nsAString& sourceLine,
- uint32_t lineNumber,
- uint32_t columnNumber,
- uint32_t flags,
- const char* category) override;
-
- NS_IMETHOD GetStack(JS::MutableHandleValue) override;
- NS_IMETHOD ToString(nsACString& aResult) override;
-
-private:
- virtual ~nsScriptErrorWithStack();
- // Complete stackframe where the error happened.
- // Must be SavedFrame object.
- JS::Heap<JSObject*> mStack;
-};
-
/******************************************************************************
* Handles pre/post script processing.
*/
diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h
index fc8670d46..399cd2181 100644
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -515,13 +515,50 @@ AllowCPOWsInAddon(const nsACString& addonId, bool allow);
bool
ExtraWarningsForSystemJS();
-class ErrorReport {
+class ErrorBase {
+ public:
+ nsString mErrorMsg;
+ nsString mFileName;
+ uint32_t mLineNumber;
+ uint32_t mColumn;
+
+ ErrorBase() : mLineNumber(0)
+ , mColumn(0)
+ {}
+
+ void Init(JSErrorBase* aReport);
+
+ void AppendErrorDetailsTo(nsCString& error);
+};
+
+class ErrorNote : public ErrorBase {
+ public:
+ void Init(JSErrorNotes::Note* aNote);
+
+ // Produce an error event message string from the given JSErrorNotes::Note.
+ // This may produce an empty string if aNote doesn't have a message
+ // attached.
+ static void ErrorNoteToMessageString(JSErrorNotes::Note* aNote,
+ nsAString& aString);
+
+ // Log the error note to the stderr.
+ void LogToStderr();
+};
+
+class ErrorReport : public ErrorBase {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ErrorReport);
+ nsTArray<ErrorNote> mNotes;
+
+ nsCString mCategory;
+ nsString mSourceLine;
+ nsString mErrorMsgName;
+ uint64_t mWindowID;
+ uint32_t mFlags;
+ bool mIsMuted;
+
ErrorReport() : mWindowID(0)
- , mLineNumber(0)
- , mColumn(0)
, mFlags(0)
, mIsMuted(false)
{}
@@ -530,6 +567,7 @@ class ErrorReport {
bool aIsChrome, uint64_t aWindowID);
void Init(JSContext* aCx, mozilla::dom::Exception* aException,
bool aIsChrome, uint64_t aWindowID);
+
// Log the error report to the console. Which console will depend on the
// window id it was initialized with.
void LogToConsole();
@@ -544,18 +582,8 @@ class ErrorReport {
static void ErrorReportToMessageString(JSErrorReport* aReport,
nsAString& aString);
- public:
-
- nsCString mCategory;
- nsString mErrorMsgName;
- nsString mErrorMsg;
- nsString mFileName;
- nsString mSourceLine;
- uint64_t mWindowID;
- uint32_t mLineNumber;
- uint32_t mColumn;
- uint32_t mFlags;
- bool mIsMuted;
+ // Log the error report to the stderr.
+ void LogToStderr();
private:
~ErrorReport() {}
diff --git a/js/xpconnect/tests/chrome/test_bug1041626.xul b/js/xpconnect/tests/chrome/test_bug1041626.xul
index c7c7b7024..11529fbe4 100644
--- a/js/xpconnect/tests/chrome/test_bug1041626.xul
+++ b/js/xpconnect/tests/chrome/test_bug1041626.xul
@@ -28,9 +28,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1041626
ok(Cu.isXrayWrapper(window[0].location), "Location is Xrayed");
let xrayOwnProperties = Object.getOwnPropertyNames(window[0].location);
- todo(xrayOwnProperties.indexOf('toJSON') != -1,
- "dummy toJSON on Location should show up in Xrayable properties");
- xrayOwnProperties.push('toJSON');
let realOwnProperties = Object.getOwnPropertyNames(window[0].wrappedJSObject.location);
ok(realOwnProperties.length > 2);
diff --git a/js/xpconnect/wrappers/WrapperFactory.cpp b/js/xpconnect/wrappers/WrapperFactory.cpp
index 0031fb127..8c9d38788 100644
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -536,7 +536,7 @@ WrapperFactory::Rewrap(JSContext* cx, HandleObject existing, HandleObject obj)
DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target);
if (existing)
- return Wrapper::Renew(cx, existing, obj, wrapper);
+ return Wrapper::Renew(existing, obj, wrapper);
return Wrapper::New(cx, obj, wrapper);
}