summaryrefslogtreecommitdiffstats
path: root/js/src
diff options
context:
space:
mode:
Diffstat (limited to 'js/src')
-rw-r--r--js/src/Makefile.in12
-rw-r--r--js/src/builtin/Array.js108
-rw-r--r--js/src/builtin/AtomicsObject.cpp27
-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.cpp393
-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/String.js12
-rw-r--r--js/src/builtin/SymbolObject.cpp37
-rw-r--r--js/src/builtin/SymbolObject.h4
-rw-r--r--js/src/builtin/TestingFunctions.cpp44
-rw-r--r--js/src/builtin/TypedObject.cpp30
-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/ctypes/CTypes.cpp2
-rw-r--r--js/src/ctypes/libffi/src/x86/win32.S28
-rw-r--r--js/src/ctypes/libffi/testsuite/libffi.call/float2.c10
-rw-r--r--js/src/ds/LifoAlloc.h16
-rw-r--r--js/src/frontend/BytecodeCompiler.cpp23
-rw-r--r--js/src/frontend/BytecodeCompiler.h2
-rw-r--r--js/src/frontend/BytecodeEmitter.cpp482
-rw-r--r--js/src/frontend/BytecodeEmitter.h50
-rw-r--r--js/src/frontend/FoldConstants.cpp11
-rw-r--r--js/src/frontend/FullParseHandler.h68
-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.cpp4
-rw-r--r--js/src/frontend/ParseNode.h31
-rw-r--r--js/src/frontend/Parser.cpp4247
-rw-r--r--js/src/frontend/Parser.h496
-rw-r--r--js/src/frontend/ReservedWords.h (renamed from js/src/vm/Keywords.h)49
-rw-r--r--js/src/frontend/SharedContext.h23
-rw-r--r--js/src/frontend/SourceNotes.h3
-rw-r--r--js/src/frontend/SyntaxParseHandler.h60
-rw-r--r--js/src/frontend/TokenKind.h99
-rw-r--r--js/src/frontend/TokenStream.cpp484
-rw-r--r--js/src/frontend/TokenStream.h263
-rw-r--r--js/src/gc/Barrier.h27
-rw-r--r--js/src/gc/GCInternals.h5
-rw-r--r--js/src/gc/GCRuntime.h4
-rw-r--r--js/src/gc/Marking.cpp85
-rw-r--r--js/src/gc/Memory.cpp102
-rw-r--r--js/src/gc/Nursery.cpp9
-rw-r--r--js/src/gc/Nursery.h1
-rw-r--r--js/src/gc/RootMarking.cpp3
-rw-r--r--js/src/gc/Statistics.cpp219
-rw-r--r--js/src/gc/Statistics.h18
-rw-r--r--js/src/gc/Tracer.cpp69
-rw-r--r--js/src/gc/Zone.cpp15
-rw-r--r--js/src/gc/Zone.h11
-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/asm.js/testSource.js48
-rw-r--r--js/src/jit-test/tests/auto-regress/bug466654.js8
-rw-r--r--js/src/jit-test/tests/auto-regress/bug516897.js6
-rw-r--r--js/src/jit-test/tests/auto-regress/bug537854.js4
-rw-r--r--js/src/jit-test/tests/auto-regress/bug560796.js9
-rw-r--r--js/src/jit-test/tests/auto-regress/bug638735.js1
-rw-r--r--js/src/jit-test/tests/auto-regress/bug639413.js9
-rw-r--r--js/src/jit-test/tests/auto-regress/bug698899.js9
-rw-r--r--js/src/jit-test/tests/auto-regress/bug746397.js10
-rw-r--r--js/src/jit-test/tests/auto-regress/bug769192.js6
-rw-r--r--js/src/jit-test/tests/baseline/bug1344334.js14
-rw-r--r--js/src/jit-test/tests/baseline/bug843444.js8
-rw-r--r--js/src/jit-test/tests/basic/bug510437.js13
-rw-r--r--js/src/jit-test/tests/basic/bug605015.js9
-rw-r--r--js/src/jit-test/tests/basic/bug631305.js9
-rw-r--r--js/src/jit-test/tests/basic/bug662562.js6
-rw-r--r--js/src/jit-test/tests/basic/bug690292.js12
-rw-r--r--js/src/jit-test/tests/basic/bug696748.js3
-rw-r--r--js/src/jit-test/tests/basic/bug713226.js2
-rw-r--r--js/src/jit-test/tests/basic/bug831846.js3
-rw-r--r--js/src/jit-test/tests/basic/function-tosource-bug779694.js2
-rw-r--r--js/src/jit-test/tests/basic/function-tosource-constructor.js16
-rw-r--r--js/src/jit-test/tests/basic/function-tosource-getset.js10
-rw-r--r--js/src/jit-test/tests/basic/hasnativemethodpure-optimization.js21
-rw-r--r--js/src/jit-test/tests/basic/testAssigningWatchedDeletedProperty.js7
-rw-r--r--js/src/jit-test/tests/basic/testBug566556.js9
-rw-r--r--js/src/jit-test/tests/basic/testBug578044.js13
-rw-r--r--js/src/jit-test/tests/basic/testBug584650.js9
-rw-r--r--js/src/jit-test/tests/basic/testBug780288-1.js20
-rw-r--r--js/src/jit-test/tests/basic/testBug780288-2.js20
-rw-r--r--js/src/jit-test/tests/basic/testEvalCalledFromWatchOverSetter.js3
-rw-r--r--js/src/jit-test/tests/basic/testFunctionStatementAliasLocals.js4
-rw-r--r--js/src/jit-test/tests/basic/testLet.js2
-rw-r--r--js/src/jit-test/tests/basic/testNonStubGetter.js7
-rw-r--r--js/src/jit-test/tests/basic/testSettingWatchPointOnReadOnlyProp.js7
-rw-r--r--js/src/jit-test/tests/basic/testTrueShiftTrue.js16
-rw-r--r--js/src/jit-test/tests/basic/testWatchRecursion.js63
-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/ctypes/function-definition.js2
-rw-r--r--js/src/jit-test/tests/debug/Script-gc-02.js2
-rw-r--r--js/src/jit-test/tests/debug/Script-gc-03.js2
-rw-r--r--js/src/jit-test/tests/debug/Script-sourceStart-04.js4
-rw-r--r--js/src/jit-test/tests/debug/Source-text-02.js1
-rw-r--r--js/src/jit-test/tests/debug/wasm-12.js26
-rw-r--r--js/src/jit-test/tests/gc/bug-900405.js3
-rw-r--r--js/src/jit-test/tests/gc/bug-913261.js5
-rw-r--r--js/src/jit-test/tests/gc/bug-986864.js8
-rw-r--r--js/src/jit-test/tests/ion/bug1063182.js8
-rw-r--r--js/src/jit-test/tests/ion/bug1493900-1.js17
-rw-r--r--js/src/jit-test/tests/ion/bug1493900-2.js7
-rw-r--r--js/src/jit-test/tests/ion/bug772901.js2
-rw-r--r--js/src/jit-test/tests/ion/bug774257-1.js8
-rw-r--r--js/src/jit-test/tests/ion/bug774257-2.js10
-rw-r--r--js/src/jit-test/tests/ion/bug779631.js9
-rw-r--r--js/src/jit-test/tests/ion/bug783590.js1
-rw-r--r--js/src/jit-test/tests/ion/unboxed-objects-invalidate.js16
-rw-r--r--js/src/jit-test/tests/jaeger/bug550665.js8
-rw-r--r--js/src/jit-test/tests/jaeger/bug557063.js7
-rw-r--r--js/src/jit-test/tests/jaeger/bug588338.js1
-rw-r--r--js/src/jit-test/tests/jaeger/bug625438.js10
-rw-r--r--js/src/jit-test/tests/jaeger/bug630366.js7
-rw-r--r--js/src/jit-test/tests/jaeger/recompile/bug641225.js1
-rw-r--r--js/src/jit-test/tests/latin1/assorted.js6
-rw-r--r--js/src/jit-test/tests/latin1/function.js4
-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/pic/fuzz1.js4
-rw-r--r--js/src/jit-test/tests/pic/fuzz3.js3
-rw-r--r--js/src/jit-test/tests/pic/watch1.js7
-rw-r--r--js/src/jit-test/tests/pic/watch1a.js17
-rw-r--r--js/src/jit-test/tests/pic/watch2.js8
-rw-r--r--js/src/jit-test/tests/pic/watch2a.js18
-rw-r--r--js/src/jit-test/tests/pic/watch3.js7
-rw-r--r--js/src/jit-test/tests/pic/watch3a.js19
-rw-r--r--js/src/jit-test/tests/pic/watch3b.js20
-rw-r--r--js/src/jit-test/tests/pic/watch4.js9
-rw-r--r--js/src/jit-test/tests/pic/watch5.js27
-rw-r--r--js/src/jit-test/tests/profiler/AutoEntryMonitor-01.js2
-rw-r--r--js/src/jit-test/tests/profiler/bug1140643.js14
-rw-r--r--js/src/jit/AliasAnalysisShared.cpp9
-rw-r--r--js/src/jit/BacktrackingAllocator.cpp44
-rw-r--r--js/src/jit/BacktrackingAllocator.h45
-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.cpp546
-rw-r--r--js/src/jit/BaselineIC.h90
-rw-r--r--js/src/jit/BaselineInspector.cpp79
-rw-r--r--js/src/jit/BaselineInspector.h11
-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.cpp515
-rw-r--r--js/src/jit/CodeGenerator.h12
-rw-r--r--js/src/jit/InlinableNatives.h1
-rw-r--r--js/src/jit/Ion.cpp2
-rw-r--r--js/src/jit/IonAnalysis.cpp61
-rw-r--r--js/src/jit/IonAnalysis.h7
-rw-r--r--js/src/jit/IonBuilder.cpp709
-rw-r--r--js/src/jit/IonBuilder.h53
-rw-r--r--js/src/jit/IonCaches.cpp477
-rw-r--r--js/src/jit/IonCaches.h12
-rw-r--r--js/src/jit/IonCode.h11
-rw-r--r--js/src/jit/JitOptions.cpp3
-rw-r--r--js/src/jit/JitOptions.h3
-rw-r--r--js/src/jit/Lowering.cpp82
-rw-r--r--js/src/jit/Lowering.h8
-rw-r--r--js/src/jit/MCallOptimize.cpp155
-rw-r--r--js/src/jit/MIR.cpp258
-rw-r--r--js/src/jit/MIR.h400
-rw-r--r--js/src/jit/MOpcodes.h8
-rw-r--r--js/src/jit/MacroAssembler.cpp315
-rw-r--r--js/src/jit/MacroAssembler.h17
-rw-r--r--js/src/jit/OptimizationTracking.cpp2
-rw-r--r--js/src/jit/ProcessExecutableMemory.cpp8
-rw-r--r--js/src/jit/ProcessExecutableMemory.h8
-rw-r--r--js/src/jit/RangeAnalysis.cpp2
-rw-r--r--js/src/jit/Recover.cpp37
-rw-r--r--js/src/jit/ScalarReplacement.cpp150
-rw-r--r--js/src/jit/SharedIC.cpp40
-rw-r--r--js/src/jit/StupidAllocator.cpp1
-rw-r--r--js/src/jit/VMFunctions.cpp51
-rw-r--r--js/src/jit/VMFunctions.h13
-rw-r--r--js/src/jit/arm/Assembler-arm.cpp7
-rw-r--r--js/src/jit/arm/MacroAssembler-arm.cpp9
-rw-r--r--js/src/jit/arm64/Architecture-arm64.h10
-rw-r--r--js/src/jit/shared/Assembler-shared.h10
-rw-r--r--js/src/jit/shared/IonAssemblerBuffer.h4
-rw-r--r--js/src/jit/shared/LIR-shared.h169
-rw-r--r--js/src/jit/shared/LOpcodes-shared.h8
-rw-r--r--js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h35
-rw-r--r--js/src/jit/x86/Assembler-x86.h13
-rw-r--r--js/src/jit/x86/BaseAssembler-x86.h8
-rw-r--r--js/src/jit/x86/MacroAssembler-x86.cpp31
-rw-r--r--js/src/js.msg20
-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/testGCAllocator.cpp12
-rw-r--r--js/src/jsapi-tests/testUbiNode.cpp2
-rw-r--r--js/src/jsapi.cpp268
-rw-r--r--js/src/jsapi.h205
-rw-r--r--js/src/jsarray.cpp584
-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.cpp228
-rw-r--r--js/src/jscntxt.h10
-rw-r--r--js/src/jscompartment.cpp64
-rw-r--r--js/src/jscompartment.h37
-rwxr-xr-xjs/src/jsdate.cpp2
-rw-r--r--js/src/jsexn.cpp206
-rw-r--r--js/src/jsexn.h6
-rw-r--r--js/src/jsfriendapi.cpp19
-rw-r--r--js/src/jsfriendapi.h73
-rw-r--r--js/src/jsfun.cpp349
-rw-r--r--js/src/jsfun.h51
-rw-r--r--js/src/jsfuninlines.h2
-rw-r--r--js/src/jsgc.cpp203
-rw-r--r--js/src/jsgc.h11
-rw-r--r--js/src/jsiter.cpp64
-rw-r--r--js/src/jsmath.cpp3
-rw-r--r--js/src/jsnativestack.cpp31
-rw-r--r--js/src/jsnum.cpp7
-rw-r--r--js/src/jsobj.cpp332
-rw-r--r--js/src/jsobj.h67
-rw-r--r--js/src/jsobjinlines.h82
-rw-r--r--js/src/json.cpp4
-rw-r--r--js/src/jsopcode.cpp15
-rw-r--r--js/src/jsscript.cpp99
-rw-r--r--js/src/jsscript.h109
-rw-r--r--js/src/jsscriptinlines.h12
-rw-r--r--js/src/jsstr.cpp52
-rw-r--r--js/src/jsstr.h6
-rw-r--r--js/src/jstypes.h8
-rw-r--r--js/src/jsversion.h1
-rw-r--r--js/src/jswatchpoint.cpp245
-rw-r--r--js/src/jswatchpoint.h90
-rw-r--r--js/src/jswrapper.h11
-rw-r--r--js/src/moz.build12
-rw-r--r--js/src/old-configure.in12
-rw-r--r--js/src/proxy/BaseProxyHandler.cpp14
-rw-r--r--js/src/proxy/CrossCompartmentWrapper.cpp4
-rw-r--r--js/src/proxy/OpaqueCrossCompartmentWrapper.cpp8
-rw-r--r--js/src/proxy/Proxy.cpp33
-rw-r--r--js/src/proxy/Proxy.h3
-rw-r--r--js/src/proxy/ScriptedProxyHandler.cpp4
-rw-r--r--js/src/proxy/SecurityWrapper.cpp19
-rw-r--r--js/src/proxy/Wrapper.cpp4
-rw-r--r--js/src/shell/OSObject.cpp5
-rw-r--r--js/src/shell/js.cpp37
-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_2017/Function/Object-toSource.js370
-rw-r--r--js/src/tests/ecma_2017/Function/browser.js0
-rw-r--r--js/src/tests/ecma_2017/Function/shell.js0
-rw-r--r--js/src/tests/ecma_5/Array/frozen-dense-array.js19
-rw-r--r--js/src/tests/ecma_5/extensions/watch-array-length.js41
-rw-r--r--js/src/tests/ecma_5/extensions/watch-inherited-property.js38
-rw-r--r--js/src/tests/ecma_5/extensions/watch-replaced-setter.js46
-rw-r--r--js/src/tests/ecma_5/extensions/watch-setter-become-setter.js44
-rw-r--r--js/src/tests/ecma_5/extensions/watch-value-prop-becoming-setter.js43
-rw-r--r--js/src/tests/ecma_5/extensions/watchpoint-deletes-JSPropertyOp-setter.js56
-rw-r--r--js/src/tests/ecma_6/Array/unscopables.js2
-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/Generators/runtime.js2
-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_5/Object/regress-362872-01.js41
-rw-r--r--js/src/tests/js1_5/Object/regress-362872-02.js24
-rw-r--r--js/src/tests/js1_5/Regress/regress-127243.js75
-rw-r--r--js/src/tests/js1_5/Regress/regress-213482.js29
-rw-r--r--js/src/tests/js1_5/Regress/regress-240577.js37
-rw-r--r--js/src/tests/js1_5/Regress/regress-355341.js29
-rw-r--r--js/src/tests/js1_5/Regress/regress-355344.js49
-rw-r--r--js/src/tests/js1_5/Regress/regress-361467.js32
-rw-r--r--js/src/tests/js1_5/Regress/regress-361617.js35
-rw-r--r--js/src/tests/js1_5/Regress/regress-385393-06.js28
-rw-r--r--js/src/tests/js1_5/Regress/regress-506567.js45
-rw-r--r--js/src/tests/js1_5/Scope/regress-185485.js2
-rw-r--r--js/src/tests/js1_5/extensions/regress-303277.js19
-rw-r--r--js/src/tests/js1_5/extensions/regress-355339.js32
-rw-r--r--js/src/tests/js1_5/extensions/regress-361346.js22
-rw-r--r--js/src/tests/js1_5/extensions/regress-361360.js32
-rw-r--r--js/src/tests/js1_5/extensions/regress-361552.js27
-rw-r--r--js/src/tests/js1_5/extensions/regress-361558.js19
-rw-r--r--js/src/tests/js1_5/extensions/regress-361571.js38
-rw-r--r--js/src/tests/js1_5/extensions/regress-361856.js35
-rw-r--r--js/src/tests/js1_5/extensions/regress-361964.js54
-rw-r--r--js/src/tests/js1_5/extensions/regress-385134.js38
-rw-r--r--js/src/tests/js1_5/extensions/regress-385393-09.js18
-rw-r--r--js/src/tests/js1_5/extensions/regress-390597.js42
-rw-r--r--js/src/tests/js1_5/extensions/regress-420612.js21
-rw-r--r--js/src/tests/js1_5/extensions/regress-435345-01.js100
-rw-r--r--js/src/tests/js1_5/extensions/regress-454040.js25
-rw-r--r--js/src/tests/js1_5/extensions/regress-454142.js30
-rw-r--r--js/src/tests/js1_5/extensions/regress-455413.js24
-rw-r--r--js/src/tests/js1_5/extensions/regress-465145.js24
-rwxr-xr-xjs/src/tests/js1_5/extensions/regress-472787.js31
-rw-r--r--js/src/tests/js1_5/extensions/regress-488995.js46
-rw-r--r--js/src/tests/js1_6/Regress/regress-476655.js40
-rw-r--r--js/src/tests/js1_6/extensions/regress-457521.js24
-rw-r--r--js/src/tests/js1_6/extensions/regress-479567.js33
-rw-r--r--js/src/tests/js1_7/extensions/regress-354945-01.js2
-rw-r--r--js/src/tests/js1_7/extensions/regress-354945-02.js2
-rw-r--r--js/src/tests/js1_7/extensions/regress-453955.js31
-rw-r--r--js/src/tests/js1_7/extensions/regress-473282.js20
-rw-r--r--js/src/tests/js1_7/geniter/regress-352197.js2
-rw-r--r--js/src/tests/js1_7/regress/regress-385133-01.js37
-rw-r--r--js/src/tests/js1_8/extensions/regress-394709.js51
-rw-r--r--js/src/tests/js1_8/extensions/regress-481989.js19
-rw-r--r--js/src/tests/js1_8/genexps/regress-683738.js8
-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_1/extensions/regress-452498-193.js34
-rw-r--r--js/src/tests/js1_8_1/extensions/regress-452498-196.js9
-rw-r--r--js/src/tests/js1_8_1/extensions/regress-520572.js41
-rw-r--r--js/src/tests/js1_8_1/regress/regress-452498-160.js5
-rw-r--r--js/src/tests/js1_8_5/extensions/regress-604781-1.js24
-rw-r--r--js/src/tests/js1_8_5/extensions/regress-604781-2.js13
-rw-r--r--js/src/tests/js1_8_5/extensions/regress-627984-1.js16
-rw-r--r--js/src/tests/js1_8_5/extensions/regress-627984-2.js15
-rw-r--r--js/src/tests/js1_8_5/extensions/regress-627984-3.js14
-rw-r--r--js/src/tests/js1_8_5/extensions/regress-627984-4.js15
-rw-r--r--js/src/tests/js1_8_5/extensions/regress-627984-5.js13
-rw-r--r--js/src/tests/js1_8_5/extensions/regress-627984-6.js15
-rw-r--r--js/src/tests/js1_8_5/extensions/regress-627984-7.js9
-rw-r--r--js/src/tests/js1_8_5/extensions/regress-631723.js10
-rw-r--r--js/src/tests/js1_8_5/extensions/regress-636697.js11
-rw-r--r--js/src/tests/js1_8_5/extensions/regress-637985.js8
-rw-r--r--js/src/tests/js1_8_5/extensions/regress-691746.js11
-rw-r--r--js/src/tests/js1_8_5/extensions/watch-undefined-setter.js19
-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/js1_8_5/regress/regress-533876.js23
-rw-r--r--js/src/tests/js1_8_5/regress/regress-548276.js10
-rw-r--r--js/src/tests/js1_8_5/regress/regress-584355.js2
-rw-r--r--js/src/tests/js1_8_5/regress/regress-584648.js16
-rw-r--r--js/src/tests/js1_8_5/regress/regress-635195.js8
-rw-r--r--js/src/tests/js1_8_5/regress/regress-636394.js10
-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
-rwxr-xr-xjs/src/tests/user.js1
-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/ArrayBufferObject.h9
-rw-r--r--js/src/vm/AsyncFunction.cpp2
-rw-r--r--js/src/vm/Caches.h16
-rw-r--r--js/src/vm/CommonPropertyNames.h40
-rw-r--r--js/src/vm/Debugger.cpp58
-rw-r--r--js/src/vm/Debugger.h3
-rw-r--r--js/src/vm/EnvironmentObject.cpp38
-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.h17
-rw-r--r--js/src/vm/Interpreter.cpp92
-rw-r--r--js/src/vm/Interpreter.h3
-rw-r--r--js/src/vm/JSONParser.cpp4
-rw-r--r--js/src/vm/NativeObject-inl.h40
-rw-r--r--js/src/vm/NativeObject.cpp70
-rw-r--r--js/src/vm/NativeObject.h74
-rw-r--r--js/src/vm/ObjectGroup-inl.h14
-rw-r--r--js/src/vm/ObjectGroup.cpp197
-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.cpp19
-rw-r--r--js/src/vm/ReceiverGuard.h5
-rw-r--r--js/src/vm/RegExpObject.cpp34
-rw-r--r--js/src/vm/RegExpObject.h11
-rw-r--r--js/src/vm/Runtime.cpp17
-rw-r--r--js/src/vm/Runtime.h25
-rw-r--r--js/src/vm/Scope.cpp28
-rw-r--r--js/src/vm/Scope.h131
-rw-r--r--js/src/vm/SelfHosting.cpp30
-rw-r--r--js/src/vm/Shape.cpp166
-rw-r--r--js/src/vm/Shape.h5
-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/Stopwatch.cpp48
-rw-r--r--js/src/vm/Stopwatch.h30
-rw-r--r--js/src/vm/String.h11
-rw-r--r--js/src/vm/StringObject-inl.h18
-rw-r--r--js/src/vm/StringObject.h2
-rw-r--r--js/src/vm/Time.cpp9
-rw-r--r--js/src/vm/TypeInference-inl.h5
-rw-r--r--js/src/vm/TypeInference.cpp314
-rw-r--r--js/src/vm/TypeInference.h89
-rw-r--r--js/src/vm/TypedArrayCommon.h21
-rw-r--r--js/src/vm/TypedArrayObject.cpp11
-rw-r--r--js/src/vm/UnboxedObject-inl.h840
-rw-r--r--js/src/vm/UnboxedObject.cpp2152
-rw-r--r--js/src/vm/UnboxedObject.h531
-rw-r--r--js/src/wasm/AsmJS.cpp68
-rw-r--r--js/src/wasm/WasmBinaryConstants.h9
-rw-r--r--js/src/wasm/WasmJS.cpp14
-rw-r--r--js/src/wasm/WasmModule.cpp3
-rw-r--r--js/src/wasm/WasmSignalHandlers.cpp9
463 files changed, 11028 insertions, 17058 deletions
diff --git a/js/src/Makefile.in b/js/src/Makefile.in
index b007954b1..bc99e62b5 100644
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -138,23 +138,13 @@ distclean::
CFLAGS += $(MOZ_ZLIB_CFLAGS)
-# Silence warnings on AIX/HP-UX from non-GNU compilers
-ifndef GNU_CC
-ifeq ($(OS_ARCH),AIX)
-# Suppress warnings from xlC
-# 1540-1281: offsetof() on null non-POD types
-# 1540-1608: anonymous unions using static data members
-CFLAGS += -qsuppress=1540-1281 -qsuppress=1540-1608
-CXXFLAGS += -qsuppress=1540-1281 -qsuppress=1540-1608
-endif
-endif
ifeq ($(OS_ARCH),SunOS)
ifeq ($(TARGET_CPU),sparc)
ifdef GNU_CC
CFLAGS += -mcpu=v9
CXXFLAGS += -mcpu=v9
-endif # GNU_CC
+endif #GNU_CC
endif
endif
diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js
index 30e6fb35f..05fc41bc1 100644
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -1073,6 +1073,114 @@ function ArrayConcat(arg1) {
return A;
}
+// https://tc39.github.io/proposal-flatMap/
+// January 4, 2019
+function ArrayFlatMap(mapperFunction/*, thisArg*/) {
+ // Step 1.
+ var O = ToObject(this);
+
+ // Step 2.
+ var sourceLen = ToLength(O.length);
+
+ // Step 3.
+ if (!IsCallable(mapperFunction))
+ ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, mapperFunction));
+
+ // Step 4.
+ var T = arguments.length > 1 ? arguments[1] : undefined;
+
+ // Step 5.
+ var A = ArraySpeciesCreate(O, 0);
+
+ // Step 6.
+ FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, T);
+
+ // Step 7.
+ return A;
+}
+
+// https://tc39.github.io/proposal-flatMap/
+// January 4, 2019
+function ArrayFlat(/* depth */) {
+ // Step 1.
+ var O = ToObject(this);
+
+ // Step 2.
+ var sourceLen = ToLength(O.length);
+
+ // Step 3.
+ var depthNum = 1;
+
+ // Step 4.
+ if (arguments.length > 0 && arguments[0] !== undefined)
+ depthNum = ToInteger(arguments[0]);
+
+ // Step 5.
+ var A = ArraySpeciesCreate(O, 0);
+
+ // Step 6.
+ FlattenIntoArray(A, O, sourceLen, 0, depthNum);
+
+ // Step 7.
+ return A;
+}
+
+// https://tc39.github.io/proposal-flatMap/
+// January 4, 2019
+function FlattenIntoArray(target, source, sourceLen, start, depth, mapperFunction, thisArg) {
+ // Step 1.
+ var targetIndex = start;
+
+ // Steps 2-3.
+ for (var sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) {
+ // Steps 3.a-c.
+ if (sourceIndex in source) {
+ // Step 3.c.i.
+ var element = source[sourceIndex];
+
+ if (mapperFunction) {
+ // Step 3.c.ii.1.
+ assert(arguments.length === 7, "thisArg is present");
+
+ // Step 3.c.ii.2.
+ element = callContentFunction(mapperFunction, thisArg, element, sourceIndex, source);
+ }
+
+ // Step 3.c.iii.
+ var shouldFlatten = false;
+
+ // Step 3.c.iv.
+ if (depth > 0) {
+ // Step 3.c.iv.1.
+ shouldFlatten = IsArray(element);
+ }
+
+ // Step 3.c.v.
+ if (shouldFlatten) {
+ // Step 3.c.v.1.
+ var elementLen = ToLength(element.length);
+
+ // Step 3.c.v.2.
+ // Recursive call to walk the depth.
+ targetIndex = FlattenIntoArray(target, element, elementLen, targetIndex, depth - 1);
+ } else {
+ // Step 3.c.vi.1.
+ if (targetIndex >= MAX_NUMERIC_INDEX)
+ ThrowTypeError(JSMSG_TOO_LONG_ARRAY);
+
+ // Step 3.c.vi.2.
+ _DefineDataProperty(target, targetIndex, element);
+
+ // Step 3.c.vi.3.
+ targetIndex++;
+ }
+ }
+ }
+
+ // Step 4.
+ return targetIndex;
+}
+
function ArrayStaticConcat(arr, arg1) {
if (arguments.length < 1)
ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.concat');
diff --git a/js/src/builtin/AtomicsObject.cpp b/js/src/builtin/AtomicsObject.cpp
index 08777fd51..ceee83349 100644
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -789,7 +789,7 @@ js::atomics_wait(JSContext* cx, unsigned argc, Value* vp)
// and it provides the necessary memory fence.
AutoLockFutexAPI lock;
- SharedMem<int32_t*>(addr) = view->viewDataShared().cast<int32_t*>() + offset;
+ SharedMem<int32_t*> addr = view->viewDataShared().cast<int32_t*>() + offset;
if (jit::AtomicOperations::loadSafeWhenRacy(addr) != value) {
r.setString(cx->names().futexNotEqual);
return true;
@@ -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..1612f0f6b 100644
--- a/js/src/builtin/IntlTimeZoneData.h
+++ b/js/src/builtin/IntlTimeZoneData.h
@@ -1,5 +1,5 @@
// Generated by make_intl_data.py. DO NOT EDIT.
-// tzdata version = 2018e
+// tzdata version = 2019c
#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 333cb3e11..575bab0b0 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;
@@ -768,7 +768,7 @@ AssertModuleScopesMatch(ModuleObject* module)
}
void
-ModuleObject::fixEnvironmentsAfterCompartmentMerge(JSContext* cx)
+ModuleObject::fixEnvironmentsAfterCompartmentMerge()
{
AssertModuleScopesMatch(this);
initialEnvironment().fixEnclosingEnvironmentAfterCompartmentMerge(script()->global());
@@ -927,7 +927,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)
@@ -1000,7 +1000,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 51a428271..22db762ac 100644
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -237,7 +237,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 cd4ac122c..bfcc8d20e 100644
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -9,11 +9,13 @@
#include "mozilla/ArrayUtils.h"
#include "jscntxt.h"
+#include "jsstr.h"
#include "builtin/Eval.h"
#include "frontend/BytecodeCompiler.h"
#include "jit/InlinableNatives.h"
#include "js/UniquePtr.h"
+#include "vm/AsyncFunction.h"
#include "vm/StringBuffer.h"
#include "jsobjinlines.h"
@@ -124,6 +126,27 @@ obj_toSource(JSContext* cx, unsigned argc, Value* vp)
return true;
}
+template <typename CharT>
+static bool
+Consume(const CharT*& s, const CharT* e, const char *chars)
+{
+ size_t len = strlen(chars);
+ if (s + len >= e)
+ return false;
+ if (!EqualChars(s, chars, len))
+ return false;
+ s += len;
+ return true;
+}
+
+template <typename CharT>
+static void
+ConsumeSpaces(const CharT*& s, const CharT* e)
+{
+ while (*s == ' ' && s < e)
+ s++;
+}
+
/*
* Given a function source string, return the offset and length of the part
* between '(function $name' and ')'.
@@ -133,37 +156,53 @@ static bool
ArgsAndBodySubstring(mozilla::Range<const CharT> chars, size_t* outOffset, size_t* outLen)
{
const CharT* const start = chars.begin().get();
- const CharT* const end = chars.end().get();
const CharT* s = start;
+ const CharT* e = chars.end().get();
- uint8_t parenChomp = 0;
- if (s[0] == '(') {
- s++;
- parenChomp = 1;
- }
-
- /* Try to jump "function" keyword. */
- s = js_strchr_limit(s, ' ', end);
- if (!s)
+ if (s == e)
return false;
- /*
- * Jump over the function's name: it can't be encoded as part
- * of an ECMA getter or setter.
- */
- s = js_strchr_limit(s, '(', end);
- if (!s)
- return false;
+ // Remove enclosing parentheses.
+ if (*s == '(' && *(e - 1) == ')') {
+ s++;
+ e--;
+ }
- if (*s == ' ')
+ (void) Consume(s, e, "async");
+ ConsumeSpaces(s, e);
+ (void) (Consume(s, e, "function") || Consume(s, e, "get") || Consume(s, e, "set"));
+ ConsumeSpaces(s, e);
+ (void) Consume(s, e, "*");
+ ConsumeSpaces(s, e);
+
+ // Jump over the function's name.
+ if (Consume(s, e, "[")) {
+ s = js_strchr_limit(s, ']', e);
+ if (!s)
+ return false;
s++;
+ ConsumeSpaces(s, e);
+ if (*s != '(')
+ return false;
+ } else {
+ s = js_strchr_limit(s, '(', e);
+ if (!s)
+ return false;
+ }
*outOffset = s - start;
- *outLen = end - s - parenChomp;
+ *outLen = e - s;
MOZ_ASSERT(*outOffset + *outLen <= chars.length());
return true;
}
+enum class PropertyKind {
+ Getter,
+ Setter,
+ Method,
+ Normal
+};
+
JSString*
js::ObjectToSource(JSContext* cx, HandleObject obj)
{
@@ -182,59 +221,28 @@ js::ObjectToSource(JSContext* cx, HandleObject obj)
if (!buf.append('{'))
return nullptr;
- RootedValue v0(cx), v1(cx);
- MutableHandleValue val[2] = {&v0, &v1};
-
- RootedString str0(cx), str1(cx);
- MutableHandleString gsop[2] = {&str0, &str1};
-
AutoIdVector idv(cx);
if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_SYMBOLS, &idv))
return nullptr;
bool comma = false;
- for (size_t i = 0; i < idv.length(); ++i) {
- RootedId id(cx, idv[i]);
- Rooted<PropertyDescriptor> desc(cx);
- if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
- return nullptr;
-
- int valcnt = 0;
- if (desc.object()) {
- if (desc.isAccessorDescriptor()) {
- if (desc.hasGetterObject() && desc.getterObject()) {
- val[valcnt].setObject(*desc.getterObject());
- gsop[valcnt].set(cx->names().get);
- valcnt++;
- }
- if (desc.hasSetterObject() && desc.setterObject()) {
- val[valcnt].setObject(*desc.setterObject());
- gsop[valcnt].set(cx->names().set);
- valcnt++;
- }
- } else {
- valcnt = 1;
- val[0].set(desc.value());
- gsop[0].set(nullptr);
- }
- }
-
+ auto AddProperty = [cx, &comma, &buf](HandleId id, HandleValue val, PropertyKind kind) -> bool {
/* Convert id to a string. */
RootedString idstr(cx);
if (JSID_IS_SYMBOL(id)) {
RootedValue v(cx, SymbolValue(JSID_TO_SYMBOL(id)));
idstr = ValueToSource(cx, v);
if (!idstr)
- return nullptr;
+ return false;
} else {
RootedValue idv(cx, IdToValue(id));
idstr = ToString<CanGC>(cx, idv);
if (!idstr)
- return nullptr;
+ return false;
/*
- * If id is a string that's not an identifier, or if it's a negative
- * integer, then it must be quoted.
+ * If id is a string that's not an identifier, or if it's a
+ * negative integer, then it must be quoted.
*/
if (JSID_IS_ATOM(id)
? !IsIdentifier(JSID_TO_ATOM(id))
@@ -242,28 +250,65 @@ js::ObjectToSource(JSContext* cx, HandleObject obj)
{
idstr = QuoteString(cx, idstr, char16_t('\''));
if (!idstr)
- return nullptr;
+ return false;
}
}
- for (int j = 0; j < valcnt; j++) {
- /* Convert val[j] to its canonical source form. */
- JSString* valsource = ValueToSource(cx, val[j]);
- if (!valsource)
- return nullptr;
+ RootedString valsource(cx, ValueToSource(cx, val));
+ if (!valsource)
+ return false;
- RootedLinearString valstr(cx, valsource->ensureLinear(cx));
- if (!valstr)
- return nullptr;
+ RootedLinearString valstr(cx, valsource->ensureLinear(cx));
+ if (!valstr)
+ return false;
- size_t voffset = 0;
- size_t vlength = valstr->length();
+ if (comma && !buf.append(", "))
+ return false;
+ comma = true;
+
+ size_t voffset, vlength;
+
+ // Methods and accessors can return exact syntax of source, that fits
+ // into property without adding property name or "get"/"set" prefix.
+ // Use the exact syntax when the following conditions are met:
+ //
+ // * It's a function object
+ // (exclude proxies)
+ // * Function's kind and property's kind are same
+ // (this can be false for dynamically defined properties)
+ // * Function has explicit name
+ // (this can be false for computed property and dynamically defined
+ // properties)
+ // * Function's name and property's name are same
+ // (this can be false for dynamically defined properties)
+ if (kind == PropertyKind::Getter || kind == PropertyKind::Setter ||
+ kind == PropertyKind::Method)
+ {
+ RootedFunction fun(cx);
+ if (val.toObject().is<JSFunction>()) {
+ fun = &val.toObject().as<JSFunction>();
+ // Method's case should be checked on caller.
+ if (((fun->isGetter() && kind == PropertyKind::Getter) ||
+ (fun->isSetter() && kind == PropertyKind::Setter) ||
+ kind == PropertyKind::Method) &&
+ fun->explicitName())
+ {
+ bool result;
+ if (!EqualStrings(cx, fun->explicitName(), idstr, &result))
+ return false;
- /*
- * Remove '(function ' from the beginning of valstr and ')' from the
- * end so that we can put "get" in front of the function definition.
- */
- if (gsop[j] && IsFunctionObject(val[j])) {
+ if (result) {
+ if (!buf.append(valstr))
+ return false;
+ return true;
+ }
+ }
+ }
+
+ {
+ // When falling back try to generate a better string
+ // representation by skipping the prelude, and also removing
+ // the enclosing parentheses.
bool success;
JS::AutoCheckCannotGC nogc;
if (valstr->hasLatin1Chars())
@@ -271,29 +316,90 @@ js::ObjectToSource(JSContext* cx, HandleObject obj)
else
success = ArgsAndBodySubstring(valstr->twoByteRange(nogc), &voffset, &vlength);
if (!success)
- gsop[j].set(nullptr);
+ kind = PropertyKind::Normal;
}
- if (comma && !buf.append(", "))
- return nullptr;
- comma = true;
+ if (kind == PropertyKind::Getter) {
+ if (!buf.append("get "))
+ return false;
+ } else if (kind == PropertyKind::Setter) {
+ if (!buf.append("set "))
+ return false;
+ } else if (kind == PropertyKind::Method && fun) {
+ if (IsWrappedAsyncFunction(fun)) {
+ if (!buf.append("async "))
+ return false;
+ }
- if (gsop[j]) {
- if (!buf.append(gsop[j]) || !buf.append(' '))
- return nullptr;
+ if (fun->isStarGenerator()) {
+ if (!buf.append('*'))
+ return false;
+ }
}
- if (JSID_IS_SYMBOL(id) && !buf.append('['))
- return nullptr;
- if (!buf.append(idstr))
- return nullptr;
- if (JSID_IS_SYMBOL(id) && !buf.append(']'))
- return nullptr;
- if (!buf.append(gsop[j] ? ' ' : ':'))
- return nullptr;
+ }
+ bool needsBracket = JSID_IS_SYMBOL(id);
+ if (needsBracket && !buf.append('['))
+ return false;
+ if (!buf.append(idstr))
+ return false;
+ if (needsBracket && !buf.append(']'))
+ return false;
+
+ if (kind == PropertyKind::Getter || kind == PropertyKind::Setter ||
+ kind == PropertyKind::Method)
+ {
if (!buf.appendSubstring(valstr, voffset, vlength))
- return nullptr;
+ return false;
+ } else {
+ if (!buf.append(':'))
+ return false;
+ if (!buf.append(valstr))
+ return false;
+ }
+ return true;
+ };
+
+ RootedId id(cx);
+ Rooted<PropertyDescriptor> desc(cx);
+ RootedValue val(cx);
+ RootedFunction fun(cx);
+ for (size_t i = 0; i < idv.length(); ++i) {
+ id = idv[i];
+ if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
+ return nullptr;
+
+ if (!desc.object())
+ continue;
+
+ if (desc.isAccessorDescriptor()) {
+ if (desc.hasGetterObject() && desc.getterObject()) {
+ val.setObject(*desc.getterObject());
+ if (!AddProperty(id, val, PropertyKind::Getter))
+ return nullptr;
+ }
+ if (desc.hasSetterObject() && desc.setterObject()) {
+ val.setObject(*desc.setterObject());
+ if (!AddProperty(id, val, PropertyKind::Setter))
+ return nullptr;
+ }
+ continue;
+ }
+
+ val.set(desc.value());
+ if (IsFunctionObject(val, fun.address())) {
+ if (IsWrappedAsyncFunction(fun))
+ fun = GetUnwrappedAsyncFunction(fun);
+
+ if (fun->isMethod()) {
+ if (!AddProperty(id, val, PropertyKind::Method))
+ return nullptr;
+ continue;
+ }
}
+
+ if (!AddProperty(id, val, PropertyKind::Normal))
+ return nullptr;
}
if (!buf.append('}'))
@@ -419,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)
{
@@ -474,97 +568,6 @@ obj_setPrototypeOf(JSContext* cx, unsigned argc, Value* vp)
return true;
}
-#if JS_HAS_OBJ_WATCHPOINT
-
-bool
-js::WatchHandler(JSContext* cx, JSObject* obj_, jsid id_, const JS::Value& old,
- JS::Value* nvp, void* closure)
-{
- RootedObject obj(cx, obj_);
- RootedId id(cx, id_);
-
- /* Avoid recursion on (obj, id) already being watched on cx. */
- AutoResolving resolving(cx, obj, id, AutoResolving::WATCH);
- if (resolving.alreadyStarted())
- return true;
-
- FixedInvokeArgs<3> args(cx);
-
- args[0].set(IdToValue(id));
- args[1].set(old);
- args[2].set(*nvp);
-
- RootedValue callable(cx, ObjectValue(*static_cast<JSObject*>(closure)));
- RootedValue thisv(cx, ObjectValue(*obj));
- RootedValue rv(cx);
- if (!Call(cx, callable, thisv, args, &rv))
- return false;
-
- *nvp = rv;
- return true;
-}
-
-static bool
-obj_watch(JSContext* cx, unsigned argc, Value* vp)
-{
- CallArgs args = CallArgsFromVp(argc, vp);
-
- RootedObject obj(cx, ToObject(cx, args.thisv()));
- if (!obj)
- return false;
-
- if (!GlobalObject::warnOnceAboutWatch(cx, obj))
- return false;
-
- if (args.length() <= 1) {
- ReportMissingArg(cx, args.calleev(), 1);
- return false;
- }
-
- RootedObject callable(cx, ValueToCallable(cx, args[1], args.length() - 2));
- if (!callable)
- return false;
-
- RootedId propid(cx);
- if (!ValueToId<CanGC>(cx, args[0], &propid))
- return false;
-
- if (!WatchProperty(cx, obj, propid, callable))
- return false;
-
- args.rval().setUndefined();
- return true;
-}
-
-static bool
-obj_unwatch(JSContext* cx, unsigned argc, Value* vp)
-{
- CallArgs args = CallArgsFromVp(argc, vp);
-
- RootedObject obj(cx, ToObject(cx, args.thisv()));
- if (!obj)
- return false;
-
- if (!GlobalObject::warnOnceAboutWatch(cx, obj))
- return false;
-
- RootedId id(cx);
- if (args.length() != 0) {
- if (!ValueToId<CanGC>(cx, args[0], &id))
- return false;
- } else {
- id = JSID_VOID;
- }
-
- if (!UnwatchProperty(cx, obj, id))
- return false;
-
- args.rval().setUndefined();
- return true;
-}
-
-#endif /* JS_HAS_OBJ_WATCHPOINT */
-
/* ECMA 15.2.4.5. */
bool
js::obj_hasOwnProperty(JSContext* cx, unsigned argc, Value* vp)
@@ -1195,11 +1198,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),
-#if JS_HAS_OBJ_WATCHPOINT
- JS_FN(js_watch_str, obj_watch, 2,0),
- JS_FN(js_unwatch_str, obj_unwatch, 1,0),
-#endif
+ JS_SELF_HOSTED_FN(js_valueOf_str, "Object_valueOf", 0,0),
JS_FN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0),
JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0),
JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0),
@@ -1314,8 +1313,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/String.js b/js/src/builtin/String.js
index e5b2ad552..f830b1aa2 100644
--- a/js/src/builtin/String.js
+++ b/js/src/builtin/String.js
@@ -828,16 +828,16 @@ function String_static_trim(string) {
return callFunction(std_String_trim, string);
}
-function String_static_trimLeft(string) {
+function String_static_trimStart(string) {
if (arguments.length < 1)
- ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.trimLeft');
- return callFunction(std_String_trimLeft, string);
+ ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.trimStart');
+ return callFunction(std_String_trimStart, string);
}
-function String_static_trimRight(string) {
+function String_static_trimEnd(string) {
if (arguments.length < 1)
- ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.trimRight');
- return callFunction(std_String_trimRight, string);
+ ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.trimEnd');
+ return callFunction(std_String_trimEnd, string);
}
function String_static_toLocaleLowerCase(string) {
diff --git a/js/src/builtin/SymbolObject.cpp b/js/src/builtin/SymbolObject.cpp
index cf48402d6..8fa860ef3 100644
--- a/js/src/builtin/SymbolObject.cpp
+++ b/js/src/builtin/SymbolObject.cpp
@@ -33,6 +33,7 @@ SymbolObject::create(JSContext* cx, JS::HandleSymbol symbol)
}
const JSPropertySpec SymbolObject::properties[] = {
+ JS_PSG("description", descriptionGetter, 0),
JS_PS_END
};
@@ -52,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;
@@ -227,6 +228,34 @@ SymbolObject::toPrimitive(JSContext* cx, unsigned argc, Value* vp)
return CallNonGenericMethod<IsSymbol, valueOf_impl>(cx, args);
}
+// ES2019 Stage 4 Draft / November 28, 2018
+// Symbol description accessor
+// See: https://tc39.github.io/proposal-Symbol-description/
+bool
+SymbolObject::descriptionGetter_impl(JSContext* cx, const CallArgs& args)
+{
+ // Get symbol object pointer.
+ HandleValue thisv = args.thisv();
+ MOZ_ASSERT(IsSymbol(thisv));
+ Rooted<Symbol*> sym(cx, thisv.isSymbol()
+ ? thisv.toSymbol()
+ : thisv.toObject().as<SymbolObject>().unbox());
+
+ // Return the symbol's description if present, otherwise return undefined.
+ if (JSString* str = sym->description())
+ args.rval().setString(str);
+ else
+ args.rval().setUndefined();
+ return true;
+}
+
+bool
+SymbolObject::descriptionGetter(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<IsSymbol, descriptionGetter_impl>(cx, args);
+}
+
JSObject*
js::InitSymbolClass(JSContext* cx, HandleObject obj)
{
diff --git a/js/src/builtin/SymbolObject.h b/js/src/builtin/SymbolObject.h
index 0f204b494..e10b42d53 100644
--- a/js/src/builtin/SymbolObject.h
+++ b/js/src/builtin/SymbolObject.h
@@ -52,6 +52,10 @@ class SymbolObject : public NativeObject
static MOZ_MUST_USE bool valueOf(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE bool toPrimitive(JSContext* cx, unsigned argc, Value* vp);
+ // Properties defined on Symbol.prototype.
+ static MOZ_MUST_USE bool descriptionGetter_impl(JSContext* cx, const CallArgs& args);
+ static MOZ_MUST_USE bool descriptionGetter(JSContext* cx, unsigned argc, Value *vp);
+
static const JSPropertySpec properties[];
static const JSFunctionSpec methods[];
static const JSFunctionSpec staticMethods[];
diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp
index 373b6c9ed..4363c7aed 100644
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -239,12 +239,11 @@ GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp)
value = BooleanValue(true);
if (!JS_SetProperty(cx, info, "intl-api", value))
return false;
-
-#if defined(SOLARIS)
+#ifdef XP_SOLARIS
value = BooleanValue(false);
#else
value = BooleanValue(true);
-#endif
+#endif
if (!JS_SetProperty(cx, info, "mapped-array-buffer", value))
return false;
@@ -3222,13 +3221,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;
@@ -3323,7 +3322,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) {
@@ -4027,7 +4027,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();
@@ -4046,6 +4046,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'])",
@@ -4580,6 +4606,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 ae74f01bf..50bf0b836 100644
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -652,7 +652,7 @@ ArrayMetaTypeDescr::create(JSContext* cx,
if (!CreateTraceList(cx, obj))
return nullptr;
- if (!cx->zone()->typeDescrObjects.put(obj)) {
+ if (!cx->zone()->addTypeDescrObject(cx, obj)) {
ReportOutOfMemory(cx);
return nullptr;
}
@@ -993,8 +993,8 @@ StructMetaTypeDescr::create(JSContext* cx,
if (!CreateTraceList(cx, descr))
return nullptr;
- if (!cx->zone()->typeDescrObjects.put(descr) ||
- !cx->zone()->typeDescrObjects.put(fieldTypeVec))
+ if (!cx->zone()->addTypeDescrObject(cx, descr) ||
+ !cx->zone()->addTypeDescrObject(cx, fieldTypeVec))
{
ReportOutOfMemory(cx);
return nullptr;
@@ -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;
@@ -1165,10 +1165,8 @@ DefineSimpleTypeDescr(JSContext* cx,
if (!CreateTraceList(cx, descr))
return false;
- if (!cx->zone()->typeDescrObjects.put(descr)) {
- ReportOutOfMemory(cx);
+ if (!cx->zone()->addTypeDescrObject(cx, descr))
return false;
- }
return true;
}
@@ -1187,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;
@@ -1199,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);
@@ -1218,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,
@@ -1242,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;
@@ -1319,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);
}
/******************************************************************************
@@ -2218,7 +2215,6 @@ const ObjectOps TypedObject::objectOps_ = {
TypedObject::obj_setProperty,
TypedObject::obj_getOwnPropertyDescriptor,
TypedObject::obj_deleteProperty,
- nullptr, nullptr, /* watch/unwatch */
nullptr, /* getElements */
TypedObject::obj_enumerate,
nullptr, /* thisValue */
diff --git a/js/src/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/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp
index 0facd0009..aed1114bd 100644
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -20,7 +20,7 @@
#include <float.h>
#endif
-#if defined(SOLARIS)
+#if defined(XP_SOLARIS)
#include <ieeefp.h>
#endif
diff --git a/js/src/ctypes/libffi/src/x86/win32.S b/js/src/ctypes/libffi/src/x86/win32.S
index daf0e799c..4f702e8b1 100644
--- a/js/src/ctypes/libffi/src/x86/win32.S
+++ b/js/src/ctypes/libffi/src/x86/win32.S
@@ -1158,8 +1158,24 @@ L_ffi_closure_SYSV_inner$stub:
.byte 0x7c /* .sleb128 -4; CIE Data Alignment Factor */
.byte 0x8 /* CIE RA Column */
#ifdef __PIC__
- .byte 0x1 /* .uleb128 0x1; Augmentation size */
- .byte 0x1b /* FDE Encoding (pcrel sdata4) */
+# if defined __sun__ && defined __svr4__
+/* 32-bit Solaris 2/x86 uses datarel encoding for PIC. GNU ld before 2.22
+ doesn't correctly sort .eh_frame_hdr with mixed encodings, so match this. */
+# define FDE_ENCODING 0x30 /* datarel */
+# define FDE_ENCODE(X) X@GOTOFF
+# else
+# define FDE_ENCODING 0x1b /* pcrel sdata4 */
+# if defined HAVE_AS_X86_PCREL
+# define FDE_ENCODE(X) X-.
+# else
+# define FDE_ENCODE(X) X@rel
+# endif
+# endif
+#else
+# define FDE_ENCODING 0 /* absolute */
+# define FDE_ENCODE(X) X
+.byte 0x1 /* .uleb128 0x1; Augmentation size */
+.byte FDE_ENCODING
#endif
.byte 0xc /* DW_CFA_def_cfa CFA = r4 + 4 = 4(%esp) */
.byte 0x4 /* .uleb128 0x4 */
@@ -1176,7 +1192,7 @@ L_ffi_closure_SYSV_inner$stub:
#if defined __PIC__ && defined HAVE_AS_X86_PCREL
.long .LFB1-. /* FDE initial location */
#else
- .long .LFB1
+ .long FDE_ENCODE(.LFB1)
#endif
.long .LFE1-.LFB1 /* FDE address range */
#ifdef __PIC__
@@ -1207,7 +1223,7 @@ L_ffi_closure_SYSV_inner$stub:
#if defined __PIC__ && defined HAVE_AS_X86_PCREL
.long .LFB3-. /* FDE initial location */
#else
- .long .LFB3
+ .long FDE_ENCODE(.LFB3)
#endif
.long .LFE3-.LFB3 /* FDE address range */
#ifdef __PIC__
@@ -1240,7 +1256,7 @@ L_ffi_closure_SYSV_inner$stub:
#if defined __PIC__ && defined HAVE_AS_X86_PCREL
.long .LFB4-. /* FDE initial location */
#else
- .long .LFB4
+ .long FDE_ENCODE(.LFB4)
#endif
.long .LFE4-.LFB4 /* FDE address range */
#ifdef __PIC__
@@ -1278,7 +1294,7 @@ L_ffi_closure_SYSV_inner$stub:
#if defined __PIC__ && defined HAVE_AS_X86_PCREL
.long .LFB5-. /* FDE initial location */
#else
- .long .LFB5
+ .long FDE_ENCODE(.LFB5)
#endif
.long .LFE5-.LFB5 /* FDE address range */
#ifdef __PIC__
diff --git a/js/src/ctypes/libffi/testsuite/libffi.call/float2.c b/js/src/ctypes/libffi/testsuite/libffi.call/float2.c
index a0b296cf4..dfdef0598 100644
--- a/js/src/ctypes/libffi/testsuite/libffi.call/float2.c
+++ b/js/src/ctypes/libffi/testsuite/libffi.call/float2.c
@@ -32,21 +32,11 @@ int main (void)
f = 3.14159;
-#if 1
- /* This is ifdef'd out for now. long double support under SunOS/gcc
- is pretty much non-existent. You'll get the odd bus error in library
- routines like printf(). */
printf ("%Lf\n", ldblit(f));
-#endif
ld = 666;
ffi_call(&cif, FFI_FN(ldblit), &ld, values);
-#if 1
- /* This is ifdef'd out for now. long double support under SunOS/gcc
- is pretty much non-existent. You'll get the odd bus error in library
- routines like printf(). */
printf ("%Lf, %Lf, %Lf, %Lf\n", ld, ldblit(f), ld - ldblit(f), LDBL_EPSILON);
-#endif
/* These are not always the same!! Check for a reasonable delta */
if (ld - ldblit(f) < LDBL_EPSILON)
diff --git a/js/src/ds/LifoAlloc.h b/js/src/ds/LifoAlloc.h
index f349cd476..b4e9c3418 100644
--- a/js/src/ds/LifoAlloc.h
+++ b/js/src/ds/LifoAlloc.h
@@ -15,6 +15,8 @@
#include "mozilla/TemplateLib.h"
#include "mozilla/TypeTraits.h"
+#include <new>
+
// This data structure supports stacky LIFO allocation (mark/release and
// LifoAllocScope). It does not maintain one contiguous segment; instead, it
// maintains a bunch of linked memory segments. In order to prevent malloc/free
@@ -285,6 +287,20 @@ class LifoAlloc
return allocImpl(n);
}
+ template<typename T, typename... Args>
+ MOZ_ALWAYS_INLINE T*
+ allocInSize(size_t n, Args&&... args)
+ {
+ MOZ_ASSERT(n >= sizeof(T), "must request enough space to store a T");
+ static_assert(alignof(T) <= detail::LIFO_ALLOC_ALIGN,
+ "LifoAlloc must provide enough alignment to store T");
+ void* ptr = alloc(n);
+ if (!ptr)
+ return nullptr;
+
+ return new (ptr) T(mozilla::Forward<Args>(args)...);
+ }
+
MOZ_ALWAYS_INLINE
void* allocInfallible(size_t n) {
AutoEnterOOMUnsafeRegion oomUnsafe;
diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp
index 76afe80b1..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());
+
+ // 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);
@@ -244,8 +250,15 @@ BytecodeCompiler::createSourceAndParser(Maybe<uint32_t> parameterListEnd /* = No
bool
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());
+ sourceObject, /* sourceStart = */ 0, sourceBuffer.length(),
+ toStringStart, toStringEnd);
return script != nullptr;
}
@@ -286,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()) {
@@ -456,7 +470,7 @@ BytecodeCompiler::compileStandaloneFunction(MutableHandleFunction fun,
if (fn->pn_funbox->function()->isInterpreted()) {
MOZ_ASSERT(fun == fn->pn_funbox->function());
- if (!createScript())
+ if (!createScript(fn->pn_funbox->toStringStart, fn->pn_funbox->toStringEnd))
return false;
Maybe<BytecodeEmitter> emitter;
@@ -650,7 +664,8 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
MOZ_ASSERT(sourceObject);
Rooted<JSScript*> script(cx, JSScript::Create(cx, options, sourceObject,
- lazy->begin(), lazy->end()));
+ lazy->begin(), lazy->end(),
+ lazy->toStringStart(), lazy->toStringEnd()));
if (!script)
return false;
diff --git a/js/src/frontend/BytecodeCompiler.h b/js/src/frontend/BytecodeCompiler.h
index 72e967639..0bc1ab2ab 100644
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -109,6 +109,8 @@ IsIdentifier(JSLinearString* str);
* As above, but taking chars + length.
*/
bool
+IsIdentifier(const char* chars, size_t length);
+bool
IsIdentifier(const char16_t* chars, size_t length);
/* True if str is a keyword. Defined in TokenStream.cpp. */
diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
index c7c615ccf..309d6c290 100644
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -319,7 +319,7 @@ ScopeKindIsInBody(ScopeKind kind)
static inline void
MarkAllBindingsClosedOver(LexicalScope::Data& data)
{
- BindingName* names = data.names;
+ TrailingNamesArray& names = data.trailingNames;
for (uint32_t i = 0; i < data.length; i++)
names[i] = BindingName(names[i].name(), true);
}
@@ -2260,12 +2260,14 @@ BytecodeEmitter::locationOfNameBoundInFunctionScope(JSAtom* name, EmitterScope*
bool
BytecodeEmitter::emitCheck(ptrdiff_t delta, ptrdiff_t* offset)
{
- *offset = code().length();
+ size_t oldLength = code().length();
+ *offset = ptrdiff_t(oldLength);
- // Start it off moderately large to avoid repeated resizings early on.
- // ~98% of cases fit within 1024 bytes.
- if (code().capacity() == 0 && !code().reserve(1024))
- return false;
+ size_t newLength = oldLength + size_t(delta);
+ if (MOZ_UNLIKELY(newLength > MaxBytecodeLength)) {
+ ReportAllocationOverflow(cx);
+ return false;
+ }
if (!code().growBy(delta)) {
ReportOutOfMemory(cx);
@@ -3067,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:
@@ -3536,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;
}
@@ -3546,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;
+ }
}
/*
@@ -3559,9 +3564,11 @@ BytecodeEmitter::maybeSetSourceMap()
if (parser->options().sourceMapURL()) {
// Warn about the replacement, but use the new one.
if (parser->ss->hasSourceMapURL()) {
- if(!parser->report(ParseWarning, false, nullptr, JSMSG_ALREADY_HAS_PRAGMA,
- parser->ss->filename(), "//# sourceMappingURL"))
+ if (!parser->reportNoOffset(ParseWarning, false, JSMSG_ALREADY_HAS_PRAGMA,
+ parser->ss->filename(), "//# sourceMappingURL"))
+ {
return false;
+ }
}
if (!parser->ss->setSourceMapURL(cx, parser->options().sourceMapURL()))
@@ -3586,33 +3593,34 @@ 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;
}
bool
-BytecodeEmitter::reportStrictWarning(ParseNode* pn, unsigned errorNumber, ...)
+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()->reportStrictWarningErrorNumberVA(pos.begin, errorNumber, args);
+ bool result = tokenStream().reportExtraWarningErrorNumberVA(nullptr, pos.begin,
+ errorNumber, args);
va_end(args);
return result;
}
@@ -3620,12 +3628,12 @@ BytecodeEmitter::reportStrictWarning(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;
}
@@ -4595,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;
@@ -5806,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 {
@@ -5843,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;
}
@@ -6238,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;
@@ -6277,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;
@@ -6641,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
@@ -6743,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;
@@ -6755,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.
@@ -7265,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;
}
@@ -7352,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;
@@ -7834,7 +8027,9 @@ 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->bufStart, funbox->bufEnd,
+ funbox->toStringStart,
+ funbox->toStringEnd));
if (!script)
return false;
@@ -8677,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;
@@ -8704,13 +8900,13 @@ BytecodeEmitter::emitStatement(ParseNode* pn)
}
if (directive) {
- if (!reportStrictWarning(pn2, JSMSG_CONTRARY_NONDIRECTIVE, directive))
+ if (!reportExtraWarning(pn2, JSMSG_CONTRARY_NONDIRECTIVE, directive))
return false;
}
} else {
current->currentLine = parser->tokenStream.srcCoords.lineNum(pn2->pn_pos.begin);
current->lastColumn = 0;
- if (!reportStrictWarning(pn2, JSMSG_USELESS_EXPR))
+ if (!reportExtraWarning(pn2, JSMSG_USELESS_EXPR))
return false;
}
}
@@ -8978,7 +9174,8 @@ BytecodeEmitter::isRestParameter(ParseNode* pn, bool* result)
if (bindings->nonPositionalFormalStart > 0) {
// |paramName| can be nullptr when the rest destructuring syntax is
// used: `function f(...[]) {}`.
- JSAtom* paramName = bindings->names[bindings->nonPositionalFormalStart - 1].name();
+ JSAtom* paramName =
+ bindings->trailingNames[bindings->nonPositionalFormalStart - 1].name();
*result = paramName && name == paramName;
return true;
}
@@ -9027,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);
/*
@@ -9186,7 +9383,7 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn)
return false;
}
- if (!emitArray(args, argc, JSOP_SPREADCALLARRAY))
+ if (!emitArray(args, argc))
return false;
if (optCodeEmitted) {
@@ -9206,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) ||
@@ -9310,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;
@@ -9378,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()))
@@ -9388,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())
@@ -9423,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;
@@ -9446,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;
@@ -9536,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;
@@ -9580,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;
@@ -9596,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;
}
@@ -9677,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)
{
/*
@@ -9692,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) {
@@ -9713,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;
@@ -10214,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))
@@ -10268,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);
@@ -10390,7 +10618,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
break;
case PNK_COMMA:
- if (!emitSequenceExpr(pn))
+ if (!emitSequenceExpr(pn, valueUsage))
return false;
break;
@@ -10412,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;
@@ -10525,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;
@@ -10631,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;
@@ -10682,28 +10911,31 @@ 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
AllocSrcNote(ExclusiveContext* cx, SrcNotesVector& notes, unsigned* index)
{
- // Start it off moderately large to avoid repeated resizings early on.
- // ~99% of cases fit within 256 bytes.
- if (notes.capacity() == 0 && !notes.reserve(256))
- return false;
+ size_t oldLength = notes.length();
+ if (MOZ_UNLIKELY(oldLength + 1 > MaxSrcNotesLength)) {
+ ReportAllocationOverflow(cx);
+ return false;
+ }
+
if (!notes.growBy(1)) {
ReportOutOfMemory(cx);
return false;
}
- *index = notes.length() - 1;
+ *index = oldLength;
return true;
}
@@ -10829,6 +11061,10 @@ BytecodeEmitter::setSrcNoteOffset(unsigned index, unsigned which, ptrdiff_t offs
/* Maybe this offset was already set to a four-byte value. */
if (!(*sn & SN_4BYTE_OFFSET_FLAG)) {
/* Insert three dummy bytes that will be overwritten shortly. */
+ if (MOZ_UNLIKELY(notes.length() + 3 > MaxSrcNotesLength)) {
+ ReportAllocationOverflow(cx);
+ return false;
+ }
jssrcnote dummy = 0;
if (!(sn = notes.insert(sn, dummy)) ||
!(sn = notes.insert(sn, dummy)) ||
diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h
index 04307c8c1..595ee6405 100644
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -109,10 +109,12 @@ struct CGYieldOffsetList {
void finish(YieldOffsetArray& array, uint32_t prologueLength);
};
-// Use zero inline elements because these go on the stack and affect how many
-// nested functions are possible.
-typedef Vector<jsbytecode, 0> BytecodeVector;
-typedef Vector<jssrcnote, 0> SrcNotesVector;
+static size_t MaxBytecodeLength = INT32_MAX;
+static size_t MaxSrcNotesLength = INT32_MAX;
+
+// Have a few inline elements to avoid heap allocation for tiny sequences.
+typedef Vector<jsbytecode, 256> BytecodeVector;
+typedef Vector<jssrcnote, 64> SrcNotesVector;
// Linked list of jump instructions that need to be patched. The linked list is
// stored in the bytes of the incomplete bytecode that will be patched, so no
@@ -169,6 +171,11 @@ struct JumpList {
void patchAll(jsbytecode* code, JumpTarget target);
};
+enum class ValueUsage {
+ WantValue,
+ IgnoreValue
+};
+
struct MOZ_STACK_CLASS BytecodeEmitter
{
class TDZCheckCache;
@@ -354,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; }
@@ -388,7 +395,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
}
bool reportError(ParseNode* pn, unsigned errorNumber, ...);
- bool reportStrictWarning(ParseNode* pn, unsigned errorNumber, ...);
+ bool reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...);
bool reportStrictModeError(ParseNode* pn, unsigned errorNumber, ...);
// If pn contains a useful expression, return true with *answer set to true.
@@ -432,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.
@@ -519,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);
@@ -531,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,
@@ -658,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.
@@ -675,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();
@@ -722,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 0fd137796..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)
{
@@ -670,9 +689,18 @@ class FullParseHandler
pn->setDirectRHSAnonFunction(true);
}
- ParseNode* newFunctionDefinition() {
- return new_<CodeNode>(PNK_FUNCTION, pos());
+ ParseNode* newFunctionStatement() {
+ return new_<CodeNode>(PNK_FUNCTION, JSOP_NOP, pos());
+ }
+
+ ParseNode* newFunctionExpression() {
+ return new_<CodeNode>(PNK_FUNCTION, JSOP_LAMBDA, pos());
}
+
+ ParseNode* newArrowFunction() {
+ return new_<CodeNode>(PNK_FUNCTION, JSOP_LAMBDA_ARROW, pos());
+ }
+
bool setComprehensionLambdaBody(ParseNode* pn, ParseNode* body) {
MOZ_ASSERT(body->isKind(PNK_STATEMENTLIST));
ParseNode* paramsBody = newList(PNK_PARAMSBODY, body);
@@ -699,7 +727,7 @@ class FullParseHandler
}
ParseNode* newModule() {
- return new_<CodeNode>(PNK_MODULE, pos());
+ return new_<CodeNode>(PNK_MODULE, JSOP_NOP, pos());
}
ParseNode* newLexicalScope(LexicalScope::Data* bindings, ParseNode* body) {
@@ -845,7 +873,7 @@ class FullParseHandler
MOZ_MUST_USE ParseNode* setLikelyIIFE(ParseNode* pn) {
return parenthesize(pn);
}
- void setPrologue(ParseNode* pn) {
+ void setInDirectivePrologue(ParseNode* pn) {
pn->pn_prologue = true;
}
@@ -861,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 ece3a45df..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;
@@ -838,7 +840,7 @@ LexicalScopeNode::dump(int indent)
if (!isEmptyScope()) {
LexicalScope::Data* bindings = scopeBindings();
for (uint32_t i = 0; i < bindings->length; i++) {
- JSAtom* name = bindings->names[i].name();
+ JSAtom* name = bindings->trailingNames[i].name();
JS::AutoCheckCannotGC nogc;
if (name->hasLatin1Chars())
DumpName(name->latin1Chars(nogc), name->length());
diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h
index c58dab431..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. */
@@ -926,10 +929,14 @@ struct ListNode : public ParseNode
struct CodeNode : public ParseNode
{
- CodeNode(ParseNodeKind kind, const TokenPos& pos)
- : ParseNode(kind, JSOP_NOP, PN_CODE, pos)
+ CodeNode(ParseNodeKind kind, JSOp op, const TokenPos& pos)
+ : ParseNode(kind, op, PN_CODE, pos)
{
MOZ_ASSERT(kind == PNK_FUNCTION || kind == PNK_MODULE);
+ MOZ_ASSERT_IF(kind == PNK_MODULE, op == JSOP_NOP);
+ MOZ_ASSERT(op == JSOP_NOP || // statement, module
+ op == JSOP_LAMBDA_ARROW || // arrow function
+ op == JSOP_LAMBDA); // expression, method, comprehension, accessor, &c.
MOZ_ASSERT(!pn_body);
MOZ_ASSERT(!pn_objbox);
}
@@ -1137,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:
@@ -1292,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>() ||
@@ -1357,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 623379f61..0c279591f 100644
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -19,6 +19,10 @@
#include "frontend/Parser.h"
+#include "mozilla/Sprintf.h"
+
+#include <new>
+
#include "jsapi.h"
#include "jsatom.h"
#include "jscntxt.h"
@@ -60,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, errno) \
+// 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) { \
- report(ParseError, false, null(), errno); \
+ 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
@@ -104,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:
@@ -125,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
@@ -187,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;
}
@@ -330,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;
}
@@ -439,7 +461,8 @@ UsedNameTracker::rewind(RewindToken token)
}
FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* traceListHead,
- JSFunction* fun, Directives directives, bool extraWarnings,
+ JSFunction* fun, uint32_t toStringStart,
+ Directives directives, bool extraWarnings,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind)
: ObjectBox(fun, traceListHead),
SharedContext(cx, Kind::ObjectBox, directives, extraWarnings),
@@ -452,6 +475,8 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* trac
bufEnd(0),
startLine(1),
startColumn(0),
+ toStringStart(toStringStart),
+ toStringEnd(0),
length(0),
generatorKindBits_(GeneratorKindAsBits(generatorKind)),
asyncKindBits_(AsyncKindAsBits(asyncKind)),
@@ -470,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
@@ -521,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)
@@ -566,68 +598,166 @@ FunctionBox::initWithEnclosingScope(Scope* enclosingScope)
computeInWith(enclosingScope);
}
-template <typename ParseHandler>
+void
+ParserBase::error(unsigned errorNumber, ...)
+{
+ va_list args;
+ va_start(args, errorNumber);
+#ifdef DEBUG
+ bool result =
+#endif
+ tokenStream.reportCompileErrorNumberVA(nullptr, pos().begin, JSREPORT_ERROR,
+ errorNumber, args);
+ MOZ_ASSERT(!result, "reporting an error returned true?");
+ va_end(args);
+}
+
+void
+ParserBase::errorWithNotes(UniquePtr<JSErrorNotes> notes, unsigned errorNumber, ...)
+{
+ va_list args;
+ va_start(args, errorNumber);
+#ifdef DEBUG
+ bool result =
+#endif
+ 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);
+}
+
bool
-Parser<ParseHandler>::reportHelper(ParseReportKind kind, bool strict, uint32_t offset,
- unsigned errorNumber, va_list args)
+ParserBase::warning(unsigned errorNumber, ...)
{
- bool result = false;
- switch (kind) {
- case ParseError:
- result = tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_ERROR, errorNumber, args);
- break;
- case ParseWarning:
- result =
- tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_WARNING, errorNumber, args);
- break;
- case ParseExtraWarning:
- result = tokenStream.reportStrictWarningErrorNumberVA(offset, errorNumber, args);
- break;
- case ParseStrictError:
- result = tokenStream.reportStrictModeErrorNumberVA(offset, strict, errorNumber, args);
- break;
- }
+ va_list args;
+ va_start(args, errorNumber);
+ bool result =
+ tokenStream.reportCompileErrorNumberVA(nullptr, pos().begin, JSREPORT_WARNING,
+ errorNumber, args);
+ va_end(args);
return result;
}
-template <typename ParseHandler>
bool
-Parser<ParseHandler>::report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...)
+ParserBase::warningAt(uint32_t offset, unsigned errorNumber, ...)
{
- uint32_t offset = (pn ? handler.getPosition(pn) : pos()).begin;
+ va_list args;
+ va_start(args, errorNumber);
+ bool result =
+ tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_WARNING,
+ errorNumber, args);
+ va_end(args);
+ return result;
+}
+bool
+ParserBase::extraWarning(unsigned errorNumber, ...)
+{
va_list args;
va_start(args, errorNumber);
- bool result = reportHelper(kind, strict, offset, errorNumber, args);
+ bool result = tokenStream.reportExtraWarningErrorNumberVA(nullptr, pos().begin,
+ errorNumber, args);
va_end(args);
return result;
}
-template <typename ParseHandler>
bool
-Parser<ParseHandler>::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...)
+ParserBase::extraWarningAt(uint32_t offset, unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
- bool result = reportHelper(kind, strict, TokenStream::NoOffset, errorNumber, args);
+
+ bool result =
+ tokenStream.reportExtraWarningErrorNumberVA(nullptr, offset, errorNumber, args);
+
va_end(args);
return result;
}
-template <typename ParseHandler>
bool
-Parser<ParseHandler>::reportWithOffset(ParseReportKind kind, bool strict, uint32_t offset,
- unsigned errorNumber, ...)
+ParserBase::strictModeError(unsigned errorNumber, ...)
+{
+ va_list args;
+ va_start(args, errorNumber);
+ bool res =
+ tokenStream.reportStrictModeErrorNumberVA(nullptr, pos().begin, pc->sc()->strict(),
+ errorNumber, args);
+ va_end(args);
+ return res;
+}
+
+bool
+ParserBase::strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...)
+{
+ va_list args;
+ va_start(args, errorNumber);
+ bool res =
+ tokenStream.reportStrictModeErrorNumberVA(nullptr, offset, pc->sc()->strict(),
+ errorNumber, args);
+ va_end(args);
+ return res;
+}
+
+bool
+ParserBase::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
- bool result = reportHelper(kind, strict, offset, errorNumber, args);
+ bool result = false;
+ uint32_t offset = TokenStream::NoOffset;
+ switch (kind) {
+ case ParseError:
+ result = tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_ERROR,
+ errorNumber, args);
+ break;
+ case ParseWarning:
+ result =
+ tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_WARNING,
+ errorNumber, args);
+ break;
+ case ParseExtraWarning:
+ result = tokenStream.reportExtraWarningErrorNumberVA(nullptr, offset,
+ errorNumber, args);
+ break;
+ case ParseStrictError:
+ result = tokenStream.reportStrictModeErrorNumberVA(nullptr, offset, strict,
+ errorNumber, args);
+ break;
+ }
va_end(args);
return result;
}
template <>
-bool
+inline bool
Parser<FullParseHandler>::abortIfSyntaxParser()
{
handler.disableSyntaxParser();
@@ -635,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),
@@ -666,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>
@@ -687,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>
@@ -736,7 +894,8 @@ Parser<ParseHandler>::newObjectBox(JSObject* obj)
template <typename ParseHandler>
FunctionBox*
-Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun, Directives inheritedDirectives,
+Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart,
+ Directives inheritedDirectives,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
bool tryAnnexB)
{
@@ -751,8 +910,9 @@ Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun, Directives inheri
* function.
*/
FunctionBox* funbox =
- alloc.new_<FunctionBox>(context, alloc, traceListHead, fun, inheritedDirectives,
- options().extraWarningsOption, generatorKind, asyncKind);
+ alloc.new_<FunctionBox>(context, alloc, traceListHead, fun, toStringStart,
+ inheritedDirectives, options().extraWarningsOption,
+ generatorKind, asyncKind);
if (!funbox) {
ReportOutOfMemory(context);
return nullptr;
@@ -820,8 +980,7 @@ Parser<ParseHandler>::parse()
if (!tokenStream.getToken(&tt, TokenStream::Operand))
return null();
if (tt != TOK_EOF) {
- report(ParseError, false, null(), JSMSG_GARBAGE_AFTER_INPUT,
- "script", TokenKindToDesc(tt));
+ error(JSMSG_GARBAGE_AFTER_INPUT, "script", TokenKindToDesc(tt));
return null();
}
if (foldConstants) {
@@ -832,56 +991,19 @@ Parser<ParseHandler>::parse()
return pn;
}
-template <typename ParseHandler>
-bool
-Parser<ParseHandler>::reportBadReturn(Node pn, ParseReportKind kind,
- unsigned errnum, unsigned anonerrnum)
-{
- JSAutoByteString name;
- if (JSAtom* atom = pc->functionBox()->function()->explicitName()) {
- if (!AtomToPrintableString(context, atom, &name))
- return false;
- } else {
- errnum = anonerrnum;
- }
- return report(kind, pc->sc()->strict(), pn, errnum, name.ptr());
-}
-
/*
* 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 reportWithOffset(ParseStrictError, pc->sc()->strict(), pos.begin,
- JSMSG_BAD_BINDING, bytes.ptr());
- }
-
- return true;
+ name != context->names().yield &&
+ !IsStrictReservedWord(name);
}
/*
@@ -908,14 +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;
- reportWithOffset(ParseError, false, 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
@@ -930,12 +1109,13 @@ Parser<ParseHandler>::reportRedeclaration(HandlePropertyName name, DeclarationKi
template <typename ParseHandler>
bool
Parser<ParseHandler>::notePositionalFormalParameter(Node fn, HandlePropertyName name,
+ uint32_t beginPos,
bool disallowDuplicateParams,
bool* duplicatedParam)
{
if (AddDeclaredNamePtr p = pc->functionScope().lookupDeclaredNameForAdd(name)) {
if (disallowDuplicateParams) {
- report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS);
+ error(JSMSG_BAD_DUP_ARGS);
return false;
}
@@ -947,17 +1127,14 @@ Parser<ParseHandler>::notePositionalFormalParameter(Node fn, HandlePropertyName
JSAutoByteString bytes;
if (!AtomToPrintableString(context, name, &bytes))
return false;
- if (!report(ParseStrictError, pc->sc()->strict(), null(),
- JSMSG_DUPLICATE_FORMAL, bytes.ptr()))
- {
+ if (!strictModeError(JSMSG_DUPLICATE_FORMAL, bytes.ptr()))
return false;
- }
}
*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;
}
@@ -970,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;
}
@@ -1064,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));
@@ -1138,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;
}
@@ -1156,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
@@ -1187,11 +1396,11 @@ Parser<ParseHandler>::checkLexicalDeclarationDirectlyWithinBlock(ParseContext::S
if (!StatementKindIsBraced(stmt.kind()) &&
stmt.kind() != StatementKind::ForLoopLexicalHead)
{
- reportWithOffset(ParseError, false, pos.begin,
- stmt.kind() == StatementKind::Label
- ? JSMSG_LEXICAL_DECL_LABEL
- : JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,
- DeclarationKindString(kind));
+ errorAt(pos.begin,
+ stmt.kind() == StatementKind::Label
+ ? JSMSG_LEXICAL_DECL_LABEL
+ : JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,
+ DeclarationKindString(kind));
return false;
}
@@ -1208,36 +1417,52 @@ 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.
AddDeclaredNamePtr p = pc->functionScope().lookupDeclaredNameForAdd(name);
if (p) {
- report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS);
+ error(JSMSG_BAD_DUP_ARGS);
return false;
}
- if (!pc->functionScope().addDeclaredName(pc, p, name, kind))
+ if (!pc->functionScope().addDeclaredName(pc, p, name, kind, pos.begin))
return false;
break;
@@ -1247,7 +1472,7 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind
// Functions in block have complex allowances in sloppy mode for being
// labelled that other lexical declarations do not have. Those checks
// are more complex than calling checkLexicalDeclarationDirectlyWithin-
- // Block and are done in checkFunctionDefinition.
+ // Block and are done inline in callers.
ParseContext::Scope* scope = pc->innermostScope();
if (AddDeclaredNamePtr p = scope->lookupDeclaredNameForAdd(name)) {
@@ -1260,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;
}
@@ -1268,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;
}
@@ -1281,7 +1506,7 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind
// contain 'let'. (CatchParameter is the only lexical binding form
// without this restriction.)
if (name == context->names().let) {
- reportWithOffset(ParseError, false, pos.begin, JSMSG_LEXICAL_DECL_DEFINES_LET);
+ errorAt(pos.begin, JSMSG_LEXICAL_DECL_DEFINES_LET);
return false;
}
@@ -1308,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;
}
}
@@ -1325,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;
@@ -1440,8 +1665,7 @@ Parser<FullParseHandler>::checkStatementsEOF()
if (!tokenStream.peekToken(&tt, TokenStream::Operand))
return false;
if (tt != TOK_EOF) {
- report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
- "expression", TokenKindToDesc(tt));
+ error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt));
return false;
}
return true;
@@ -1451,16 +1675,26 @@ template <typename Scope>
static typename Scope::Data*
NewEmptyBindingData(ExclusiveContext* cx, LifoAlloc& alloc, uint32_t numBindings)
{
+ using Data = typename Scope::Data;
size_t allocSize = Scope::sizeOfData(numBindings);
- typename Scope::Data* bindings = static_cast<typename Scope::Data*>(alloc.alloc(allocSize));
- if (!bindings) {
+ auto* bindings = alloc.allocInSize<Data>(allocSize, numBindings);
+ if (!bindings)
ReportOutOfMemory(cx);
- return nullptr;
- }
- PodZero(bindings);
return bindings;
}
+/**
+ * Copy-construct |BindingName|s from |bindings| into |cursor|, then return
+ * the location one past the newly-constructed |BindingName|s.
+ */
+static MOZ_MUST_USE BindingName*
+FreshlyInitializeBindings(BindingName* cursor, const Vector<BindingName>& bindings)
+{
+ for (const BindingName& binding : bindings)
+ new (cursor++) BindingName(binding);
+ return cursor;
+}
+
template <>
Maybe<GlobalScope::Data*>
Parser<FullParseHandler>::newGlobalScopeData(ParseContext::Scope& scope)
@@ -1505,22 +1739,20 @@ Parser<FullParseHandler>::newGlobalScopeData(ParseContext::Scope& scope)
return Nothing();
// The ordering here is important. See comments in GlobalScope.
- BindingName* start = bindings->names;
+ BindingName* start = bindings->trailingNames.start();
BindingName* cursor = start;
- PodCopy(cursor, funs.begin(), funs.length());
- cursor += funs.length();
+ cursor = FreshlyInitializeBindings(cursor, funs);
bindings->varStart = cursor - start;
- PodCopy(cursor, vars.begin(), vars.length());
- cursor += vars.length();
+ cursor = FreshlyInitializeBindings(cursor, vars);
bindings->letStart = cursor - start;
- PodCopy(cursor, lets.begin(), lets.length());
- cursor += lets.length();
+ cursor = FreshlyInitializeBindings(cursor, lets);
bindings->constStart = cursor - start;
- PodCopy(cursor, consts.begin(), consts.length());
+ cursor = FreshlyInitializeBindings(cursor, consts);
+
bindings->length = numBindings;
}
@@ -1572,22 +1804,20 @@ Parser<FullParseHandler>::newModuleScopeData(ParseContext::Scope& scope)
return Nothing();
// The ordering here is important. See comments in ModuleScope.
- BindingName* start = bindings->names;
+ BindingName* start = bindings->trailingNames.start();
BindingName* cursor = start;
- PodCopy(cursor, imports.begin(), imports.length());
- cursor += imports.length();
+ cursor = FreshlyInitializeBindings(cursor, imports);
bindings->varStart = cursor - start;
- PodCopy(cursor, vars.begin(), vars.length());
- cursor += vars.length();
+ cursor = FreshlyInitializeBindings(cursor, vars);
bindings->letStart = cursor - start;
- PodCopy(cursor, lets.begin(), lets.length());
- cursor += lets.length();
+ cursor = FreshlyInitializeBindings(cursor, lets);
bindings->constStart = cursor - start;
- PodCopy(cursor, consts.begin(), consts.length());
+ cursor = FreshlyInitializeBindings(cursor, consts);
+
bindings->length = numBindings;
}
@@ -1623,16 +1853,16 @@ Parser<FullParseHandler>::newEvalScopeData(ParseContext::Scope& scope)
if (!bindings)
return Nothing();
- BindingName* start = bindings->names;
+ BindingName* start = bindings->trailingNames.start();
BindingName* cursor = start;
// Keep track of what vars are functions. This is only used in BCE to omit
// superfluous DEFVARs.
- PodCopy(cursor, funs.begin(), funs.length());
- cursor += funs.length();
+ cursor = FreshlyInitializeBindings(cursor, funs);
bindings->varStart = cursor - start;
- PodCopy(cursor, vars.begin(), vars.length());
+ cursor = FreshlyInitializeBindings(cursor, vars);
+
bindings->length = numBindings;
}
@@ -1697,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;
@@ -1719,18 +1946,17 @@ Parser<FullParseHandler>::newFunctionScopeData(ParseContext::Scope& scope, bool
return Nothing();
// The ordering here is important. See comments in FunctionScope.
- BindingName* start = bindings->names;
+ BindingName* start = bindings->trailingNames.start();
BindingName* cursor = start;
- PodCopy(cursor, positionalFormals.begin(), positionalFormals.length());
- cursor += positionalFormals.length();
+ cursor = FreshlyInitializeBindings(cursor, positionalFormals);
bindings->nonPositionalFormalStart = cursor - start;
- PodCopy(cursor, formals.begin(), formals.length());
- cursor += formals.length();
+ cursor = FreshlyInitializeBindings(cursor, formals);
bindings->varStart = cursor - start;
- PodCopy(cursor, vars.begin(), vars.length());
+ cursor = FreshlyInitializeBindings(cursor, vars);
+
bindings->length = numBindings;
}
@@ -1760,10 +1986,11 @@ Parser<FullParseHandler>::newVarScopeData(ParseContext::Scope& scope)
return Nothing();
// The ordering here is important. See comments in FunctionScope.
- BindingName* start = bindings->names;
+ BindingName* start = bindings->trailingNames.start();
BindingName* cursor = start;
- PodCopy(cursor, vars.begin(), vars.length());
+ cursor = FreshlyInitializeBindings(cursor, vars);
+
bindings->length = numBindings;
}
@@ -1808,14 +2035,14 @@ Parser<FullParseHandler>::newLexicalScopeData(ParseContext::Scope& scope)
return Nothing();
// The ordering here is important. See comments in LexicalScope.
- BindingName* cursor = bindings->names;
+ BindingName* cursor = bindings->trailingNames.start();
BindingName* start = cursor;
- PodCopy(cursor, lets.begin(), lets.length());
- cursor += lets.length();
+ cursor = FreshlyInitializeBindings(cursor, lets);
bindings->constStart = cursor - start;
- PodCopy(cursor, consts.begin(), consts.length());
+ cursor = FreshlyInitializeBindings(cursor, consts);
+
bindings->length = numBindings;
}
@@ -1900,7 +2127,7 @@ Parser<FullParseHandler>::evalBody(EvalSharedContext* evalsc)
// script.
if (hasUsedName(context->names().arguments)) {
if (IsArgumentsUsedInLegacyGenerator(context, pc->sc()->compilationEnclosingScope())) {
- report(ParseError, false, nullptr, JSMSG_BAD_GENEXP_BODY, js_arguments_str);
+ error(JSMSG_BAD_GENEXP_BODY, js_arguments_str);
return nullptr;
}
}
@@ -1986,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();
@@ -1998,7 +2225,7 @@ Parser<FullParseHandler>::moduleBody(ModuleSharedContext* modulesc)
if (!tokenStream.getToken(&tt, TokenStream::Operand))
return null();
if (tt != TOK_EOF) {
- report(ParseError, false, null(), JSMSG_GARBAGE_AFTER_INPUT, "module", TokenKindToDesc(tt));
+ error(JSMSG_GARBAGE_AFTER_INPUT, "module", TokenKindToDesc(tt));
return null();
}
@@ -2078,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();
}
@@ -2121,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();
@@ -2137,7 +2370,7 @@ Parser<ParseHandler>::finishFunctionScopes()
return false;
}
- if (funbox->function()->isNamedLambda()) {
+ if (funbox->function()->isNamedLambda() && !isStandaloneFunction) {
if (!propagateFreeNamesAndMarkClosedOverBindings(pc->namedLambdaScope()))
return false;
}
@@ -2147,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();
@@ -2170,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;
@@ -2182,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
@@ -2206,6 +2439,7 @@ Parser<SyntaxParseHandler>::finishFunction()
LazyScript* lazy = LazyScript::Create(context, fun, pc->closedOverBindingsForLazy(),
pc->innerFunctionsForLazy, versionNumber(),
funbox->bufStart, funbox->bufEnd,
+ funbox->toStringStart,
funbox->startLine, funbox->startColumn);
if (!lazy)
return false;
@@ -2218,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())
@@ -2259,7 +2495,33 @@ Parser<FullParseHandler>::standaloneFunction(HandleFunction fun,
{
MOZ_ASSERT(checkOptionsCalled);
- Node fn = handler.newFunctionDefinition();
+ // Skip prelude.
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt))
+ return null();
+ if (asyncKind == AsyncFunction) {
+ if (!tokenStream.getToken(&tt))
+ return null();
+ }
+ MOZ_ASSERT(tt == TOK_FUNCTION);
+
+ if (!tokenStream.getToken(&tt))
+ return null();
+ if (generatorKind == StarGenerator && asyncKind == SyncFunction) {
+ MOZ_ASSERT(tt == TOK_MUL);
+ if (!tokenStream.getToken(&tt))
+ return null();
+ }
+
+ // Skip function name, if present.
+ if (tt == TOK_NAME || tt == TOK_YIELD) {
+ MOZ_ASSERT(tokenStream.currentName() == fun->explicitName());
+ } else {
+ MOZ_ASSERT(fun->explicitName() == nullptr);
+ tokenStream.ungetToken();
+ }
+
+ Node fn = handler.newFunctionStatement();
if (!fn)
return null();
@@ -2268,8 +2530,8 @@ Parser<FullParseHandler>::standaloneFunction(HandleFunction fun,
return null();
fn->pn_body = argsbody;
- FunctionBox* funbox = newFunctionBox(fn, fun, inheritedDirectives, generatorKind,
- asyncKind, /* tryAnnexB = */ false);
+ FunctionBox* funbox = newFunctionBox(fn, fun, /* toStringStart = */ 0, inheritedDirectives,
+ generatorKind, asyncKind, /* tryAnnexB = */ false);
if (!funbox)
return null();
funbox->initStandaloneFunction(enclosingScope);
@@ -2280,19 +2542,17 @@ 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();
}
- TokenKind tt;
if (!tokenStream.getToken(&tt, TokenStream::Operand))
return null();
if (tt != TOK_EOF) {
- report(ParseError, false, null(), JSMSG_GARBAGE_AFTER_INPUT,
- "function body", TokenKindToDesc(tt));
+ error(JSMSG_GARBAGE_AFTER_INPUT, "function body", TokenKindToDesc(tt));
return null();
}
@@ -2344,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) {
@@ -2561,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>
@@ -2687,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;
}
@@ -2697,8 +2980,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
if (!tokenStream.getToken(&tt, firstTokenModifier))
return false;
if (tt != TOK_LP) {
- report(ParseError, false, null(),
- kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL);
+ error(kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL);
return false;
}
@@ -2730,13 +3012,13 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
AtomVector& positionalFormals = pc->positionalFormalParameterNames();
if (IsGetterKind(kind)) {
- report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s");
+ error(JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s");
return false;
}
while (true) {
if (hasRest) {
- report(ParseError, false, null(), JSMSG_PARAMETER_AFTER_REST);
+ error(JSMSG_PARAMETER_AFTER_REST);
return false;
}
@@ -2744,19 +3026,18 @@ 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)) {
- report(ParseError, false, null(),
- JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
+ error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
return false;
}
disallowDuplicateParams = true;
if (duplicatedParam) {
// Has duplicated args before the rest parameter.
- report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS);
+ error(JSMSG_BAD_DUP_ARGS);
return false;
}
@@ -2766,8 +3047,8 @@ 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) {
- report(ParseError, false, null(), JSMSG_NO_REST_NAME);
+ if (!TokenKindIsPossibleIdentifier(tt) && tt != TOK_LB && tt != TOK_LC) {
+ error(JSMSG_NO_REST_NAME);
return false;
}
}
@@ -2778,7 +3059,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
disallowDuplicateParams = true;
if (duplicatedParam) {
// Has duplicated args before the destructuring parameter.
- report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS);
+ error(JSMSG_BAD_DUP_ARGS);
return false;
}
@@ -2796,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
- report(ParseError, false, null(), 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;
}
@@ -2824,14 +3100,10 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
break;
}
-
- default:
- report(ParseError, false, null(), JSMSG_MISSING_FORMAL);
- return false;
}
if (positionalFormals.length() >= ARGNO_LIMIT) {
- report(ParseError, false, null(), JSMSG_TOO_MANY_FUN_ARGS);
+ error(JSMSG_TOO_MANY_FUN_ARGS);
return false;
}
@@ -2846,12 +3118,12 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
MOZ_ASSERT(!parenFreeArrow);
if (hasRest) {
- report(ParseError, false, null(), JSMSG_REST_WITH_DEFAULT);
+ error(JSMSG_REST_WITH_DEFAULT);
return false;
}
disallowDuplicateParams = true;
if (duplicatedParam) {
- report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS);
+ error(JSMSG_BAD_DUP_ARGS);
return false;
}
@@ -2895,12 +3167,11 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
return false;
if (tt != TOK_RP) {
if (IsSetterKind(kind)) {
- report(ParseError, false, null(),
- JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
+ error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
return false;
}
- report(ParseError, false, null(), JSMSG_PAREN_AFTER_FORMAL);
+ error(JSMSG_PAREN_AFTER_FORMAL);
return false;
}
}
@@ -2913,78 +3184,17 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
funbox->function()->setArgCount(positionalFormals.length());
} else if (IsSetterKind(kind)) {
- report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
+ error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
return false;
}
return true;
}
-template <typename ParseHandler>
-bool
-Parser<ParseHandler>::checkFunctionDefinition(HandleAtom funAtom, Node pn, FunctionSyntaxKind kind,
- GeneratorKind generatorKind, bool* tryAnnexB)
-{
- if (kind == Statement) {
- TokenPos pos = handler.getPosition(pn);
- RootedPropertyName funName(context, funAtom->asPropertyName());
-
- // In sloppy mode, Annex B.3.2 allows labelled function
- // declarations. Otherwise it is a parse error.
- ParseContext::Statement* declaredInStmt = pc->innermostStatement();
- if (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) {
- MOZ_ASSERT(!pc->sc()->strict(),
- "labeled functions shouldn't be parsed in strict mode");
-
- // Find the innermost non-label statement. Report an error if it's
- // unbraced: functions can't appear in it. Otherwise the statement
- // (or its absence) determines the scope the function's bound in.
- while (declaredInStmt && declaredInStmt->kind() == StatementKind::Label)
- declaredInStmt = declaredInStmt->enclosing();
-
- if (declaredInStmt && !StatementKindIsBraced(declaredInStmt->kind())) {
- reportWithOffset(ParseError, false, pos.begin, JSMSG_SLOPPY_FUNCTION_LABEL);
- return false;
- }
- }
-
- if (declaredInStmt) {
- MOZ_ASSERT(declaredInStmt->kind() != StatementKind::Label);
- MOZ_ASSERT(StatementKindIsBraced(declaredInStmt->kind()));
-
- if (!pc->sc()->strict() && generatorKind == NotGenerator) {
- // Under sloppy mode, try Annex B.3.3 semantics. If making an
- // additional 'var' binding of the same name does not throw an
- // early error, do so. This 'var' binding would be assigned
- // the function object when its declaration is reached, not at
- // the start of the block.
-
- if (!tryDeclareVarForAnnexBLexicalFunction(funName, tryAnnexB))
- return false;
- }
-
- if (!noteDeclaredName(funName, DeclarationKind::LexicalFunction, pos))
- return false;
- } else {
- if (!noteDeclaredName(funName, DeclarationKind::BodyLevelFunction, pos))
- return false;
-
- // Body-level functions in modules are always closed over.
- if (pc->atModuleLevel())
- pc->varScope().lookupDeclaredName(funName)->value()->setClosedOver();
- }
- } else {
- // A function expression does not introduce any binding.
- handler.setOp(pn, kind == Arrow ? JSOP_LAMBDA_ARROW : JSOP_LAMBDA);
- }
-
- return true;
-}
-
template <>
bool
-Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, FunctionSyntaxKind kind,
- bool tryAnnexB)
+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)
// that function, not any of its nested children. The initial syntax-only
@@ -2993,7 +3203,7 @@ Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, FunctionSyntaxKin
RootedFunction fun(context, handler.nextLazyInnerFunction());
MOZ_ASSERT(!fun->isLegacyGenerator());
- FunctionBox* funbox = newFunctionBox(pn, fun, Directives(/* strict = */ false),
+ FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, Directives(/* strict = */ false),
fun->generatorKind(), fun->asyncKind(), tryAnnexB);
if (!funbox)
return false;
@@ -3001,6 +3211,8 @@ Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, FunctionSyntaxKin
LazyScript* lazy = fun->lazyScript();
if (lazy->needsHomeObject())
funbox->setNeedsHomeObject();
+ if (lazy->isExprBody())
+ funbox->setIsExprBody();
PropagateTransitiveParseFlags(lazy, pc->sc());
@@ -3013,18 +3225,23 @@ Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, FunctionSyntaxKin
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, FunctionSyntaxKind kind,
- bool tryAnnexB)
+Parser<SyntaxParseHandler>::skipLazyInnerFunction(Node pn, uint32_t toStringStart,
+ FunctionSyntaxKind kind, bool tryAnnexB)
{
MOZ_CRASH("Cannot skip lazy inner functions when syntax parsing");
}
@@ -3043,7 +3260,7 @@ Parser<ParseHandler>::addExprAndGetNextTemplStrToken(YieldHandling yieldHandling
if (!tokenStream.getToken(&tt))
return false;
if (tt != TOK_RC) {
- report(ParseError, false, null(), JSMSG_TEMPLSTR_UNTERM_EXPR);
+ error(JSMSG_TEMPLSTR_UNTERM_EXPR);
return false;
}
@@ -3076,7 +3293,7 @@ template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::templateLiteral(YieldHandling yieldHandling)
{
- Node pn = noSubstitutionTemplate();
+ Node pn = noSubstitutionUntaggedTemplate();
if (!pn)
return null();
@@ -3089,7 +3306,7 @@ Parser<ParseHandler>::templateLiteral(YieldHandling yieldHandling)
if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt))
return null();
- pn = noSubstitutionTemplate();
+ pn = noSubstitutionUntaggedTemplate();
if (!pn)
return null();
@@ -3100,31 +3317,20 @@ Parser<ParseHandler>::templateLiteral(YieldHandling yieldHandling)
template <typename ParseHandler>
typename ParseHandler::Node
-Parser<ParseHandler>::functionDefinition(InHandling inHandling, YieldHandling yieldHandling,
+Parser<ParseHandler>::functionDefinition(uint32_t toStringStart, Node pn, InHandling inHandling,
+ YieldHandling yieldHandling,
HandleAtom funName, FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
- InvokedPrediction invoked)
+ bool tryAnnexB /* = false */)
{
MOZ_ASSERT_IF(kind == Statement, funName);
MOZ_ASSERT_IF(asyncKind == AsyncFunction, generatorKind == StarGenerator);
- Node pn = handler.newFunctionDefinition();
- if (!pn)
- return null();
-
- if (invoked)
- pn = handler.setLikelyIIFE(pn);
-
- // Note the declared name and check for early errors.
- bool tryAnnexB = false;
- if (!checkFunctionDefinition(funName, pn, kind, generatorKind, &tryAnnexB))
- return null();
-
// When fully parsing a LazyScript, we do not fully reparse its inner
// functions, which are also lazy. Instead, their free variables and
// source extents are recorded and may be skipped.
if (handler.canSkipLazyInnerFunctions()) {
- if (!skipLazyInnerFunction(pn, kind, tryAnnexB))
+ if (!skipLazyInnerFunction(pn, toStringStart, kind, tryAnnexB))
return null();
return pn;
}
@@ -3157,8 +3363,9 @@ Parser<ParseHandler>::functionDefinition(InHandling inHandling, YieldHandling yi
// reparse a function due to failed syntax parsing and encountering new
// "use foo" directives.
while (true) {
- if (trySyntaxParseInnerFunction(pn, fun, inHandling, yieldHandling, kind, generatorKind,
- asyncKind, tryAnnexB, directives, &newDirectives))
+ if (trySyntaxParseInnerFunction(pn, fun, toStringStart, inHandling, yieldHandling, kind,
+ generatorKind, asyncKind, tryAnnexB, directives,
+ &newDirectives))
{
break;
}
@@ -3185,6 +3392,7 @@ Parser<ParseHandler>::functionDefinition(InHandling inHandling, YieldHandling yi
template <>
bool
Parser<FullParseHandler>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunction fun,
+ uint32_t toStringStart,
InHandling inHandling,
YieldHandling yieldHandling,
FunctionSyntaxKind kind,
@@ -3218,14 +3426,15 @@ 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, inheritedDirectives, generatorKind,
- asyncKind, tryAnnexB);
+ 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, inHandling,
- yieldHandling, kind, inheritedDirectives, newDirectives))
+ if (!parser->innerFunction(SyntaxParseHandler::NodeGeneric, pc, funbox, toStringStart,
+ inHandling, yieldHandling, kind,
+ inheritedDirectives, newDirectives))
{
if (parser->hadAbortedSyntaxParse()) {
// Try again with a full parse. UsedNameTracker needs to be
@@ -3251,13 +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, inHandling, yieldHandling, kind, generatorKind, asyncKind,
- tryAnnexB, inheritedDirectives, newDirectives);
+ 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 toStringStart,
InHandling inHandling,
YieldHandling yieldHandling,
FunctionSyntaxKind kind,
@@ -3268,13 +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, inHandling, yieldHandling, kind, generatorKind, asyncKind,
- tryAnnexB, inheritedDirectives, newDirectives);
+ 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 toStringStart,
InHandling inHandling, YieldHandling yieldHandling,
FunctionSyntaxKind kind, Directives inheritedDirectives,
Directives* newDirectives)
@@ -3298,6 +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 toStringStart,
InHandling inHandling, YieldHandling yieldHandling,
FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
@@ -3309,21 +3521,21 @@ 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, inheritedDirectives, generatorKind,
- asyncKind, tryAnnexB);
+ FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, inheritedDirectives,
+ generatorKind, asyncKind, tryAnnexB);
if (!funbox)
return false;
funbox->initWithEnclosingParseContext(outerpc, kind);
- return innerFunction(pn, outerpc, funbox, inHandling, yieldHandling, kind, inheritedDirectives,
- newDirectives);
+ return innerFunction(pn, outerpc, funbox, toStringStart, inHandling, yieldHandling, kind,
+ inheritedDirectives, newDirectives);
}
template <typename ParseHandler>
bool
Parser<ParseHandler>::appendToCallSiteObj(Node callSiteObj)
{
- Node cookedNode = noSubstitutionTemplate();
+ Node cookedNode = noSubstitutionTaggedTemplate();
if (!cookedNode)
return false;
@@ -3346,13 +3558,13 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict
{
MOZ_ASSERT(checkOptionsCalled);
- Node pn = handler.newFunctionDefinition();
+ Node pn = handler.newFunctionStatement();
if (!pn)
return null();
Directives directives(strict);
- FunctionBox* funbox = newFunctionBox(pn, fun, directives, generatorKind, asyncKind,
- /* tryAnnexB = */ false);
+ FunctionBox* funbox = newFunctionBox(pn, fun, /* toStringStart = */ 0, directives,
+ generatorKind, asyncKind, /* tryAnnexB = */ false);
if (!funbox)
return null();
funbox->initFromLazyFunction();
@@ -3401,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
@@ -3410,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) {
@@ -3428,7 +3646,7 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
if (!tokenStream.matchToken(&matched, TOK_ARROW))
return false;
if (!matched) {
- report(ParseError, false, null(), JSMSG_BAD_ARROW_ARGS);
+ error(JSMSG_BAD_ARROW_ARGS);
return false;
}
}
@@ -3436,7 +3654,7 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
// When parsing something for new Function() we have to make sure to
// only treat a certain part of the source as a parameter list.
if (parameterListEnd.isSome() && parameterListEnd.value() != pos().begin) {
- report(ParseError, false, null(), JSMSG_UNEXPECTED_PARAMLIST_END);
+ error(JSMSG_UNEXPECTED_PARAMLIST_END);
return false;
}
@@ -3445,30 +3663,30 @@ 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 ||
IsConstructorKind(kind)) {
- report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY);
+ error(JSMSG_CURLY_BEFORE_BODY);
return false;
}
if (kind != Arrow) {
#if JS_HAS_EXPR_CLOSURES
- addTelemetry(JSCompartment::DeprecatedExpressionClosure);
if (!warnOnceAboutExprClosure())
return false;
#else
- report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY);
+ error(JSMSG_CURLY_BEFORE_BODY);
return false;
#endif
}
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) {
- report(ParseError, false, null(), 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,35 +3758,38 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
template <typename ParseHandler>
typename ParseHandler::Node
-Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling,
- FunctionAsyncKind asyncKind)
+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();
+ if (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) {
+ MOZ_ASSERT(!pc->sc()->strict(),
+ "labeled functions shouldn't be parsed in strict mode");
+
+ // Find the innermost non-label statement. Report an error if it's
+ // unbraced: functions can't appear in it. Otherwise the statement
+ // (or its absence) determines the scope the function's bound in.
+ while (declaredInStmt && declaredInStmt->kind() == StatementKind::Label)
+ declaredInStmt = declaredInStmt->enclosing();
+
+ if (declaredInStmt && !StatementKindIsBraced(declaredInStmt->kind())) {
+ error(JSMSG_SLOPPY_FUNCTION_LABEL);
+ return null();
}
}
- RootedPropertyName name(context);
- GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator;
TokenKind tt;
if (!tokenStream.getToken(&tt))
return null();
+ GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator;
if (tt == TOK_MUL) {
if (asyncKind != SyncFunction) {
- report(ParseError, false, null(), JSMSG_ASYNC_GENERATOR);
+ error(JSMSG_ASYNC_GENERATOR);
return null();
}
generatorKind = StarGenerator;
@@ -3558,7 +3797,8 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling
return null();
}
- if (tt == TOK_NAME || tt == TOK_YIELD) {
+ RootedPropertyName name(context);
+ if (TokenKindIsPossibleIdentifier(tt)) {
name = bindingIdentifier(yieldHandling);
if (!name)
return null();
@@ -3567,34 +3807,53 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling
tokenStream.ungetToken();
} else {
/* Unnamed function expressions are forbidden in statement context. */
- report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT);
+ error(JSMSG_UNNAMED_FUNCTION_STMT);
return null();
}
- YieldHandling newYieldHandling = GetYieldHandling(generatorKind, asyncKind);
- Node fun = functionDefinition(InAllowed, newYieldHandling, name, Statement, generatorKind,
- asyncKind, PredictUninvoked);
- if (!fun)
- return null();
+ // Note the declared name and check for early errors.
+ bool tryAnnexB = false;
+ if (declaredInStmt) {
+ MOZ_ASSERT(declaredInStmt->kind() != StatementKind::Label);
+ MOZ_ASSERT(StatementKindIsBraced(declaredInStmt->kind()));
+
+ if (!pc->sc()->strict() && generatorKind == NotGenerator) {
+ // In sloppy mode, try Annex B.3.3 semantics. If making an
+ // additional 'var' binding of the same name does not throw an
+ // early error, do so. This 'var' binding would be assigned
+ // the function object when its declaration is reached, not at
+ // the start of the block.
+ if (!tryDeclareVarForAnnexBLexicalFunction(name, pos().begin, &tryAnnexB))
+ return null();
+ }
- if (synthesizedStmtForAnnexB) {
- Node synthesizedStmtList = handler.newStatementList(handler.getPosition(fun));
- if (!synthesizedStmtList)
+ if (!noteDeclaredName(name, DeclarationKind::LexicalFunction, pos()))
+ return null();
+ } else {
+ DeclarationKind kind = pc->atModuleLevel()
+ ? DeclarationKind::ModuleBodyLevelFunction
+ : DeclarationKind::BodyLevelFunction;
+ if (!noteDeclaredName(name, kind, pos()))
return null();
- handler.addStatementToList(synthesizedStmtList, fun);
- return finishLexicalScope(*synthesizedScopeForAnnexB, synthesizedStmtList);
}
- return fun;
+ Node pn = handler.newFunctionStatement();
+ if (!pn)
+ return null();
+
+ YieldHandling newYieldHandling = GetYieldHandling(generatorKind, asyncKind);
+ return functionDefinition(toStringStart, pn, InAllowed, newYieldHandling,
+ name, Statement, generatorKind, asyncKind, tryAnnexB);
}
template <typename ParseHandler>
typename ParseHandler::Node
-Parser<ParseHandler>::functionExpr(InvokedPrediction invoked, FunctionAsyncKind asyncKind)
+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))
@@ -3602,7 +3861,7 @@ Parser<ParseHandler>::functionExpr(InvokedPrediction invoked, FunctionAsyncKind
if (tt == TOK_MUL) {
if (asyncKind != SyncFunction) {
- report(ParseError, false, null(), JSMSG_ASYNC_GENERATOR);
+ error(JSMSG_ASYNC_GENERATOR);
return null();
}
generatorKind = StarGenerator;
@@ -3613,7 +3872,7 @@ Parser<ParseHandler>::functionExpr(InvokedPrediction invoked, FunctionAsyncKind
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();
@@ -3621,8 +3880,15 @@ Parser<ParseHandler>::functionExpr(InvokedPrediction invoked, FunctionAsyncKind
tokenStream.ungetToken();
}
- return functionDefinition(InAllowed, yieldHandling, name, Expression, generatorKind,
- asyncKind, invoked);
+ Node pn = handler.newFunctionExpression();
+ if (!pn)
+ return null();
+
+ if (invoked)
+ pn = handler.setLikelyIIFE(pn);
+
+ return functionDefinition(toStringStart, pn, InAllowed, yieldHandling, name, Expression,
+ generatorKind, asyncKind);
}
/*
@@ -3643,17 +3909,6 @@ IsEscapeFreeStringLiteral(const TokenPos& pos, JSAtom* str)
return pos.begin + str->length() + 2 == pos.end;
}
-template <typename ParseHandler>
-bool
-Parser<ParseHandler>::checkUnescapedName()
-{
- if (!tokenStream.currentToken().nameContainsEscape())
- return true;
-
- report(ParseError, false, null(), JSMSG_ESCAPED_KEYWORD);
- return false;
-}
-
template <>
bool
Parser<SyntaxParseHandler>::asmJS(Node list)
@@ -3726,10 +3981,10 @@ Parser<FullParseHandler>::asmJS(Node list)
*/
template <typename ParseHandler>
bool
-Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool* cont)
+Parser<ParseHandler>::maybeParseDirective(Node list, Node possibleDirective, bool* cont)
{
TokenPos directivePos;
- JSAtom* directive = handler.isStringExprStatement(pn, &directivePos);
+ JSAtom* directive = handler.isStringExprStatement(possibleDirective, &directivePos);
*cont = !!directive;
if (!*cont)
@@ -3746,7 +4001,7 @@ Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool* cont)
// directive in the future. We don't want to interfere with people
// taking advantage of directive-prologue-enabled features that appear
// in other browsers first.
- handler.setPrologue(pn);
+ handler.setInDirectivePrologue(possibleDirective);
if (directive == context->names().useStrict) {
// Functions with non-simple parameter lists (destructuring,
@@ -3760,8 +4015,7 @@ Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool* cont)
: funbox->hasParameterExprs
? "default"
: "rest";
- reportWithOffset(ParseError, false, directivePos.begin,
- JSMSG_STRICT_NON_SIMPLE_PARAMS, parameterKind);
+ errorAt(directivePos.begin, JSMSG_STRICT_NON_SIMPLE_PARAMS, parameterKind);
return false;
}
}
@@ -3774,7 +4028,7 @@ Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool* cont)
// occur in the directive prologue -- octal escapes -- and
// complain now.
if (tokenStream.sawOctalEscape()) {
- report(ParseError, false, null(), JSMSG_DEPRECATED_OCTAL);
+ error(JSMSG_DEPRECATED_OCTAL);
return false;
}
pc->sc()->strictScript = true;
@@ -3782,7 +4036,7 @@ Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool* cont)
} else if (directive == context->names().useAsm) {
if (pc->isFunctionBox())
return asmJS(list);
- return report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL);
+ return warningAt(directivePos.begin, JSMSG_USE_ASM_DIRECTIVE_FAIL);
}
}
return true;
@@ -3814,10 +4068,8 @@ Parser<ParseHandler>::statementList(YieldHandling yieldHandling)
if (tt == TOK_EOF || tt == TOK_RC)
break;
if (afterReturn) {
- TokenPos pos(0, 0);
- if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand))
+ if (!tokenStream.peekOffset(&statementBegin, TokenStream::Operand))
return null();
- statementBegin = pos.begin;
}
Node next = statementListItem(yieldHandling, canHaveDirectives);
if (!next) {
@@ -3828,11 +4080,9 @@ Parser<ParseHandler>::statementList(YieldHandling yieldHandling)
if (!warnedAboutStatementsAfterReturn) {
if (afterReturn) {
if (!handler.isStatementPermittedAfterReturnStatement(next)) {
- if (!reportWithOffset(ParseWarning, false, statementBegin,
- JSMSG_STMT_AFTER_RETURN))
- {
+ if (!warningAt(statementBegin, JSMSG_STMT_AFTER_RETURN))
return null();
- }
+
warnedAboutStatementsAfterReturn = true;
}
} else if (handler.isReturnStatement(next)) {
@@ -3863,7 +4113,7 @@ Parser<ParseHandler>::condition(InHandling inHandling, YieldHandling yieldHandli
/* Check for (a = b) and warn about possible (a == b) mistype. */
if (handler.isUnparenthesizedAssignment(pn)) {
- if (!report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
+ if (!extraWarning(JSMSG_EQUAL_AS_ASSIGN))
return null();
}
return pn;
@@ -3877,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));
@@ -3900,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>
@@ -3920,7 +4179,8 @@ Parser<ParseHandler>::PossibleError::hasError(ErrorKind kind)
template <typename ParseHandler>
void
-Parser<ParseHandler>::PossibleError::setPending(ErrorKind kind, Node pn, unsigned errorNumber)
+Parser<ParseHandler>::PossibleError::setPending(ErrorKind kind, const TokenPos& pos,
+ unsigned errorNumber)
{
// Don't overwrite a previously recorded error.
if (hasError(kind))
@@ -3929,23 +4189,33 @@ Parser<ParseHandler>::PossibleError::setPending(ErrorKind kind, Node pn, unsigne
// If we report an error later, we'll do it from the position where we set
// the state to pending.
Error& err = error(kind);
- err.offset_ = (pn ? parser_.handler.getPosition(pn) : parser_.pos()).begin;
+ err.offset_ = pos.begin;
err.errorNumber_ = errorNumber;
err.state_ = ErrorState::Pending;
}
template <typename ParseHandler>
void
-Parser<ParseHandler>::PossibleError::setPendingDestructuringError(Node pn, unsigned errorNumber)
+Parser<ParseHandler>::PossibleError::setPendingDestructuringErrorAt(const TokenPos& pos,
+ unsigned errorNumber)
+{
+ setPending(ErrorKind::Destructuring, pos, errorNumber);
+}
+
+template <typename ParseHandler>
+void
+Parser<ParseHandler>::PossibleError::setPendingDestructuringWarningAt(const TokenPos& pos,
+ unsigned errorNumber)
{
- setPending(ErrorKind::Destructuring, pn, errorNumber);
+ setPending(ErrorKind::DestructuringWarning, pos, errorNumber);
}
template <typename ParseHandler>
void
-Parser<ParseHandler>::PossibleError::setPendingExpressionError(Node pn, unsigned errorNumber)
+Parser<ParseHandler>::PossibleError::setPendingExpressionErrorAt(const TokenPos& pos,
+ unsigned errorNumber)
{
- setPending(ErrorKind::Expression, pn, errorNumber);
+ setPending(ErrorKind::Expression, pos, errorNumber);
}
template <typename ParseHandler>
@@ -3956,29 +4226,42 @@ Parser<ParseHandler>::PossibleError::checkForError(ErrorKind kind)
return true;
Error& err = error(kind);
- parser_.reportWithOffset(ParseError, false, err.offset_, err.errorNumber_);
+ parser_.errorAt(err.offset_, err.errorNumber_);
return false;
}
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);
@@ -4011,205 +4294,271 @@ Parser<ParseHandler>::PossibleError::transferErrorsTo(PossibleError* other)
}
template <typename ParseHandler>
-bool
-Parser<ParseHandler>::checkAssignmentToCall(Node target, unsigned msg)
+typename ParseHandler::Node
+Parser<ParseHandler>::bindingInitializer(Node lhs, DeclarationKind kind,
+ YieldHandling yieldHandling)
{
- MOZ_ASSERT(handler.isFunctionCall(target));
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_ASSIGN));
+
+ if (kind == DeclarationKind::FormalParameter)
+ pc->functionBox()->hasParameterExprs = true;
- // Assignment to function calls is forbidden in ES6. We're still somewhat
- // concerned about sites using this in dead code, so forbid it only in
- // strict mode code (or if the werror option has been set), and otherwise
- // warn.
- return report(ParseStrictError, pc->sc()->strict(), target, msg);
+ Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
+ if (!rhs)
+ return null();
+
+ handler.checkAndSetIsDirectRHSAnonFunction(rhs);
+
+ Node assign = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP);
+ if (!assign)
+ return null();
+
+ if (foldConstants && !FoldConstants(context, &assign, this))
+ return null();
+
+ return assign;
}
-template <>
-bool
-Parser<FullParseHandler>::checkDestructuringName(ParseNode* expr, Maybe<DeclarationKind> maybeDecl)
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::bindingIdentifier(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)) {
- report(ParseError, false, expr, JSMSG_BAD_DESTRUCT_PARENS);
- return false;
- }
+ RootedPropertyName name(context, bindingIdentifier(yieldHandling));
+ if (!name)
+ return null();
- // 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)) {
- report(ParseError, false, expr, JSMSG_NO_VARIABLE_NAME);
- return false;
- }
+ Node binding = newName(name);
+ if (!binding || !noteDeclaredName(name, kind, pos()))
+ return null();
- RootedPropertyName name(context, expr->name());
- return noteDeclaredName(name, *maybeDecl, handler.getPosition(expr));
- }
+ return binding;
+}
- // Otherwise this is an expression in destructuring outside a declaration.
- if (!reportIfNotValidSimpleAssignmentTarget(expr, KeyedDestructuringAssignment))
- 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);
- MOZ_ASSERT(!handler.isFunctionCall(expr),
- "function calls shouldn't be considered valid targets in "
- "destructuring patterns");
+ if (tt == TOK_LC)
+ return objectBindingPattern(kind, yieldHandling);
- if (handler.isNameAnyParentheses(expr)) {
- // The arguments/eval identifiers are simple in non-strict mode code.
- // Warn to discourage their use nonetheless.
- return reportIfArgumentsEvalTarget(expr);
+ if (!TokenKindIsPossibleIdentifierName(tt)) {
+ error(JSMSG_NO_VARIABLE_NAME);
+ return null();
}
- // Nothing further to do for property accesses.
- MOZ_ASSERT(handler.isPropertyAccess(expr));
- return true;
+ return bindingIdentifier(kind, yieldHandling);
}
-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>::objectBindingPattern(DeclarationKind kind, YieldHandling yieldHandling)
{
- MOZ_ASSERT(objectPattern->isKind(PNK_OBJECT));
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
- 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);
+ JS_CHECK_RECURSION(context, return null());
- target = member->pn_right;
- }
- if (handler.isUnparenthesizedAssignment(target))
- target = target->pn_left;
+ uint32_t begin = pos().begin;
+ Node literal = handler.newObjectLiteral(begin);
+ if (!literal)
+ return null();
- if (handler.isUnparenthesizedDestructuringPattern(target)) {
- if (!checkDestructuringPattern(target, maybeDecl))
- return false;
+ 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 {
- if (!checkDestructuringName(target, maybeDecl))
- return false;
- }
- }
+ TokenPos namePos = tokenStream.nextToken().pos;
- return true;
-}
+ 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|.
-template <>
-bool
-Parser<FullParseHandler>::checkDestructuringArray(ParseNode* arrayPattern,
- Maybe<DeclarationKind> maybeDecl)
-{
- MOZ_ASSERT(arrayPattern->isKind(PNK_ARRAY));
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
- for (ParseNode* element = arrayPattern->pn_head; element; element = element->pn_next) {
- if (element->isKind(PNK_ELISION))
- continue;
+ 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();
- ParseNode* target;
- if (element->isKind(PNK_SPREAD)) {
- if (element->pn_next) {
- report(ParseError, false, element->pn_next, JSMSG_PARAMETER_AFTER_REST);
- return false;
}
- target = element->pn_kid;
- } else if (handler.isUnparenthesizedAssignment(element)) {
- target = element->pn_left;
- } else {
- target = element;
}
- 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)) {
- report(ParseError, false, pattern, 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>
@@ -4220,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>
@@ -4245,11 +4585,11 @@ Parser<ParseHandler>::destructuringDeclarationWithoutYieldOrAwait(DeclarationKin
Node res = destructuringDeclaration(kind, yieldHandling, tt);
if (res) {
if (pc->lastYieldOffset != startYieldOffset) {
- reportWithOffset(ParseError, false, pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT);
+ errorAt(pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT);
return null();
}
if (pc->lastAwaitOffset != startAwaitOffset) {
- reportWithOffset(ParseError, false, pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT);
+ errorAt(pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT);
return null();
}
}
@@ -4261,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);
@@ -4271,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);
}
@@ -4327,14 +4670,7 @@ Parser<ParseHandler>::declarationPattern(Node decl, DeclarationKind declKind, To
}
}
- TokenKind token;
- if (!tokenStream.getToken(&token, TokenStream::None))
- return null();
-
- if (token != TOK_ASSIGN) {
- report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_DECL);
- return null();
- }
+ MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
Node init = assignExpr(forHeadKind ? InProhibited : InAllowed,
yieldHandling, TripledotProhibited);
@@ -4349,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);
}
@@ -4367,6 +4709,10 @@ Parser<ParseHandler>::initializerInNameDeclaration(Node decl, Node binding,
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_ASSIGN));
+ uint32_t initializerOffset;
+ if (!tokenStream.peekOffset(&initializerOffset, TokenStream::Operand))
+ return false;
+
Node initializer = assignExpr(forHeadKind ? InProhibited : InAllowed,
yieldHandling, TripledotProhibited);
if (!initializer)
@@ -4384,7 +4730,7 @@ Parser<ParseHandler>::initializerInNameDeclaration(Node decl, Node binding,
//
// for (var/let/const x = ... of ...); // BAD
if (isForOf) {
- report(ParseError, false, binding, JSMSG_BAD_FOR_LEFTSIDE);
+ errorAt(initializerOffset, JSMSG_OF_AFTER_FOR_LOOP_DECL);
return false;
}
@@ -4393,18 +4739,15 @@ Parser<ParseHandler>::initializerInNameDeclaration(Node decl, Node binding,
//
// for (let/const x = ... in ...); // BAD
if (DeclarationKindIsLexical(declKind)) {
- report(ParseError, false, binding, JSMSG_BAD_FOR_LEFTSIDE);
+ errorAt(initializerOffset, JSMSG_IN_AFTER_LEXICAL_FOR_DECL);
return false;
}
// This leaves only initialized for-in |var| declarations. ES6
// forbids these; later ES un-forbids in non-strict mode code.
*forHeadKind = PNK_FORIN;
- if (!report(ParseStrictError, pc->sc()->strict(), initializer,
- JSMSG_INVALID_FOR_IN_DECL_WITH_INIT))
- {
+ if (!strictModeErrorAt(initializerOffset, JSMSG_INVALID_FOR_IN_DECL_WITH_INIT))
return false;
- }
*forInOrOfExpression = expressionAfterForInOrOf(PNK_FORIN, yieldHandling);
if (!*forInOrOfExpression)
@@ -4448,8 +4791,9 @@ 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) {
- report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
+ // Anything other than possible identifier is an error.
+ if (!TokenKindIsPossibleIdentifier(tt)) {
+ error(JSMSG_NO_VARIABLE_NAME);
return null();
}
@@ -4509,7 +4853,7 @@ Parser<ParseHandler>::declarationName(Node decl, DeclarationKind declKind, Token
// Normal const declarations, and const declarations in for(;;)
// heads, must be initialized.
if (declKind == DeclarationKind::Const) {
- report(ParseError, false, binding, JSMSG_BAD_CONST_DECL);
+ errorAt(namePos.begin, JSMSG_BAD_CONST_DECL);
return null();
}
}
@@ -4589,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
@@ -4602,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;
@@ -4614,42 +4961,35 @@ 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) {
- report(ParseError, false, null(), JSMSG_NO_BINDING_NAME);
+ if (!TokenKindIsPossibleIdentifierName(afterAs)) {
+ error(JSMSG_NO_BINDING_NAME);
return false;
}
} else {
@@ -4658,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;
- report(ParseError, false, null(), JSMSG_AS_AFTER_RESERVED_WORD, bytes.ptr());
+ error(JSMSG_AS_AFTER_RESERVED_WORD, ReservedWordToCharZ(importName));
return false;
}
}
@@ -4686,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) {
- report(ParseError, false, null(), JSMSG_AS_AFTER_IMPORT_STAR);
- return false;
- }
+ MUST_MATCH_TOKEN(TOK_AS, JSMSG_AS_AFTER_IMPORT_STAR);
- if (!checkUnescapedName())
- return false;
-
- 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)
@@ -4744,21 +5074,13 @@ 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()
{
MOZ_ASSERT(tokenStream.currentToken().type == TOK_IMPORT);
if (!pc->atModuleLevel()) {
- report(ParseError, false, null(), JSMSG_IMPORT_DECL_AT_TOP_LEVEL);
+ error(JSMSG_IMPORT_DECL_AT_TOP_LEVEL);
return null();
}
@@ -4771,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
@@ -4807,7 +5136,7 @@ Parser<FullParseHandler>::importDeclaration()
return null();
if (tt != TOK_LC && tt != TOK_MUL) {
- report(ParseError, false, null(), JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT);
+ error(JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT);
return null();
}
@@ -4815,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) {
- report(ParseError, false, null(), 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 {
- report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_IMPORT);
- return null();
}
Node moduleSpec = stringLiteral();
if (!moduleSpec)
return null();
- if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterNonExpression())
return null();
ParseNode* node =
@@ -4874,7 +5187,7 @@ Parser<FullParseHandler>::checkExportedName(JSAtom* exportName)
if (!AtomToPrintableString(context, exportName, &str))
return false;
- report(ParseError, false, null(), JSMSG_DUPLICATE_EXPORT_NAME, str.ptr());
+ error(JSMSG_DUPLICATE_EXPORT_NAME, str.ptr());
return false;
}
@@ -4911,290 +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()) {
- report(ParseError, false, null(), 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) {
- if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
- return null();
- if (tt != TOK_NAME) {
- report(ParseError, false, null(), JSMSG_NO_EXPORT_NAME);
- return null();
- }
- }
+ 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) {
- report(ParseError, false, null(), 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(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(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(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:
- report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_EXPORT);
+ return node;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exportFunctionDeclaration(uint32_t begin)
+{
+ if (!abortIfSyntaxParser())
return null();
- }
- ParseNode* node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
- if (!node || !pc->sc()->asModuleContext()->builder.processExport(node))
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
+
+ Node kid = functionStmt(pos().begin, YieldIsKeyword, NameRequired);
+ if (!kid)
+ return null();
+
+ if (!checkExportedNameForFunction(kid))
+ return null();
+
+ 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>
@@ -5206,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);
}
@@ -5219,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(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);
@@ -5254,7 +5873,7 @@ Parser<ParseHandler>::ifStatement(YieldHandling yieldHandling)
if (!tokenStream.peekToken(&tt, TokenStream::Operand))
return null();
if (tt == TOK_SEMI) {
- if (!report(ParseExtraWarning, false, null(), JSMSG_EMPTY_CONSEQUENT))
+ if (!extraWarning(JSMSG_EMPTY_CONSEQUENT))
return null();
}
@@ -5341,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;
@@ -5355,37 +5970,6 @@ Parser<ParseHandler>::matchInOrOf(bool* isForInp, bool* isForOfp)
template <class ParseHandler>
bool
-Parser<ParseHandler>::validateForInOrOfLHSExpression(Node target, PossibleError* possibleError)
-{
- if (handler.isUnparenthesizedDestructuringPattern(target))
- return checkDestructuringPattern(target, Nothing(), possibleError);
-
- // All other permitted targets are simple.
- if (!reportIfNotValidSimpleAssignmentTarget(target, ForInOrOfTarget))
- return false;
-
- if (handler.isPropertyAccess(target))
- return true;
-
- if (handler.isNameAnyParentheses(target)) {
- // The arguments/eval identifiers are simple in non-strict mode code,
- // but warn to discourage use nonetheless.
- if (!reportIfArgumentsEvalTarget(target))
- return false;
-
- handler.adjustGetToSet(target);
- return true;
- }
-
- if (handler.isFunctionCall(target))
- return checkAssignmentToCall(target, JSMSG_BAD_FOR_LEFTSIDE);
-
- report(ParseError, false, target, JSMSG_BAD_FOR_LEFTSIDE);
- return false;
-}
-
-template <class ParseHandler>
-bool
Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling,
ParseNodeKind* forHeadKind,
Node* forInitialPart,
@@ -5428,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))
@@ -5464,6 +6045,10 @@ Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling,
return *forInitialPart != null();
}
+ uint32_t exprOffset;
+ if (!tokenStream.peekOffset(&exprOffset, TokenStream::Operand))
+ return false;
+
// Finally, handle for-loops that start with expressions. Pass
// |InProhibited| so that |in| isn't parsed in a RelationalExpression as a
// binary operator. |in| makes it a for-in loop, *not* an |in| expression.
@@ -5501,14 +6086,35 @@ Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling,
//
// See ES6 13.7.
if (isForOf && letIsIdentifier) {
- report(ParseError, false, *forInitialPart, JSMSG_LET_STARTING_FOROF_LHS);
+ errorAt(exprOffset, JSMSG_LET_STARTING_FOROF_LHS);
return false;
}
*forHeadKind = isForIn ? PNK_FORIN : PNK_FOROF;
- if (!validateForInOrOfLHSExpression(*forInitialPart, &possibleError))
+ // Verify the left-hand side expression doesn't have a forbidden form.
+ if (handler.isUnparenthesizedDestructuringPattern(*forInitialPart)) {
+ if (!possibleError.checkForDestructuringErrorOrWarning())
+ return false;
+ } else if (handler.isNameAnyParentheses(*forInitialPart)) {
+ const char* chars = handler.nameIsArgumentsEvalAnyParentheses(*forInitialPart, context);
+ if (chars) {
+ // |chars| is "arguments" or "eval" here.
+ if (!strictModeErrorAt(exprOffset, JSMSG_BAD_STRICT_ASSIGN, chars))
+ return false;
+ }
+
+ handler.adjustGetToSet(*forInitialPart);
+ } else if (handler.isPropertyAccess(*forInitialPart)) {
+ // Permitted: no additional testing/fixup needed.
+ } else if (handler.isFunctionCall(*forInitialPart)) {
+ if (!strictModeErrorAt(exprOffset, JSMSG_BAD_FOR_LEFTSIDE))
+ return false;
+ } else {
+ errorAt(exprOffset, JSMSG_BAD_FOR_LEFTSIDE);
return false;
+ }
+
if (!possibleError.checkForExpressionError())
return false;
@@ -5532,12 +6138,11 @@ 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;
isForEach = true;
- addTelemetry(JSCompartment::DeprecatedForEach);
if (!warnOnceAboutForEach())
return null();
}
@@ -5593,7 +6198,7 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling)
Node init = startNode;
if (isForEach) {
- reportWithOffset(ParseError, false, begin, JSMSG_BAD_FOR_EACH_LOOP);
+ errorAt(begin, JSMSG_BAD_FOR_EACH_LOOP);
return null();
}
@@ -5653,19 +6258,13 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling)
iflags |= JSITER_ENUMERATE;
} else {
if (isForEach) {
- report(ParseError, false, startNode, JSMSG_BAD_FOR_EACH_LOOP);
+ errorAt(begin, JSMSG_BAD_FOR_EACH_LOOP);
return null();
}
stmt.refineForKind(StatementKind::ForOfLoop);
}
- if (!handler.isDeclarationList(target)) {
- MOZ_ASSERT(!forLoopLexicalScope);
- if (!checkAndMarkAsAssignmentLhs(target, PlainAssignment))
- return null();
- }
-
// Parser::declaration consumed everything up to the closing ')'. That
// token follows an {Assignment,}Expression, so the next token must be
// consumed as if an operator continued the expression, i.e. as None.
@@ -5729,7 +6328,7 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling)
switch (tt) {
case TOK_DEFAULT:
if (seenDefault) {
- report(ParseError, false, null(), JSMSG_TOO_MANY_DEFAULTS);
+ error(JSMSG_TOO_MANY_DEFAULTS);
return null();
}
seenDefault = true;
@@ -5743,7 +6342,7 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling)
break;
default:
- report(ParseError, false, null(), JSMSG_BAD_SWITCH);
+ error(JSMSG_BAD_SWITCH);
return null();
}
@@ -5762,10 +6361,8 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling)
if (tt == TOK_RC || tt == TOK_CASE || tt == TOK_DEFAULT)
break;
if (afterReturn) {
- TokenPos pos(0, 0);
- if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand))
+ if (!tokenStream.peekOffset(&statementBegin, TokenStream::Operand))
return null();
- statementBegin = pos.begin;
}
Node stmt = statementListItem(yieldHandling);
if (!stmt)
@@ -5773,11 +6370,9 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling)
if (!warnedAboutStatementsAfterReturn) {
if (afterReturn) {
if (!handler.isStatementPermittedAfterReturnStatement(stmt)) {
- if (!reportWithOffset(ParseWarning, false, statementBegin,
- JSMSG_STMT_AFTER_RETURN))
- {
+ if (!warningAt(statementBegin, JSMSG_STMT_AFTER_RETURN))
return null();
- }
+
warnedAboutStatementsAfterReturn = true;
}
} else if (handler.isReturnStatement(stmt)) {
@@ -5827,8 +6422,10 @@ Parser<ParseHandler>::continueStatement(YieldHandling yieldHandling)
for (;;) {
stmt = ParseContext::Statement::findNearest(stmt, isLoop);
if (!stmt) {
- report(ParseError, false, null(),
- foundLoop ? JSMSG_LABEL_NOT_FOUND : JSMSG_BAD_CONTINUE);
+ if (foundLoop)
+ error(JSMSG_LABEL_NOT_FOUND);
+ else
+ errorAt(begin, JSMSG_BAD_CONTINUE);
return null();
}
@@ -5848,11 +6445,11 @@ Parser<ParseHandler>::continueStatement(YieldHandling yieldHandling)
break;
}
} else if (!pc->findInnermostStatement(isLoop)) {
- report(ParseError, false, null(), JSMSG_BAD_CONTINUE);
+ error(JSMSG_BAD_CONTINUE);
return null();
}
- if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterNonExpression())
return null();
return handler.newContinueStatement(label, TokenPos(begin, pos().end));
@@ -5878,7 +6475,7 @@ Parser<ParseHandler>::breakStatement(YieldHandling yieldHandling)
};
if (!pc->findInnermostStatement<ParseContext::LabelStatement>(hasSameLabel)) {
- report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND);
+ error(JSMSG_LABEL_NOT_FOUND);
return null();
}
} else {
@@ -5887,12 +6484,12 @@ Parser<ParseHandler>::breakStatement(YieldHandling yieldHandling)
};
if (!pc->findInnermostStatement(isBreakTarget)) {
- report(ParseError, false, null(), JSMSG_TOUGH_BREAK);
+ errorAt(begin, JSMSG_TOUGH_BREAK);
return null();
}
}
- if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterNonExpression())
return null();
return handler.newBreakStatement(label, TokenPos(begin, pos().end));
@@ -5932,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();
}
@@ -5943,10 +6540,9 @@ Parser<ParseHandler>::returnStatement(YieldHandling yieldHandling)
if (!pn)
return null();
+ /* Disallow "return v;" in legacy generators. */
if (pc->isLegacyGenerator() && exprNode) {
- /* Disallow "return v;" in legacy generators. */
- reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN,
- JSMSG_BAD_ANON_GENERATOR_RETURN);
+ errorAt(begin, JSMSG_BAD_GENERATOR_RETURN);
return null();
}
@@ -6037,13 +6633,12 @@ Parser<ParseHandler>::yieldExpression(InHandling inHandling)
return null();
if (!pc->isFunctionBox()) {
- report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
+ error(JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
return null();
}
if (pc->functionBox()->isArrow()) {
- reportWithOffset(ParseError, false, begin,
- JSMSG_YIELD_IN_ARROW, js_yield_str);
+ errorAt(begin, JSMSG_YIELD_IN_ARROW, js_yield_str);
return null();
}
@@ -6051,25 +6646,22 @@ Parser<ParseHandler>::yieldExpression(InHandling inHandling)
pc->functionBox()->function()->isGetter() ||
pc->functionBox()->function()->isSetter())
{
- reportWithOffset(ParseError, false, begin,
- JSMSG_YIELD_IN_METHOD, js_yield_str);
+ errorAt(begin, JSMSG_YIELD_IN_METHOD, js_yield_str);
return null();
}
if (pc->funHasReturnExpr
#if JS_HAS_EXPR_CLOSURES
- || pc->functionBox()->function()->isExprBody()
+ || pc->functionBox()->isExprBody()
#endif
)
{
/* As in Python (see PEP-255), disallow return v; in generators. */
- reportBadReturn(null(), ParseError, JSMSG_BAD_GENERATOR_RETURN,
- JSMSG_BAD_ANON_GENERATOR_RETURN);
+ errorAt(begin, JSMSG_BAD_GENERATOR_RETURN);
return null();
}
pc->functionBox()->setGeneratorKind(LegacyGenerator);
- addTelemetry(JSCompartment::DeprecatedLegacyGenerator);
MOZ_FALLTHROUGH;
@@ -6119,14 +6711,13 @@ Parser<ParseHandler>::withStatement(YieldHandling yieldHandling)
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH));
uint32_t begin = pos().begin;
- // In most cases, we want the constructs forbidden in strict mode code to be
- // a subset of those that JSOPTION_EXTRA_WARNINGS warns about, and we should
- // use reportStrictModeError. However, 'with' is the sole instance of a
- // construct that is forbidden in strict mode code, but doesn't even merit a
- // warning under JSOPTION_EXTRA_WARNINGS. See
+ // Usually we want the constructs forbidden in strict mode code to be a
+ // subset of those that ContextOptions::extraWarnings() warns about, and we
+ // use strictModeError directly. But while 'with' is forbidden in strict
+ // mode code, it doesn't even merit a warning in non-strict code. See
// https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
if (pc->sc()->strict()) {
- if (!report(ParseStrictError, true, null(), JSMSG_STRICT_CODE_WITH))
+ if (!strictModeError(JSMSG_STRICT_CODE_WITH))
return null();
}
@@ -6165,7 +6756,7 @@ Parser<ParseHandler>::labeledItem(YieldHandling yieldHandling)
// GeneratorDeclaration is only matched by HoistableDeclaration in
// StatementListItem, so generators can't be inside labels.
if (next == TOK_MUL) {
- report(ParseError, false, null(), JSMSG_GENERATOR_LABEL);
+ error(JSMSG_GENERATOR_LABEL);
return null();
}
@@ -6173,11 +6764,11 @@ Parser<ParseHandler>::labeledItem(YieldHandling yieldHandling)
// is ever matched. Per Annex B.3.2 that modifies this text, this
// applies only to strict mode code.
if (pc->sc()->strict()) {
- report(ParseError, false, null(), JSMSG_FUNCTION_LABEL);
+ error(JSMSG_FUNCTION_LABEL);
return null();
}
- return functionStmt(yieldHandling, NameRequired);
+ return functionStmt(pos().begin, yieldHandling, NameRequired);
}
tokenStream.ungetToken();
@@ -6196,13 +6787,13 @@ Parser<ParseHandler>::labeledStatement(YieldHandling yieldHandling)
return stmt->label() == label;
};
+ uint32_t begin = pos().begin;
+
if (pc->findInnermostStatement<ParseContext::LabelStatement>(hasSameLabel)) {
- report(ParseError, false, null(), JSMSG_DUPLICATE_LABEL);
+ errorAt(begin, JSMSG_DUPLICATE_LABEL);
return null();
}
- uint32_t begin = pos().begin;
-
tokenStream.consumeKnownToken(TOK_COLON);
/* Push a label struct and parse the statement. */
@@ -6226,11 +6817,11 @@ Parser<ParseHandler>::throwStatement(YieldHandling yieldHandling)
if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
return null();
if (tt == TOK_EOF || tt == TOK_SEMI || tt == TOK_RC) {
- report(ParseError, false, null(), JSMSG_MISSING_EXPR_AFTER_THROW);
+ error(JSMSG_MISSING_EXPR_AFTER_THROW);
return null();
}
if (tt == TOK_EOL) {
- report(ParseError, false, null(), JSMSG_LINE_BREAK_AFTER_THROW);
+ error(JSMSG_LINE_BREAK_AFTER_THROW);
return null();
}
@@ -6238,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));
@@ -6258,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.
@@ -6273,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))
@@ -6286,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;
@@ -6304,7 +6899,7 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
/* Check for another catch after unconditional catch. */
if (hasUnconditionalCatch) {
- report(ParseError, false, null(), JSMSG_CATCH_AFTER_GENERAL);
+ error(JSMSG_CATCH_AFTER_GENERAL);
return null();
}
@@ -6338,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:
- report(ParseError, false, null(), JSMSG_CATCH_IDENTIFIER);
- return null();
}
Node catchGuard = null();
@@ -6402,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))
@@ -6415,12 +7008,14 @@ 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();
}
if (!catchList && !finallyBlock) {
- report(ParseError, false, null(), JSMSG_CATCH_OR_FINALLY);
+ error(JSMSG_CATCH_OR_FINALLY);
return null();
}
@@ -6432,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
@@ -6451,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.
@@ -6465,7 +7064,7 @@ Parser<ParseHandler>::debuggerStatement()
{
TokenPos p;
p.begin = pos().begin;
- if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterNonExpression())
return null();
p.end = pos().end;
@@ -6497,49 +7096,6 @@ JSOpFromPropertyType(PropertyType propType)
}
}
-static FunctionSyntaxKind
-FunctionSyntaxKindFromPropertyType(PropertyType propType)
-{
- switch (propType) {
- case PropertyType::Getter:
- return Getter;
- case PropertyType::GetterNoExpressionClosure:
- return GetterNoExpressionClosure;
- case PropertyType::Setter:
- return Setter;
- case PropertyType::SetterNoExpressionClosure:
- return SetterNoExpressionClosure;
- case PropertyType::Method:
- case PropertyType::GeneratorMethod:
- case PropertyType::AsyncMethod:
- return Method;
- case PropertyType::Constructor:
- return ClassConstructor;
- case PropertyType::DerivedConstructor:
- return DerivedClassConstructor;
- default:
- MOZ_CRASH("unexpected property type");
- }
-}
-
-static GeneratorKind
-GeneratorKindFromPropertyType(PropertyType propType)
-{
- if (propType == PropertyType::GeneratorMethod)
- return StarGenerator;
- if (propType == PropertyType::AsyncMethod)
- return StarGenerator;
- return NotGenerator;
-}
-
-static FunctionAsyncKind
-AsyncKindFromPropertyType(PropertyType propType)
-{
- if (propType == PropertyType::AsyncMethod)
- return AsyncFunction;
- return SyncFunction;
-}
-
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
@@ -6548,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;
@@ -6555,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();
@@ -6565,7 +7122,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
tokenStream.ungetToken();
} else {
// Class statements must have a bound name
- report(ParseError, false, null(), JSMSG_UNNAMED_CLASS_STMT);
+ error(JSMSG_UNNAMED_CLASS_STMT);
return null();
}
} else {
@@ -6573,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();
}
@@ -6609,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;
@@ -6621,31 +7182,30 @@ 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);
- report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
- "property name", TokenKindToDesc(tt));
+ 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 {
tokenStream.ungetToken();
}
+ uint32_t nameOffset;
+ if (!tokenStream.peekOffset(&nameOffset))
+ return null();
+
PropertyType propType;
- Node propName = propertyName(yieldHandling, classMethods, &propType, &propAtom);
+ Node propName = propertyName(yieldHandling, declKind, classMethods, &propType, &propAtom);
if (!propName)
return null();
@@ -6654,7 +7214,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
propType != PropertyType::AsyncMethod &&
propType != PropertyType::Constructor && propType != PropertyType::DerivedConstructor)
{
- report(ParseError, false, null(), JSMSG_BAD_METHOD_DEF);
+ errorAt(nameOffset, JSMSG_BAD_METHOD_DEF);
return null();
}
@@ -6662,19 +7222,20 @@ 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) {
- report(ParseError, false, propName, JSMSG_BAD_METHOD_DEF);
+ errorAt(nameOffset, JSMSG_BAD_METHOD_DEF);
return null();
}
- if (seenConstructor) {
- report(ParseError, false, propName, JSMSG_DUPLICATE_PROPERTY, "constructor");
+ 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) {
- report(ParseError, false, propName, JSMSG_BAD_METHOD_DEF);
+ errorAt(nameOffset, JSMSG_BAD_METHOD_DEF);
return null();
}
@@ -6696,7 +7257,12 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
if (!tokenStream.isCurrentTokenType(TOK_RB))
funName = propAtom;
}
- Node fn = methodDefinition(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();
@@ -6707,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) {
@@ -6718,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) {
@@ -6746,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;
@@ -6767,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;
@@ -6791,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;
}
@@ -6812,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;
}
@@ -6863,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) {
@@ -6882,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.
@@ -6893,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);
@@ -6901,8 +7471,7 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
}
if (forbiddenLetDeclaration) {
- report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT,
- "lexical declarations");
+ error(JSMSG_FORBIDDEN_AS_STATEMENT, "lexical declarations");
return null();
}
}
@@ -6918,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);
@@ -6956,7 +7522,7 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
// detected this way, so don't bother passing around an extra parameter
// everywhere.
if (!pc->isFunctionBox()) {
- report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
+ error(JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
return null();
}
return returnStatement(yieldHandling);
@@ -6966,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:
@@ -6984,12 +7550,12 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
// statement of |if| or |else|, but Parser::consequentOrAlternative
// handles that).
case TOK_FUNCTION:
- report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT, "function declarations");
+ error(JSMSG_FORBIDDEN_AS_STATEMENT, "function declarations");
return null();
// |class| is also forbidden by lookahead restriction.
case TOK_CLASS:
- report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT, "classes");
+ error(JSMSG_FORBIDDEN_AS_STATEMENT, "classes");
return null();
// ImportDeclaration (only inside modules)
@@ -7003,11 +7569,11 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
// Miscellaneous error cases arguably better caught here than elsewhere.
case TOK_CATCH:
- report(ParseError, false, null(), JSMSG_CATCH_WITHOUT_TRY);
+ error(JSMSG_CATCH_WITHOUT_TRY);
return null();
case TOK_FINALLY:
- report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY);
+ error(JSMSG_FINALLY_WITHOUT_TRY);
return null();
// NOTE: default case handled in the ExpressionStatement section.
@@ -7048,7 +7614,7 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
if (!canHaveDirectives && tokenStream.currentToken().atom() == context->names().useAsm) {
if (!abortIfSyntaxParser())
return null();
- if (!report(ParseWarning, false, null(), JSMSG_USE_ASM_DIRECTIVE_FAIL))
+ if (!warning(JSMSG_USE_ASM_DIRECTIVE_FAIL))
return null();
}
return expressionStatement(yieldHandling);
@@ -7072,25 +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 toStringStart = pos().begin;
tokenStream.consumeKnownToken(TOK_FUNCTION);
- return functionStmt(yieldHandling, NameRequired, AsyncFunction);
+ return functionStmt(toStringStart, yieldHandling, NameRequired, AsyncFunction);
}
}
@@ -7103,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);
@@ -7141,7 +7708,7 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
// detected this way, so don't bother passing around an extra parameter
// everywhere.
if (!pc->isFunctionBox()) {
- report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
+ error(JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
return null();
}
return returnStatement(yieldHandling);
@@ -7151,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:
@@ -7169,7 +7736,7 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
// HoistableDeclaration[?Yield, ~Default]
case TOK_FUNCTION:
- return functionStmt(yieldHandling, NameRequired);
+ return functionStmt(pos().begin, yieldHandling, NameRequired);
// ClassDeclaration[?Yield, ~Default]
case TOK_CLASS:
@@ -7180,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:
@@ -7193,11 +7760,11 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
// Miscellaneous error cases arguably better caught here than elsewhere.
case TOK_CATCH:
- report(ParseError, false, null(), JSMSG_CATCH_WITHOUT_TRY);
+ error(JSMSG_CATCH_WITHOUT_TRY);
return null();
case TOK_FINALLY:
- report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY);
+ error(JSMSG_FINALLY_WITHOUT_TRY);
return null();
// NOTE: default case handled in the ExpressionStatement section.
@@ -7242,8 +7809,7 @@ Parser<ParseHandler>::expr(InHandling inHandling, YieldHandling yieldHandling,
if (!tokenStream.peekToken(&tt))
return null();
if (tt != TOK_ARROW) {
- report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
- "expression", TokenKindToDesc(TOK_RP));
+ error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TOK_RP));
return null();
}
@@ -7400,7 +7966,7 @@ Parser<ParseHandler>::orExpr1(InHandling inHandling, YieldHandling yieldHandling
return null();
// Report an error for unary expressions on the LHS of **.
if (tok == TOK_POW && handler.isUnparenthesizedUnaryExpression(pn)) {
- report(ParseError, false, null(), JSMSG_BAD_POW_LEFTSIDE);
+ error(JSMSG_BAD_POW_LEFTSIDE);
return null();
}
pnk = BinaryOpTokenKindToParseNodeKind(tok);
@@ -7471,65 +8037,6 @@ Parser<ParseHandler>::condExpr1(InHandling inHandling, YieldHandling yieldHandli
}
template <typename ParseHandler>
-bool
-Parser<ParseHandler>::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor flavor,
- PossibleError* possibleError)
-{
- MOZ_ASSERT(flavor != KeyedDestructuringAssignment,
- "destructuring must use special checking/marking code, not "
- "this method");
-
- if (handler.isUnparenthesizedDestructuringPattern(target)) {
- if (flavor == CompoundAssignment) {
- report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_ASS);
- return false;
- }
-
- return checkDestructuringPattern(target, Nothing(), possibleError);
- }
-
- // All other permitted targets are simple.
- if (!reportIfNotValidSimpleAssignmentTarget(target, flavor))
- return false;
-
- if (handler.isPropertyAccess(target))
- return true;
-
- if (handler.isNameAnyParentheses(target)) {
- // The arguments/eval identifiers are simple in non-strict mode code,
- // but warn to discourage use nonetheless.
- if (!reportIfArgumentsEvalTarget(target))
- return false;
-
- handler.adjustGetToSet(target);
- return true;
- }
-
- MOZ_ASSERT(handler.isFunctionCall(target));
- return checkAssignmentToCall(target, JSMSG_BAD_LEFTSIDE_OF_ASS);
-}
-
-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,
TripledotHandling tripledotHandling,
@@ -7553,8 +8060,13 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
if (!tokenStream.getToken(&tt, TokenStream::Operand))
return null();
+ 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();
@@ -7585,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;
}
@@ -7604,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));
@@ -7620,8 +8131,7 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
if (!tokenStream.getToken(&tt))
return null();
if (tt != TOK_ARROW) {
- report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
- "'=>' after argument list", TokenKindToDesc(tt));
+ error(JSMSG_UNEXPECTED_TOKEN, "'=>' after argument list", TokenKindToDesc(tt));
return null();
}
@@ -7659,7 +8169,7 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
MOZ_ASSERT(next == TOK_ARROW || next == TOK_EOL);
if (next != TOK_ARROW) {
- report(ParseError, false, null(), JSMSG_LINE_BREAK_BEFORE_ARROW);
+ error(JSMSG_LINE_BREAK_BEFORE_ARROW);
return null();
}
tokenStream.consumeKnownToken(TOK_ARROW);
@@ -7672,32 +8182,34 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
tokenStream.seek(start);
- if (!tokenStream.peekToken(&next, TokenStream::Operand))
+ if (!tokenStream.getToken(&next, TokenStream::Operand))
return null();
+ 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;
}
}
- Node arrowFunc = functionDefinition(inHandling, yieldHandling, nullptr,
+ Node pn = handler.newArrowFunction();
+ if (!pn)
+ return null();
+
+ Node arrowFunc = functionDefinition(toStringStart, pn, inHandling, yieldHandling, nullptr,
Arrow, generatorKind, asyncKind);
if (!arrowFunc)
return null();
@@ -7747,19 +8259,42 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
return lhs;
}
- AssignmentFlavor flavor = kind == PNK_ASSIGN ? PlainAssignment : CompoundAssignment;
- if (!checkAndMarkAsAssignmentLhs(lhs, flavor, &possibleErrorInner))
+ // Verify the left-hand side expression doesn't have a forbidden form.
+ if (handler.isUnparenthesizedDestructuringPattern(lhs)) {
+ if (kind != PNK_ASSIGN) {
+ error(JSMSG_BAD_DESTRUCT_ASS);
+ return null();
+ }
+
+ 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(exprPos.begin, JSMSG_BAD_STRICT_ASSIGN, chars))
+ return null();
+ }
+
+ handler.adjustGetToSet(lhs);
+ } else if (handler.isPropertyAccess(lhs)) {
+ // Permitted: no additional testing/fixup needed.
+ } else if (handler.isFunctionCall(lhs)) {
+ if (!strictModeErrorAt(exprPos.begin, JSMSG_BAD_LEFTSIDE_OF_ASS))
+ return null();
+
+ if (possibleError)
+ possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET);
+ } else {
+ 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);
@@ -7796,91 +8331,29 @@ Parser<ParseHandler>::isValidSimpleAssignmentTarget(Node node,
template <typename ParseHandler>
bool
-Parser<ParseHandler>::reportIfArgumentsEvalTarget(Node nameNode)
-{
- const char* chars = handler.nameIsArgumentsEvalAnyParentheses(nameNode, context);
- if (!chars)
- return true;
-
- if (!report(ParseStrictError, pc->sc()->strict(), nameNode, JSMSG_BAD_STRICT_ASSIGN, chars))
- return false;
-
- MOZ_ASSERT(!pc->sc()->strict(),
- "an error should have been reported if this was strict mode "
- "code");
- return true;
-}
-
-template <typename ParseHandler>
-bool
-Parser<ParseHandler>::reportIfNotValidSimpleAssignmentTarget(Node target, AssignmentFlavor flavor)
+Parser<ParseHandler>::checkIncDecOperand(Node operand, uint32_t operandOffset)
{
- FunctionCallBehavior behavior = flavor == KeyedDestructuringAssignment
- ? ForbidAssignmentToFunctionCalls
- : PermitAssignmentToFunctionCalls;
- if (isValidSimpleAssignmentTarget(target, behavior))
- return true;
-
- if (handler.isNameAnyParentheses(target)) {
- // Use a special error if the target is arguments/eval. This ensures
- // targeting these names is consistently a SyntaxError (which error numbers
- // below don't guarantee) while giving us a nicer error message.
- if (!reportIfArgumentsEvalTarget(target))
+ if (handler.isNameAnyParentheses(operand)) {
+ if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(operand, context)) {
+ if (!strictModeErrorAt(operandOffset, JSMSG_BAD_STRICT_ASSIGN, chars))
+ return false;
+ }
+ } else if (handler.isPropertyAccess(operand)) {
+ // Permitted: no additional testing/fixup needed.
+ } else if (handler.isFunctionCall(operand)) {
+ // Assignment to function calls is forbidden in ES6. We're still
+ // somewhat concerned about sites using this in dead code, so forbid it
+ // only in strict mode code (or if the werror option has been set), and
+ // otherwise warn.
+ if (!strictModeErrorAt(operandOffset, JSMSG_BAD_INCOP_OPERAND))
return false;
- }
-
- unsigned errnum = 0;
- const char* extra = nullptr;
-
- switch (flavor) {
- case IncrementAssignment:
- errnum = JSMSG_BAD_OPERAND;
- extra = "increment";
- break;
-
- case DecrementAssignment:
- errnum = JSMSG_BAD_OPERAND;
- extra = "decrement";
- break;
-
- case KeyedDestructuringAssignment:
- errnum = JSMSG_BAD_DESTRUCT_TARGET;
- break;
-
- case PlainAssignment:
- case CompoundAssignment:
- errnum = JSMSG_BAD_LEFTSIDE_OF_ASS;
- break;
-
- case ForInOrOfTarget:
- errnum = JSMSG_BAD_FOR_LEFTSIDE;
- break;
- }
-
- report(ParseError, pc->sc()->strict(), target, errnum, extra);
- return false;
-}
-
-template <typename ParseHandler>
-bool
-Parser<ParseHandler>::checkAndMarkAsIncOperand(Node target, AssignmentFlavor flavor)
-{
- MOZ_ASSERT(flavor == IncrementAssignment || flavor == DecrementAssignment);
-
- // Check.
- if (!reportIfNotValidSimpleAssignmentTarget(target, flavor))
+ } else {
+ errorAt(operandOffset, JSMSG_BAD_INCOP_OPERAND);
return false;
-
- // Mark.
- if (handler.isNameAnyParentheses(target)) {
- // Assignment to arguments/eval is allowed outside strict mode code,
- // but it's dodgy. Report a strict warning (error, if werror was set).
- if (!reportIfArgumentsEvalTarget(target))
- return false;
- } else if (handler.isFunctionCall(target)) {
- if (!checkAssignmentToCall(target, JSMSG_BAD_INCOP_OPERAND))
- return false;
}
+
+ MOZ_ASSERT(isValidSimpleAssignmentTarget(operand, PermitAssignmentToFunctionCalls),
+ "inconsistent increment/decrement operand validation");
return true;
}
@@ -7944,18 +8417,21 @@ Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling t
TokenKind tt2;
if (!tokenStream.getToken(&tt2, TokenStream::Operand))
return null();
- Node pn2 = memberExpr(yieldHandling, TripledotProhibited, tt2);
- if (!pn2)
- return null();
- AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment;
- if (!checkAndMarkAsIncOperand(pn2, flavor))
+
+ uint32_t operandOffset = pos().begin;
+ Node operand = memberExpr(yieldHandling, TripledotProhibited, tt2);
+ if (!operand || !checkIncDecOperand(operand, operandOffset))
return null();
+
return handler.newUpdate((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT,
- begin,
- pn2);
+ begin, operand);
}
case TOK_DELETE: {
+ uint32_t exprOffset;
+ if (!tokenStream.peekOffset(&exprOffset, TokenStream::Operand))
+ return null();
+
Node expr = unaryExpr(yieldHandling, TripledotProhibited);
if (!expr)
return null();
@@ -7963,8 +8439,9 @@ Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling t
// Per spec, deleting any unary expression is valid -- it simply
// returns true -- except for one case that is illegal in strict mode.
if (handler.isNameAnyParentheses(expr)) {
- if (!report(ParseStrictError, pc->sc()->strict(), expr, JSMSG_DEPRECATED_DELETE_OPERAND))
+ if (!strictModeErrorAt(exprOffset, JSMSG_DEPRECATED_DELETE_OPERAND))
return null();
+
pc->sc()->setBindingsAccessedDynamically();
}
@@ -7972,39 +8449,35 @@ 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.
- report(ParseError, false, null(), 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 pn = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true,
+ Node expr = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true,
possibleError, invoked);
- if (!pn)
+ if (!expr)
return null();
/* Don't look across a newline boundary for a postfix incop. */
if (!tokenStream.peekTokenSameLine(&tt))
return null();
- if (tt == TOK_INC || tt == TOK_DEC) {
- tokenStream.consumeKnownToken(tt);
- AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment;
- if (!checkAndMarkAsIncOperand(pn, flavor))
- return null();
- return handler.newUpdate((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT,
- begin,
- pn);
- }
- return pn;
+
+ if (tt != TOK_INC && tt != TOK_DEC)
+ return expr;
+
+ tokenStream.consumeKnownToken(tt);
+ if (!checkIncDecOperand(expr, begin))
+ return null();
+ return handler.newUpdate((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT,
+ begin, expr);
}
}
}
@@ -8026,10 +8499,9 @@ template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin)
{
- Node genfn = handler.newFunctionDefinition();
+ Node genfn = handler.newFunctionExpression();
if (!genfn)
return null();
- handler.setOp(genfn, JSOP_LAMBDA);
ParseContext* outerpc = pc;
@@ -8049,8 +8521,8 @@ 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, directives, StarGenerator, SyncFunction,
- /* tryAnnexB = */ false);
+ FunctionBox* genFunbox = newFunctionBox(genfn, fun, /* toStringStart = */ 0, directives,
+ StarGenerator, SyncFunction, /* tryAnnexB = */ false);
if (!genFunbox)
return null();
genFunbox->isGenexpLambda = true;
@@ -8082,12 +8554,14 @@ Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin)
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
+ uint32_t end = pos().end;
handler.setBeginPosition(comp, begin);
- handler.setEndPosition(comp, pos().end);
+ handler.setEndPosition(comp, end);
+ genFunbox->setEnd(end);
handler.addStatementToList(body, comp);
- handler.setEndPosition(body, pos().end);
+ handler.setEndPosition(body, end);
handler.setBeginPosition(genfn, begin);
- handler.setEndPosition(genfn, pos().end);
+ handler.setEndPosition(genfn, end);
Node generator = newDotGeneratorName();
if (!generator)
@@ -8123,10 +8597,12 @@ 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) {
- report(ParseError, false, null(), JSMSG_LET_COMP_BINDING);
+ error(JSMSG_LET_COMP_BINDING);
return null();
}
TokenPos namePos = pos();
@@ -8134,10 +8610,10 @@ 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) {
- report(ParseError, false, null(), JSMSG_OF_AFTER_FOR_NAME);
+ error(JSMSG_OF_AFTER_FOR_NAME);
return null();
}
@@ -8198,7 +8674,7 @@ Parser<ParseHandler>::comprehensionIf(GeneratorKind comprehensionKind)
/* Check for (a = b) and warn about possible (a == b) mistype. */
if (handler.isUnparenthesizedAssignment(cond)) {
- if (!report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
+ if (!extraWarning(JSMSG_EQUAL_AS_ASSIGN))
return null();
}
@@ -8259,8 +8735,7 @@ Parser<ParseHandler>::comprehension(GeneratorKind comprehensionKind)
return null();
if (comprehensionKind != NotGenerator && pc->lastYieldOffset != startYieldOffset) {
- reportWithOffset(ParseError, false, pc->lastYieldOffset,
- JSMSG_BAD_GENEXP_BODY, js_yield_str);
+ errorAt(pc->lastYieldOffset, JSMSG_BAD_GENEXP_BODY, js_yield_str);
return null();
}
@@ -8322,11 +8797,11 @@ Parser<ParseHandler>::assignExprWithoutYieldOrAwait(YieldHandling yieldHandling)
Node res = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
if (res) {
if (pc->lastYieldOffset != startYieldOffset) {
- reportWithOffset(ParseError, false, pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT);
+ errorAt(pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT);
return null();
}
if (pc->lastAwaitOffset != startAwaitOffset) {
- reportWithOffset(ParseError, false, pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT);
+ errorAt(pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT);
return null();
}
}
@@ -8383,13 +8858,8 @@ Parser<ParseHandler>::argumentList(YieldHandling yieldHandling, Node listNode, b
}
}
- TokenKind tt;
- if (!tokenStream.getToken(&tt))
- return false;
- if (tt != TOK_RP) {
- report(ParseError, false, null(), JSMSG_PAREN_AFTER_ARGS);
- return false;
- }
+ MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_ARGS);
+
handler.setEndPosition(listNode, pos().end);
return true;
}
@@ -8475,19 +8945,19 @@ 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()) {
- report(ParseError, false, null(), JSMSG_BAD_SUPERPROP, "property");
+ error(JSMSG_BAD_SUPERPROP, "property");
return null();
}
nextMember = handler.newPropertyAccess(lhs, field, pos().end);
if (!nextMember)
return null();
} else {
- report(ParseError, false, null(), JSMSG_NAME_AFTER_DOT);
+ error(JSMSG_NAME_AFTER_DOT);
return null();
}
} else if (tt == TOK_LB) {
@@ -8498,7 +8968,7 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
- report(ParseError, false, null(), JSMSG_BAD_SUPERPROP, "member");
+ error(JSMSG_BAD_SUPERPROP, "member");
return null();
}
nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end);
@@ -8510,12 +8980,12 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
{
if (handler.isSuperBase(lhs)) {
if (!pc->sc()->allowSuperCall()) {
- report(ParseError, false, null(), JSMSG_BAD_SUPERCALL);
+ error(JSMSG_BAD_SUPERCALL);
return null();
}
if (tt != TOK_LP) {
- report(ParseError, false, null(), JSMSG_BAD_SUPER);
+ error(JSMSG_BAD_SUPER);
return null();
}
@@ -8542,7 +9012,7 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
return null();
} else {
if (options().selfHostingMode && handler.isPropertyAccess(lhs)) {
- report(ParseError, false, null(), JSMSG_SELFHOSTED_METHOD_CALL);
+ error(JSMSG_SELFHOSTED_METHOD_CALL);
return null();
}
@@ -8552,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;
@@ -8570,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;
}
}
@@ -8624,7 +9095,7 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
}
if (handler.isSuperBase(lhs)) {
- report(ParseError, false, null(), JSMSG_BAD_SUPER);
+ error(JSMSG_BAD_SUPER);
return null();
}
@@ -8646,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) {
- report(ParseError, false, null(), 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)
- {
- report(ParseError, false, null(), 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) {
- report(ParseError, false, null(), JSMSG_BAD_STRICT_ASSIGN, badName);
- return nullptr;
- }
+ return true;
+}
- badName = ident == context->names().let
- ? "let"
- : ident == context->names().static_
- ? "static"
- : nullptr;
- if (badName) {
- report(ParseError, false, null(), JSMSG_RESERVED_ID, badName);
- return nullptr;
- }
- }
- } else {
- if (yieldHandling == YieldIsKeyword ||
- pc->sc()->strict() ||
- versionNumber() >= JSVERSION_1_7)
- {
- report(ParseError, false, null(), 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;
}
@@ -8757,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;
@@ -8772,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());
}
@@ -8809,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)
{
@@ -8840,7 +9405,7 @@ Parser<ParseHandler>::arrayInitializer(YieldHandling yieldHandling, PossibleErro
TokenStream::Modifier modifier = TokenStream::Operand;
for (; ; index++) {
if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
- report(ParseError, false, null(), JSMSG_ARRAY_INIT_TOO_BIG);
+ error(JSMSG_ARRAY_INIT_TOO_BIG);
return null();
}
@@ -8857,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);
@@ -8883,11 +9460,13 @@ Parser<ParseHandler>::arrayInitializer(YieldHandling yieldHandling, PossibleErro
break;
}
if (tt == TOK_TRIPLEDOT && possibleError)
- possibleError->setPendingDestructuringError(null(), JSMSG_REST_WITH_COMMA);
+ possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA);
}
}
- 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;
@@ -8903,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");
@@ -8916,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] ...
//
@@ -8936,21 +9516,18 @@ 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();
}
}
if (isAsync && isGenerator) {
- report(ParseError, false, null(), JSMSG_ASYNC_GENERATOR);
+ error(JSMSG_ASYNC_GENERATOR);
return null();
}
@@ -8967,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());
@@ -9020,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())
@@ -9031,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::KeywordIsName);
+ tokenStream.consumeKnownToken(TOK_LB);
- 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;
}
@@ -9061,10 +9626,6 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
return null();
break;
}
-
- default:
- report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
- return null();
}
TokenKind tt;
@@ -9073,16 +9634,18 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
if (tt == TOK_COLON) {
if (isGenerator) {
- report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
+ error(JSMSG_BAD_PROP_ID);
return null();
}
*propType = PropertyType::Normal;
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) {
- report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
+ error(JSMSG_BAD_PROP_ID);
return null();
}
tokenStream.ungetToken();
@@ -9103,34 +9666,31 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
return propName;
}
- report(ParseError, false, null(), JSMSG_COLON_AFTER_ID);
+ error(JSMSG_COLON_AFTER_ID);
return null();
}
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>
@@ -9139,205 +9699,274 @@ 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;
- tokenStream.ungetToken();
+ if (tt == TOK_TRIPLEDOT) {
+ // object spread
+ tokenStream.consumeKnownToken(TOK_TRIPLEDOT);
+ uint32_t begin = pos().begin;
- PropertyType propType;
- Node propName = propertyName(yieldHandling, literal, &propType, &propAtom);
- if (!propName)
- return null();
+ TokenPos innerPos;
+ if (!tokenStream.peekTokenPos(&innerPos, TokenStream::Operand))
+ return null();
- if (propType == PropertyType::Normal) {
- Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
- possibleError);
- if (!propExpr)
+ 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;
- handler.checkAndSetIsDirectRHSAnonFunction(propExpr);
+ PropertyType propType;
+ Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom);
+ if (!propName)
- if (foldConstants && !FoldConstants(context, &propExpr, this))
return null();
- if (propAtom == context->names().proto) {
- if (seenPrototypeMutation) {
- // Directly report the error when we're not in a
- // destructuring context.
- if (!possibleError) {
- report(ParseError, false, propName, JSMSG_DUPLICATE_PROTO_PROPERTY);
- return null();
- }
-
- // Otherwise delay error reporting until we've determined
- // whether or not we're destructuring.
- possibleError->setPendingExpressionError(propName,
- JSMSG_DUPLICATE_PROTO_PROPERTY);
- }
- seenPrototypeMutation = true;
-
- // 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.
- uint32_t begin = handler.getPosition(propName).begin;
- if (!handler.addPrototypeMutation(literal, 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) {
- report(ParseError, false, null(), 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) {
- report(ParseError, false, null(), 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();
+
+ Node nameExpr = identifierReference(name);
+ if (!nameExpr)
+ return null();
- tokenStream.consumeKnownToken(TOK_ASSIGN);
+ if (possibleError)
+ checkDestructuringAssignmentTarget(nameExpr, namePos, possibleError);
- if (!seenCoverInitializedName) {
- // "shorthand default" or "CoverInitializedName" syntax is only
- // valid in the case of destructuring.
- seenCoverInitializedName = true;
+ 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();
- 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}`.
- report(ParseError, false, null(), JSMSG_COLON_AFTER_ID);
+ 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->setPendingExpressionError(null(), 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(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) {
- report(ParseError, false, null(), 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(PropertyType propType, HandleAtom funName)
+Parser<ParseHandler>::methodDefinition(uint32_t toStringStart, PropertyType propType,
+ HandleAtom funName)
{
- FunctionSyntaxKind kind = FunctionSyntaxKindFromPropertyType(propType);
- GeneratorKind generatorKind = GeneratorKindFromPropertyType(propType);
- FunctionAsyncKind asyncKind = AsyncKindFromPropertyType(propType);
+ FunctionSyntaxKind kind;
+ switch (propType) {
+ case PropertyType::Getter:
+ kind = Getter;
+ break;
+
+ case PropertyType::GetterNoExpressionClosure:
+ kind = GetterNoExpressionClosure;
+ break;
+
+ case PropertyType::Setter:
+ kind = Setter;
+ break;
+
+ case PropertyType::SetterNoExpressionClosure:
+ kind = SetterNoExpressionClosure;
+ break;
+
+ case PropertyType::Method:
+ case PropertyType::GeneratorMethod:
+ case PropertyType::AsyncMethod:
+ kind = Method;
+ break;
+
+ case PropertyType::Constructor:
+ kind = ClassConstructor;
+ break;
+
+ case PropertyType::DerivedConstructor:
+ kind = DerivedClassConstructor;
+ break;
+
+ default:
+ MOZ_CRASH("Parser: methodDefinition: unexpected property type");
+ }
+
+ GeneratorKind generatorKind = (propType == PropertyType::GeneratorMethod ||
+ propType == PropertyType::AsyncMethod)
+ ? StarGenerator
+ : NotGenerator;
+
+ FunctionAsyncKind asyncKind = (propType == PropertyType::AsyncMethod)
+ ? AsyncFunction
+ : SyncFunction;
+
YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
- return functionDefinition(InAllowed, yieldHandling, funName, kind, generatorKind, asyncKind);
+
+ Node pn = handler.newFunctionExpression();
+ if (!pn)
+ return null();
+
+ return functionDefinition(toStringStart, pn, InAllowed, yieldHandling, funName,
+ kind, generatorKind, asyncKind);
}
template <typename ParseHandler>
@@ -9366,17 +9995,13 @@ Parser<ParseHandler>::tryNewTarget(Node &newTarget)
if (!tokenStream.getToken(&next))
return false;
- if (next != TOK_NAME || tokenStream.currentName() != context->names().target) {
- report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
- "target", TokenKindToDesc(next));
+ if (next != TOK_TARGET) {
+ error(JSMSG_UNEXPECTED_TOKEN, "target", TokenKindToDesc(next));
return false;
}
- if (!checkUnescapedName())
- return false;
-
if (!pc->sc()->allowNewTarget()) {
- reportWithOffset(ParseError, false, begin, JSMSG_BAD_NEWTARGET);
+ errorAt(begin, JSMSG_BAD_NEWTARGET);
return false;
}
@@ -9399,7 +10024,7 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
switch (tt) {
case TOK_FUNCTION:
- return functionExpr(invoked);
+ return functionExpr(pos().begin, invoked);
case TOK_CLASS:
return classDefinition(yieldHandling, ClassExpression, NameRequired);
@@ -9423,8 +10048,7 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
if (!tokenStream.peekToken(&next))
return null();
if (next != TOK_ARROW) {
- report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
- "expression", TokenKindToDesc(TOK_RP));
+ error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TOK_RP));
return null();
}
@@ -9445,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);
}
@@ -9453,21 +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 toStringStart = pos().begin;
tokenStream.consumeKnownToken(TOK_FUNCTION);
- return functionExpr(PredictUninvoked, AsyncFunction);
+ return functionExpr(toStringStart, PredictUninvoked, AsyncFunction);
}
}
@@ -9510,8 +10138,7 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
// name, closing parenthesis, and arrow, and allow it only if all are
// present.
if (tripledotHandling != TripledotAllowed) {
- report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
- "expression", TokenKindToDesc(tt));
+ error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt));
return null();
}
@@ -9532,9 +10159,8 @@ 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) {
- report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
- "rest argument name", TokenKindToDesc(next));
+ if (!TokenKindIsPossibleIdentifier(next)) {
+ error(JSMSG_UNEXPECTED_TOKEN, "rest argument name", TokenKindToDesc(next));
return null();
}
}
@@ -9542,8 +10168,7 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
if (!tokenStream.getToken(&next))
return null();
if (next != TOK_RP) {
- report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
- "closing parenthesis", TokenKindToDesc(next));
+ error(JSMSG_UNEXPECTED_TOKEN, "closing parenthesis", TokenKindToDesc(next));
return null();
}
@@ -9552,8 +10177,7 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
if (next != TOK_ARROW) {
// Advance the scanner for proper error location reporting.
tokenStream.consumeKnownToken(next);
- report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
- "'=>' after argument list", TokenKindToDesc(next));
+ error(JSMSG_UNEXPECTED_TOKEN, "'=>' after argument list", TokenKindToDesc(next));
return null();
}
@@ -9562,11 +10186,6 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
// Return an arbitrary expression node. See case TOK_RP above.
return handler.newNullLiteral(pos());
}
-
- default:
- report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
- "expression", TokenKindToDesc(tt));
- return null();
}
}
@@ -9580,19 +10199,8 @@ Parser<ParseHandler>::exprInParens(InHandling inHandling, YieldHandling yieldHan
return expr(inHandling, yieldHandling, tripledotHandling, possibleError, PredictInvoked);
}
-template <typename ParseHandler>
-void
-Parser<ParseHandler>::addTelemetry(JSCompartment::DeprecatedLanguageExtension e)
-{
- JSContext* cx = context->maybeJSContext();
- if (!cx)
- return;
- cx->compartment()->addTelemetry(getFilename(), e);
-}
-
-template <typename ParseHandler>
bool
-Parser<ParseHandler>::warnOnceAboutExprClosure()
+ParserBase::warnOnceAboutExprClosure()
{
#ifndef RELEASE_OR_BETA
JSContext* cx = context->maybeJSContext();
@@ -9600,7 +10208,7 @@ Parser<ParseHandler>::warnOnceAboutExprClosure()
return true;
if (!cx->compartment()->warnedAboutExprClosure) {
- if (!report(ParseWarning, false, null(), JSMSG_DEPRECATED_EXPR_CLOSURE))
+ if (!warning(JSMSG_DEPRECATED_EXPR_CLOSURE))
return false;
cx->compartment()->warnedAboutExprClosure = true;
}
@@ -9608,9 +10216,8 @@ Parser<ParseHandler>::warnOnceAboutExprClosure()
return true;
}
-template <typename ParseHandler>
bool
-Parser<ParseHandler>::warnOnceAboutForEach()
+ParserBase::warnOnceAboutForEach()
{
JSContext* cx = context->maybeJSContext();
if (!cx)
@@ -9618,7 +10225,7 @@ Parser<ParseHandler>::warnOnceAboutForEach()
if (!cx->compartment()->warnedAboutForEach) {
// Disabled warning spew.
- // if (!report(ParseWarning, false, null(), JSMSG_DEPRECATED_FOR_EACH))
+ // if (!warning(JSMSG_DEPRECATED_FOR_EACH))
// return false;
cx->compartment()->warnedAboutForEach = true;
}
diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h
index b58b021cd..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;
@@ -769,13 +928,13 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
*
* Ex:
* PossibleError possibleError(*this);
- * possibleError.setPendingExpressionError(pn, JSMSG_BAD_PROP_ID);
+ * possibleError.setPendingExpressionErrorAt(pos, JSMSG_BAD_PROP_ID);
* // A JSMSG_BAD_PROP_ID ParseError is reported, returns false.
* if (!possibleError.checkForExpressionError())
* return false; // we reach this point with a pending exception
*
* PossibleError possibleError(*this);
- * possibleError.setPendingExpressionError(pn, JSMSG_BAD_PROP_ID);
+ * possibleError.setPendingExpressionErrorAt(pos, JSMSG_BAD_PROP_ID);
* // Returns true, no error is reported.
* if (!possibleError.checkForDestructuringError())
* return false; // not reached, no pending exception
@@ -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.
@@ -815,11 +975,15 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
// Set a pending error. Only a single error may be set per instance and
// error kind.
- void setPending(ErrorKind kind, Node pn, unsigned errorNumber);
+ void setPending(ErrorKind kind, const TokenPos& pos, unsigned errorNumber);
// 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 setPendingDestructuringError(Node pn, unsigned errorNumber);
+ 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 setPendingExpressionError(Node pn, unsigned errorNumber);
+ 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,70 +1027,21 @@ 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;
void prepareNodeForMutation(Node node) { handler.prepareNodeForMutation(node); }
void freeTree(Node node) { handler.freeTree(node); }
- private:
- bool reportHelper(ParseReportKind kind, bool strict, uint32_t offset,
- unsigned errorNumber, va_list args);
public:
- bool report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...);
- bool reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...);
- bool reportWithOffset(ParseReportKind kind, bool strict, uint32_t offset, 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
@@ -941,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.
*/
@@ -954,7 +1076,8 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
* cx->tempLifoAlloc.
*/
ObjectBox* newObjectBox(JSObject* obj);
- FunctionBox* newFunctionBox(Node fn, JSFunction* fun, Directives directives,
+ FunctionBox* newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart,
+ Directives directives,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
bool tryAnnexB);
@@ -968,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);
@@ -1034,43 +1147,23 @@ 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, InHandling inHandling,
- YieldHandling yieldHandling, FunctionSyntaxKind kind,
+ bool innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox, uint32_t toStringStart,
+ InHandling inHandling, YieldHandling yieldHandling,
+ FunctionSyntaxKind kind,
Directives inheritedDirectives, Directives* newDirectives);
// Parse a function's formal parameters and its body assuming its function
// ParseContext is already on the stack.
bool functionFormalParametersAndBody(InHandling inHandling, YieldHandling yieldHandling,
Node pn, FunctionSyntaxKind kind,
- 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.
@@ -1088,9 +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(YieldHandling yieldHandling, DefaultHandling defaultHandling,
+ Node functionStmt(uint32_t toStringStart,
+ YieldHandling yieldHandling, DefaultHandling defaultHandling,
FunctionAsyncKind asyncKind = SyncFunction);
- Node functionExpr(InvokedPrediction invoked = PredictUninvoked,
+ Node functionExpr(uint32_t toStringStart, InvokedPrediction invoked = PredictUninvoked,
FunctionAsyncKind asyncKind = SyncFunction);
Node statementList(YieldHandling yieldHandling);
@@ -1106,7 +1200,6 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
Node* forInitialPart,
mozilla::Maybe<ParseContext::Scope>& forLetImpliedScope,
Node* forInOrOfExpression);
- bool validateForInOrOfLHSExpression(Node target, PossibleError* possibleError);
Node expressionAfterForInOrOf(ParseNodeKind forHeadKind, YieldHandling yieldHandling);
Node switchStatement(YieldHandling yieldHandling);
@@ -1132,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);
@@ -1222,7 +1333,7 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
bool tryNewTarget(Node& newTarget);
bool checkAndMarkSuperScope();
- Node methodDefinition(PropertyType propType, HandleAtom funName);
+ Node methodDefinition(uint32_t toStringStart, PropertyType propType, HandleAtom funName);
/*
* Additional JS parsers.
@@ -1230,10 +1341,11 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
bool functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind,
Node funcpn);
- Node functionDefinition(InHandling inHandling, YieldHandling yieldHandling, HandleAtom name,
+ Node functionDefinition(uint32_t toStringStart, Node pn,
+ InHandling inHandling, YieldHandling yieldHandling, HandleAtom name,
FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
- InvokedPrediction invoked = PredictUninvoked);
+ bool tryAnnexB = false);
// Parse a function body. Pass StatementListBody if the body is a list of
// statements; pass ExpressionBody if the body is a single expression.
@@ -1265,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() {
@@ -1298,17 +1423,6 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
#endif
}
- enum AssignmentFlavor {
- PlainAssignment,
- CompoundAssignment,
- KeyedDestructuringAssignment,
- IncrementAssignment,
- DecrementAssignment,
- ForInOrOfTarget
- };
-
- bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor,
- PossibleError* possibleError=nullptr);
bool matchInOrOf(bool* isForInp, bool* isForOfp);
bool hasUsedFunctionSpecialName(HandlePropertyName name);
@@ -1319,23 +1433,27 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
Node newDotGeneratorName();
bool declareDotGeneratorName();
- bool checkFunctionDefinition(HandleAtom funAtom, Node pn, FunctionSyntaxKind kind,
- GeneratorKind generatorKind, bool* tryAnnexB);
- bool skipLazyInnerFunction(Node pn, FunctionSyntaxKind kind, bool tryAnnexB);
- bool innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun,
+ bool skipLazyInnerFunction(Node pn, uint32_t toStringStart, FunctionSyntaxKind kind,
+ bool tryAnnexB);
+ 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, InHandling inHandling,
- YieldHandling yieldHandling, FunctionSyntaxKind kind,
+ 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,
@@ -1346,26 +1464,24 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls);
private:
- bool reportIfArgumentsEvalTarget(Node nameNode);
- bool reportIfNotValidSimpleAssignmentTarget(Node target, AssignmentFlavor flavor);
-
- bool checkAndMarkAsIncOperand(Node kid, AssignmentFlavor flavor);
+ 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);
@@ -1384,27 +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);
-
- bool checkAssignmentToCall(Node node, unsigned errnum);
+ 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);
@@ -1412,18 +1528,28 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
static Node null() { return ParseHandler::null(); }
- bool reportBadReturn(Node pn, ParseReportKind kind, unsigned errnum, unsigned anonerrnum);
-
JSAtom* prefixAccessorName(PropertyType propType, HandleAtom propAtom);
- TokenPos pos() const { return tokenStream.currentToken().pos; }
-
bool asmJS(Node list);
+};
+
+template <typename ParseHandler>
+class MOZ_STACK_CLASS AutoAwaitIsKeyword
+{
+ private:
+ Parser<ParseHandler>* parser_;
+ bool oldAwaitIsKeyword_;
- void addTelemetry(JSCompartment::DeprecatedLanguageExtension e);
+ public:
+ AutoAwaitIsKeyword(Parser<ParseHandler>* parser, bool awaitIsKeyword) {
+ parser_ = parser;
+ oldAwaitIsKeyword_ = parser_->awaitIsKeyword_;
+ parser_->setAwaitIsKeyword(awaitIsKeyword);
+ }
- bool warnOnceAboutExprClosure();
- bool warnOnceAboutForEach();
+ ~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 a6ac542f6..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,6 +451,8 @@ class FunctionBox : public ObjectBox, public SharedContext
uint32_t bufEnd;
uint32_t startLine;
uint32_t startColumn;
+ uint32_t toStringStart;
+ uint32_t toStringEnd;
uint16_t length;
uint8_t generatorKindBits_; /* The GeneratorKind of this function. */
@@ -472,12 +475,15 @@ 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,
- Directives directives, bool extraWarnings, GeneratorKind generatorKind,
- FunctionAsyncKind asyncKind);
+ uint32_t toStringStart, Directives directives, bool extraWarnings,
+ GeneratorKind generatorKind, FunctionAsyncKind asyncKind);
MutableHandle<LexicalScope::Data*> namedLambdaBindings() {
MOZ_ASSERT(context->compartment()->runtimeFromAnyThread()->keepAtoms());
@@ -545,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
@@ -594,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 b7f00605b..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) {
@@ -342,7 +363,10 @@ class SyntaxParseHandler
void checkAndSetIsDirectRHSAnonFunction(Node pn) {}
- Node newFunctionDefinition() { return NodeFunctionDefinition; }
+ Node newFunctionStatement() { return NodeFunctionDefinition; }
+ Node newFunctionExpression() { return NodeFunctionDefinition; }
+ Node newArrowFunction() { return NodeFunctionDefinition; }
+
bool setComprehensionLambdaBody(Node pn, Node body) { return true; }
void setFunctionFormalParametersAndBody(Node pn, Node kid) {}
void setFunctionBody(Node pn, Node kid) {}
@@ -494,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)
@@ -519,15 +543,15 @@ class SyntaxParseHandler
MOZ_MUST_USE Node setLikelyIIFE(Node pn) {
return pn; // Remain in syntax-parse mode.
}
- void setPrologue(Node pn) {}
+ void setInDirectivePrologue(Node pn) {}
bool isConstant(Node pn) { return false; }
bool isUnparenthesizedName(Node node) {
return node == NodeUnparenthesizedArgumentsName ||
- node == NodeUnparenthesizedAsyncName ||
node == NodeUnparenthesizedEvalName ||
- node == NodeUnparenthesizedName;
+ node == NodeUnparenthesizedName ||
+ node == NodePotentialAsyncKeyword;
}
bool isNameAnyParentheses(Node node) {
@@ -538,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;
}
@@ -548,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 179a7c244..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>
@@ -172,6 +174,12 @@ frontend::IsIdentifier(JSLinearString* str)
}
bool
+frontend::IsIdentifier(const char* chars, size_t length)
+{
+ return ::IsIdentifier(chars, length);
+}
+
+bool
frontend::IsIdentifier(const char16_t* chars, size_t length)
{
return ::IsIdentifier(chars, length);
@@ -180,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)
@@ -217,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 {
@@ -548,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;
}
@@ -593,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;
@@ -605,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
@@ -631,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);
@@ -649,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;
@@ -740,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;
@@ -751,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;
}
@@ -762,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::reportStrictWarningErrorNumberVA(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
@@ -796,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);
}
@@ -928,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
@@ -987,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_);
}
@@ -997,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_);
}
@@ -1104,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
@@ -1357,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;
@@ -1532,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();
}
@@ -1684,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))
@@ -1751,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))
@@ -1768,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;
@@ -1791,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;
@@ -1808,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();
@@ -1825,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();
@@ -1844,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;
@@ -1891,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;
@@ -1911,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) {
@@ -1939,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;
@@ -1952,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;
@@ -1963,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;
@@ -1979,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);
@@ -1997,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 5d6b4b795..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,22 +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 reportStrictWarningErrorNumberVA(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, ...);
@@ -415,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);
@@ -432,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
@@ -475,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");
}
@@ -505,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) {
@@ -570,6 +629,14 @@ class MOZ_STACK_CLASS TokenStream
return true;
}
+ MOZ_MUST_USE bool peekOffset(uint32_t* offset, Modifier modifier = None) {
+ TokenPos pos;
+ if (!peekTokenPos(&pos, modifier))
+ return false;
+ *offset = pos.begin;
+ return true;
+ }
+
// This is like peekToken(), with one exception: if there is an EOL
// between the end of the current token and the start of the next token, it
// return true and store TOK_EOL in |*ttp|. In that case, no token with
@@ -637,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))
@@ -732,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
@@ -940,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();
@@ -957,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);
@@ -975,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; }
@@ -1022,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/Barrier.h b/js/src/gc/Barrier.h
index effc9233e..dce3b2a20 100644
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -667,29 +667,15 @@ class HeapSlot : public WriteBarrieredBase<Value>
Element = 1
};
- explicit HeapSlot() = delete;
-
- explicit HeapSlot(NativeObject* obj, Kind kind, uint32_t slot, const Value& v)
- : WriteBarrieredBase<Value>(v)
- {
- post(obj, kind, slot, v);
- }
-
- explicit HeapSlot(NativeObject* obj, Kind kind, uint32_t slot, const HeapSlot& s)
- : WriteBarrieredBase<Value>(s.value)
- {
- post(obj, kind, slot, s);
- }
-
- ~HeapSlot() {
- pre();
- }
-
void init(NativeObject* owner, Kind kind, uint32_t slot, const Value& v) {
value = v;
post(owner, kind, slot, v);
}
+ void destroy() {
+ pre();
+ }
+
#ifdef DEBUG
bool preconditionForSet(NativeObject* owner, Kind kind, uint32_t slot) const;
bool preconditionForWriteBarrierPost(NativeObject* obj, Kind kind, uint32_t slot,
@@ -703,11 +689,6 @@ class HeapSlot : public WriteBarrieredBase<Value>
post(owner, kind, slot, v);
}
- /* For users who need to manually barrier the raw types. */
- static void writeBarrierPost(NativeObject* owner, Kind kind, uint32_t slot, const Value& target) {
- reinterpret_cast<HeapSlot*>(const_cast<Value*>(&target))->post(owner, kind, slot, target);
- }
-
private:
void post(NativeObject* owner, Kind kind, uint32_t slot, const Value& target) {
MOZ_ASSERT(preconditionForWriteBarrierPost(owner, kind, slot, target));
diff --git a/js/src/gc/GCInternals.h b/js/src/gc/GCInternals.h
index 4919b87a5..e8df0bb70 100644
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -9,7 +9,6 @@
#include "mozilla/ArrayUtils.h"
#include "mozilla/Maybe.h"
-#include "mozilla/PodOperations.h"
#include "jscntxt.h"
@@ -102,9 +101,9 @@ struct TenureCountCache
static const size_t EntryShift = 4;
static const size_t EntryCount = 1 << EntryShift;
- TenureCount entries[EntryCount];
+ TenureCount entries[EntryCount] = {}; // zeroes
- TenureCountCache() { mozilla::PodZero(this); }
+ TenureCountCache() = default;
HashNumber hash(ObjectGroup* group) {
#if JS_BITS_PER_WORD == 32
diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h
index 5c2576efd..f102e9ef0 100644
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -900,7 +900,8 @@ class GCRuntime
void requestMajorGC(JS::gcreason::Reason reason);
SliceBudget defaultBudget(JS::gcreason::Reason reason, int64_t millis);
- void budgetIncrementalGC(SliceBudget& budget, AutoLockForExclusiveAccess& lock);
+ void budgetIncrementalGC(JS::gcreason::Reason reason, SliceBudget& budget,
+ AutoLockForExclusiveAccess& lock);
void resetIncrementalGC(AbortReason reason, AutoLockForExclusiveAccess& lock);
// Assert if the system state is such that we should never
@@ -915,6 +916,7 @@ class GCRuntime
void collect(bool nonincrementalByAPI, SliceBudget budget, JS::gcreason::Reason reason) JS_HAZ_GC_CALL;
MOZ_MUST_USE bool gcCycle(bool nonincrementalByAPI, SliceBudget& budget,
JS::gcreason::Reason reason);
+ bool shouldRepeatForDeadZone(JS::gcreason::Reason reason);
void incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason reason,
AutoLockForExclusiveAccess& lock);
diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp
index b2c105999..262fc8cbc 100644
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -18,6 +18,7 @@
#include "builtin/ModuleObject.h"
#include "gc/GCInternals.h"
#include "gc/Policy.h"
+#include "gc/StoreBuffer-inl.h"
#include "jit/IonCode.h"
#include "js/SliceBudget.h"
#include "vm/ArgumentsObject.h"
@@ -28,7 +29,6 @@
#include "vm/Shape.h"
#include "vm/Symbol.h"
#include "vm/TypedArrayObject.h"
-#include "vm/UnboxedObject.h"
#include "wasm/WasmJS.h"
#include "jscompartmentinlines.h"
@@ -37,7 +37,6 @@
#include "gc/Nursery-inl.h"
#include "vm/String-inl.h"
-#include "vm/UnboxedObject-inl.h"
using namespace js;
using namespace js::gc;
@@ -1231,34 +1230,34 @@ BindingIter::trace(JSTracer* trc)
void
LexicalScope::Data::trace(JSTracer* trc)
{
- TraceBindingNames(trc, names, length);
+ TraceBindingNames(trc, trailingNames.start(), length);
}
void
FunctionScope::Data::trace(JSTracer* trc)
{
TraceNullableEdge(trc, &canonicalFunction, "scope canonical function");
- TraceNullableBindingNames(trc, names, length);
+ TraceNullableBindingNames(trc, trailingNames.start(), length);
}
void
VarScope::Data::trace(JSTracer* trc)
{
- TraceBindingNames(trc, names, length);
+ TraceBindingNames(trc, trailingNames.start(), length);
}
void
GlobalScope::Data::trace(JSTracer* trc)
{
- TraceBindingNames(trc, names, length);
+ TraceBindingNames(trc, trailingNames.start(), length);
}
void
EvalScope::Data::trace(JSTracer* trc)
{
- TraceBindingNames(trc, names, length);
+ TraceBindingNames(trc, trailingNames.start(), length);
}
void
ModuleScope::Data::trace(JSTracer* trc)
{
TraceNullableEdge(trc, &module, "scope module");
- TraceBindingNames(trc, names, length);
+ TraceBindingNames(trc, trailingNames.start(), length);
}
void
Scope::traceChildren(JSTracer* trc)
@@ -1302,13 +1301,13 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope)
traverseEdge(scope, static_cast<Scope*>(scope->enclosing_));
if (scope->environmentShape_)
traverseEdge(scope, static_cast<Shape*>(scope->environmentShape_));
- BindingName* names = nullptr;
+ TrailingNamesArray* names = nullptr;
uint32_t length = 0;
switch (scope->kind_) {
case ScopeKind::Function: {
FunctionScope::Data* data = reinterpret_cast<FunctionScope::Data*>(scope->data_);
traverseEdge(scope, static_cast<JSObject*>(data->canonicalFunction));
- names = data->names;
+ names = &data->trailingNames;
length = data->length;
break;
}
@@ -1316,7 +1315,7 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope)
case ScopeKind::FunctionBodyVar:
case ScopeKind::ParameterExpressionVar: {
VarScope::Data* data = reinterpret_cast<VarScope::Data*>(scope->data_);
- names = data->names;
+ names = &data->trailingNames;
length = data->length;
break;
}
@@ -1327,7 +1326,7 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope)
case ScopeKind::NamedLambda:
case ScopeKind::StrictNamedLambda: {
LexicalScope::Data* data = reinterpret_cast<LexicalScope::Data*>(scope->data_);
- names = data->names;
+ names = &data->trailingNames;
length = data->length;
break;
}
@@ -1335,7 +1334,7 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope)
case ScopeKind::Global:
case ScopeKind::NonSyntactic: {
GlobalScope::Data* data = reinterpret_cast<GlobalScope::Data*>(scope->data_);
- names = data->names;
+ names = &data->trailingNames;
length = data->length;
break;
}
@@ -1343,7 +1342,7 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope)
case ScopeKind::Eval:
case ScopeKind::StrictEval: {
EvalScope::Data* data = reinterpret_cast<EvalScope::Data*>(scope->data_);
- names = data->names;
+ names = &data->trailingNames;
length = data->length;
break;
}
@@ -1351,7 +1350,7 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope)
case ScopeKind::Module: {
ModuleScope::Data* data = reinterpret_cast<ModuleScope::Data*>(scope->data_);
traverseEdge(scope, static_cast<JSObject*>(data->module));
- names = data->names;
+ names = &data->trailingNames;
length = data->length;
break;
}
@@ -1361,12 +1360,12 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope)
}
if (scope->kind_ == ScopeKind::Function) {
for (uint32_t i = 0; i < length; i++) {
- if (JSAtom* name = names[i].name())
+ if (JSAtom* name = names->operator[](i).name())
traverseEdge(scope, static_cast<JSString*>(name));
}
} else {
for (uint32_t i = 0; i < length; i++)
- traverseEdge(scope, static_cast<JSString*>(names[i].name()));
+ traverseEdge(scope, static_cast<JSString*>(names->operator[](i).name()));
}
}
@@ -1395,14 +1394,6 @@ js::ObjectGroup::traceChildren(JSTracer* trc)
if (maybePreliminaryObjects())
maybePreliminaryObjects()->trace(trc);
- if (maybeUnboxedLayout())
- unboxedLayout().trace(trc);
-
- if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup()) {
- TraceManuallyBarrieredEdge(trc, &unboxedGroup, "group_original_unboxed_group");
- setOriginalUnboxedGroup(unboxedGroup);
- }
-
if (JSObject* descr = maybeTypeDescr()) {
TraceManuallyBarrieredEdge(trc, &descr, "group_type_descr");
setTypeDescr(&descr->as<TypeDescr>());
@@ -1436,12 +1427,6 @@ js::GCMarker::lazilyMarkChildren(ObjectGroup* group)
if (group->maybePreliminaryObjects())
group->maybePreliminaryObjects()->trace(this);
- if (group->maybeUnboxedLayout())
- group->unboxedLayout().trace(this);
-
- if (ObjectGroup* unboxedGroup = group->maybeOriginalUnboxedGroup())
- traverseEdge(group, unboxedGroup);
-
if (TypeDescr* descr = group->maybeTypeDescr())
traverseEdge(group, static_cast<JSObject*>(descr));
@@ -1484,23 +1469,6 @@ CallTraceHook(Functor f, JSTracer* trc, JSObject* obj, CheckGeneration check, Ar
return nullptr;
}
- if (clasp == &UnboxedPlainObject::class_) {
- JSObject** pexpando = obj->as<UnboxedPlainObject>().addressOfExpando();
- if (*pexpando)
- f(pexpando, mozilla::Forward<Args>(args)...);
-
- UnboxedPlainObject& unboxed = obj->as<UnboxedPlainObject>();
- const UnboxedLayout& layout = check == CheckGeneration::DoChecks
- ? unboxed.layout()
- : unboxed.layoutDontCheckGeneration();
- if (layout.traceList()) {
- VisitTraceList(f, layout.traceList(), unboxed.data(),
- mozilla::Forward<Args>(args)...);
- }
-
- return nullptr;
- }
-
clasp->doTrace(trc, obj);
if (!clasp->isNative())
@@ -2293,18 +2261,6 @@ static inline void
TraceWholeCell(TenuringTracer& mover, JSObject* object)
{
mover.traceObject(object);
-
- // Additionally trace the expando object attached to any unboxed plain
- // objects. Baseline and Ion can write properties to the expando while
- // only adding a post barrier to the owning unboxed object. Note that
- // it isn't possible for a nursery unboxed object to have a tenured
- // expando, so that adding a post barrier on the original object will
- // capture any tenured->nursery edges in the expando as well.
-
- if (object->is<UnboxedPlainObject>()) {
- if (UnboxedExpandoObject* expando = object->as<UnboxedPlainObject>().maybeExpando())
- expando->traceChildren(&mover);
- }
}
static inline void
@@ -2548,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>()) {
@@ -2892,10 +2846,9 @@ struct UnmarkGrayTracer : public JS::CallbackTracer
*
* There is an additional complication for certain kinds of edges that are not
* contained explicitly in the source object itself, such as from a weakmap key
- * to its value, and from an object being watched by a watchpoint to the
- * watchpoint's closure. These "implicit edges" are represented in some other
- * container object, such as the weakmap or the watchpoint itself. In these
- * cases, calling unmark gray on an object won't find all of its children.
+ * to its value. These "implicit edges" are represented in some other
+ * container object, such as the weakmap itself. In these cases, calling unmark
+ * gray on an object won't find all of its children.
*
* Handling these implicit edges has two parts:
* - A special pass enumerating all of the containers that know about the
diff --git a/js/src/gc/Memory.cpp b/js/src/gc/Memory.cpp
index 26da75469..418984057 100644
--- a/js/src/gc/Memory.cpp
+++ b/js/src/gc/Memory.cpp
@@ -17,11 +17,6 @@
#include "jswin.h"
#include <psapi.h>
-#elif defined(SOLARIS)
-
-#include <sys/mman.h>
-#include <unistd.h>
-
#elif defined(XP_UNIX)
#include <algorithm>
@@ -408,86 +403,6 @@ DeallocateMappedContent(void* p, size_t length)
# endif
-#elif defined(SOLARIS)
-
-#ifndef MAP_NOSYNC
-# define MAP_NOSYNC 0
-#endif
-
-void
-InitMemorySubsystem()
-{
- if (pageSize == 0)
- pageSize = allocGranularity = size_t(sysconf(_SC_PAGESIZE));
-}
-
-void*
-MapAlignedPages(size_t size, size_t alignment)
-{
- MOZ_ASSERT(size >= alignment);
- MOZ_ASSERT(size >= allocGranularity);
- MOZ_ASSERT(size % alignment == 0);
- MOZ_ASSERT(size % pageSize == 0);
- MOZ_ASSERT_IF(alignment < allocGranularity, allocGranularity % alignment == 0);
- MOZ_ASSERT_IF(alignment > allocGranularity, alignment % allocGranularity == 0);
-
- int prot = PROT_READ | PROT_WRITE;
- int flags = MAP_PRIVATE | MAP_ANON | MAP_ALIGN | MAP_NOSYNC;
-
- void* p = mmap((caddr_t)alignment, size, prot, flags, -1, 0);
- if (p == MAP_FAILED)
- return nullptr;
- return p;
-}
-
-static void*
-MapAlignedPagesLastDitch(size_t size, size_t alignment)
-{
- return nullptr;
-}
-
-void
-UnmapPages(void* p, size_t size)
-{
- MOZ_ALWAYS_TRUE(0 == munmap((caddr_t)p, size));
-}
-
-bool
-MarkPagesUnused(void* p, size_t size)
-{
- MOZ_ASSERT(OffsetFromAligned(p, pageSize) == 0);
- return true;
-}
-
-bool
-MarkPagesInUse(void* p, size_t size)
-{
- if (!DecommitEnabled())
- return;
-
- MOZ_ASSERT(OffsetFromAligned(p, pageSize) == 0);
-}
-
-size_t
-GetPageFaultCount()
-{
- return 0;
-}
-
-void*
-AllocateMappedContent(int fd, size_t offset, size_t length, size_t alignment)
-{
- // Not implemented.
- return nullptr;
-}
-
-// Deallocate mapped memory for object.
-void
-DeallocateMappedContent(void* p, size_t length)
-{
- // Not implemented.
-}
-
#elif defined(XP_UNIX)
void
@@ -500,8 +415,15 @@ InitMemorySubsystem()
static inline void*
MapMemoryAt(void* desired, size_t length, int prot = PROT_READ | PROT_WRITE,
int flags = MAP_PRIVATE | MAP_ANON, int fd = -1, off_t offset = 0)
+
+// Solaris manages 64-bit address space in a different manner from every other
+// AMD64 operating system, but fortunately the fix is the same one
+// required for every operating system on 64-bit SPARC, Itanium, and ARM.
+// Most people's intuition failed them here and they thought this couldn't
+// possibly be correct on AMD64, but for Solaris/illumos it is.
+
{
-#if defined(__ia64__) || (defined(__sparc64__) && defined(__NetBSD__)) || defined(__aarch64__)
+#if defined(__ia64__) || (defined(__sparc64__) && defined(__NetBSD__)) || defined(__aarch64__) || (defined(__sun) && defined(__x86_64__))
MOZ_ASSERT((0xffff800000000000ULL & (uintptr_t(desired) + length - 1)) == 0);
#endif
void* region = mmap(desired, length, prot, flags, fd, offset);
@@ -551,7 +473,7 @@ MapMemory(size_t length, int prot = PROT_READ | PROT_WRITE,
return nullptr;
}
return region;
-#elif defined(__aarch64__)
+#elif defined(__aarch64__) || (defined(__sun) && defined(__x86_64__))
/*
* There might be similar virtual address issue on arm64 which depends on
* hardware and kernel configurations. But the work around is slightly
@@ -763,7 +685,11 @@ MarkPagesUnused(void* p, size_t size)
return false;
MOZ_ASSERT(OffsetFromAligned(p, pageSize) == 0);
- int result = madvise(p, size, MADV_DONTNEED);
+#ifdef XP_SOLARIS
+ int result = posix_madvise(p, size, POSIX_MADV_DONTNEED);
+#else
+ int result = madvise(p, size, MADV_DONTNEED);
+#endif
return result != -1;
}
diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp
index 55ca5a059..93a0eb6a8 100644
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -210,6 +210,7 @@ js::Nursery::disable()
return;
updateNumChunks(0);
currentEnd_ = 0;
+ position_ = 0;
runtime()->gc.storeBuffer.disable();
}
@@ -530,7 +531,6 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason)
// the nursery is full, look for object groups that are getting promoted
// excessively and try to pretenure them.
maybeStartProfile(ProfileKey::Pretenure);
- uint32_t pretenureCount = 0;
if (promotionRate > 0.8 || reason == JS::gcreason::FULL_STORE_BUFFER) {
JSContext* cx = rt->contextFromMainThread();
for (auto& entry : tenureCounts.entries) {
@@ -539,7 +539,6 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason)
if (group->canPreTenure()) {
AutoCompartment ac(cx, group->compartment());
group->setShouldPreTenure(cx);
- pretenureCount++;
}
}
}
@@ -556,12 +555,6 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason)
minorGcCount_++;
int64_t totalTime = profileTimes_[ProfileKey::Total];
- rt->addTelemetry(JS_TELEMETRY_GC_MINOR_US, totalTime);
- rt->addTelemetry(JS_TELEMETRY_GC_MINOR_REASON, reason);
- if (totalTime > 1000)
- rt->addTelemetry(JS_TELEMETRY_GC_MINOR_REASON_LONG, reason);
- rt->addTelemetry(JS_TELEMETRY_GC_NURSERY_BYTES, sizeOfHeapCommitted());
- rt->addTelemetry(JS_TELEMETRY_GC_PRETENURE_COUNT, pretenureCount);
rt->gc.stats.endNurseryCollection(reason);
TraceMinorGCEnd();
diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h
index 0d215d997..a839a4979 100644
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -245,6 +245,7 @@ class Nursery
// Free space remaining, not counting chunk trailers.
MOZ_ALWAYS_INLINE size_t freeSpace() const {
+ MOZ_ASSERT(isEnabled());
MOZ_ASSERT(currentEnd_ - position_ <= NurseryChunkUsableSize);
return (currentEnd_ - position_) +
(numChunks() - currentChunk_ - 1) * NurseryChunkUsableSize;
diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp
index 93264084b..7d665e8eb 100644
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -14,7 +14,6 @@
#include "jsgc.h"
#include "jsprf.h"
#include "jstypes.h"
-#include "jswatchpoint.h"
#include "builtin/MapObject.h"
#include "frontend/BytecodeCompiler.h"
@@ -478,6 +477,7 @@ js::gc::GCRuntime::bufferGrayRoots()
for (GCZonesIter zone(rt); !zone.done(); zone.next())
MOZ_ASSERT(zone->gcGrayRoots.empty());
+ gcstats::AutoPhase ap(stats, gcstats::PHASE_BUFFER_GRAY_ROOTS);
BufferGrayRootsTracer grayBufferer(rt);
if (JSTraceDataOp op = grayRootTracer.op)
@@ -540,4 +540,3 @@ GCRuntime::resetBufferedGrayRoots() const
for (GCZonesIter zone(rt); !zone.done(); zone.next())
zone->gcGrayRoots.clearAndFree();
}
-
diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp
index 19f9986dd..8a9f4e135 100644
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -34,13 +34,6 @@ using mozilla::MakeRange;
using mozilla::PodArrayZero;
using mozilla::PodZero;
-/*
- * If this fails, then you can either delete this assertion and allow all
- * larger-numbered reasons to pile up in the last telemetry bucket, or switch
- * to GC_REASON_3 and bump the max value.
- */
-JS_STATIC_ASSERT(JS::gcreason::NUM_TELEMETRY_REASONS >= JS::gcreason::NUM_REASONS);
-
const char*
js::gcstats::ExplainInvocationKind(JSGCInvocationKind gckind)
{
@@ -92,7 +85,6 @@ struct PhaseInfo
Phase index;
const char* name;
Phase parent;
- const uint8_t telemetryBucket;
};
// The zeroth entry in the timing arrays is used for phases that have a
@@ -134,78 +126,74 @@ struct DagChildEdge {
*/
static const PhaseInfo phases[] = {
- { PHASE_MUTATOR, "Mutator Running", PHASE_NO_PARENT, 0 },
- { PHASE_GC_BEGIN, "Begin Callback", PHASE_NO_PARENT, 1 },
- { PHASE_WAIT_BACKGROUND_THREAD, "Wait Background Thread", PHASE_NO_PARENT, 2 },
- { PHASE_MARK_DISCARD_CODE, "Mark Discard Code", PHASE_NO_PARENT, 3 },
- { PHASE_RELAZIFY_FUNCTIONS, "Relazify Functions", PHASE_NO_PARENT, 4 },
- { PHASE_PURGE, "Purge", PHASE_NO_PARENT, 5 },
- { PHASE_MARK, "Mark", PHASE_NO_PARENT, 6 },
- { PHASE_UNMARK, "Unmark", PHASE_MARK, 7 },
+ { PHASE_MUTATOR, "Mutator Running", PHASE_NO_PARENT },
+ { PHASE_GC_BEGIN, "Begin Callback", PHASE_NO_PARENT },
+ { PHASE_WAIT_BACKGROUND_THREAD, "Wait Background Thread", PHASE_NO_PARENT },
+ { PHASE_MARK_DISCARD_CODE, "Mark Discard Code", PHASE_NO_PARENT },
+ { PHASE_RELAZIFY_FUNCTIONS, "Relazify Functions", PHASE_NO_PARENT },
+ { PHASE_PURGE, "Purge", PHASE_NO_PARENT },
+ { PHASE_MARK, "Mark", PHASE_NO_PARENT },
+ { PHASE_UNMARK, "Unmark", PHASE_MARK },
/* PHASE_MARK_ROOTS */
- { PHASE_MARK_DELAYED, "Mark Delayed", PHASE_MARK, 8 },
- { PHASE_SWEEP, "Sweep", PHASE_NO_PARENT, 9 },
- { PHASE_SWEEP_MARK, "Mark During Sweeping", PHASE_SWEEP, 10 },
- { PHASE_SWEEP_MARK_TYPES, "Mark Types During Sweeping", PHASE_SWEEP_MARK, 11 },
- { PHASE_SWEEP_MARK_INCOMING_BLACK, "Mark Incoming Black Pointers", PHASE_SWEEP_MARK, 12 },
- { PHASE_SWEEP_MARK_WEAK, "Mark Weak", PHASE_SWEEP_MARK, 13 },
- { PHASE_SWEEP_MARK_INCOMING_GRAY, "Mark Incoming Gray Pointers", PHASE_SWEEP_MARK, 14 },
- { PHASE_SWEEP_MARK_GRAY, "Mark Gray", PHASE_SWEEP_MARK, 15 },
- { PHASE_SWEEP_MARK_GRAY_WEAK, "Mark Gray and Weak", PHASE_SWEEP_MARK, 16 },
- { PHASE_FINALIZE_START, "Finalize Start Callbacks", PHASE_SWEEP, 17 },
- { PHASE_WEAK_ZONEGROUP_CALLBACK, "Per-Slice Weak Callback", PHASE_FINALIZE_START, 57 },
- { PHASE_WEAK_COMPARTMENT_CALLBACK, "Per-Compartment Weak Callback", PHASE_FINALIZE_START, 58 },
- { PHASE_SWEEP_ATOMS, "Sweep Atoms", PHASE_SWEEP, 18 },
- { PHASE_SWEEP_SYMBOL_REGISTRY, "Sweep Symbol Registry", PHASE_SWEEP, 19 },
- { PHASE_SWEEP_COMPARTMENTS, "Sweep Compartments", PHASE_SWEEP, 20 },
- { PHASE_SWEEP_DISCARD_CODE, "Sweep Discard Code", PHASE_SWEEP_COMPARTMENTS, 21 },
- { PHASE_SWEEP_INNER_VIEWS, "Sweep Inner Views", PHASE_SWEEP_COMPARTMENTS, 22 },
- { PHASE_SWEEP_CC_WRAPPER, "Sweep Cross Compartment Wrappers", PHASE_SWEEP_COMPARTMENTS, 23 },
- { PHASE_SWEEP_BASE_SHAPE, "Sweep Base Shapes", PHASE_SWEEP_COMPARTMENTS, 24 },
- { PHASE_SWEEP_INITIAL_SHAPE, "Sweep Initial Shapes", PHASE_SWEEP_COMPARTMENTS, 25 },
- { PHASE_SWEEP_TYPE_OBJECT, "Sweep Type Objects", PHASE_SWEEP_COMPARTMENTS, 26 },
- { PHASE_SWEEP_BREAKPOINT, "Sweep Breakpoints", PHASE_SWEEP_COMPARTMENTS, 27 },
- { PHASE_SWEEP_REGEXP, "Sweep Regexps", PHASE_SWEEP_COMPARTMENTS, 28 },
- { PHASE_SWEEP_MISC, "Sweep Miscellaneous", PHASE_SWEEP_COMPARTMENTS, 29 },
- { PHASE_SWEEP_TYPES, "Sweep type information", PHASE_SWEEP_COMPARTMENTS, 30 },
- { PHASE_SWEEP_TYPES_BEGIN, "Sweep type tables and compilations", PHASE_SWEEP_TYPES, 31 },
- { PHASE_SWEEP_TYPES_END, "Free type arena", PHASE_SWEEP_TYPES, 32 },
- { PHASE_SWEEP_OBJECT, "Sweep Object", PHASE_SWEEP, 33 },
- { PHASE_SWEEP_STRING, "Sweep String", PHASE_SWEEP, 34 },
- { PHASE_SWEEP_SCRIPT, "Sweep Script", PHASE_SWEEP, 35 },
- { PHASE_SWEEP_SCOPE, "Sweep Scope", PHASE_SWEEP, 59 },
- { PHASE_SWEEP_SHAPE, "Sweep Shape", PHASE_SWEEP, 36 },
- { PHASE_SWEEP_JITCODE, "Sweep JIT code", PHASE_SWEEP, 37 },
- { PHASE_FINALIZE_END, "Finalize End Callback", PHASE_SWEEP, 38 },
- { PHASE_DESTROY, "Deallocate", PHASE_SWEEP, 39 },
- { PHASE_COMPACT, "Compact", PHASE_NO_PARENT, 40 },
- { PHASE_COMPACT_MOVE, "Compact Move", PHASE_COMPACT, 41 },
- { PHASE_COMPACT_UPDATE, "Compact Update", PHASE_COMPACT, 42 },
+ { PHASE_MARK_DELAYED, "Mark Delayed", PHASE_MARK },
+ { PHASE_SWEEP, "Sweep", PHASE_NO_PARENT },
+ { PHASE_SWEEP_MARK, "Mark During Sweeping", PHASE_SWEEP },
+ { PHASE_SWEEP_MARK_TYPES, "Mark Types During Sweeping", PHASE_SWEEP_MARK },
+ { PHASE_SWEEP_MARK_INCOMING_BLACK, "Mark Incoming Black Pointers", PHASE_SWEEP_MARK },
+ { PHASE_SWEEP_MARK_WEAK, "Mark Weak", PHASE_SWEEP_MARK },
+ { PHASE_SWEEP_MARK_INCOMING_GRAY, "Mark Incoming Gray Pointers", PHASE_SWEEP_MARK },
+ { PHASE_SWEEP_MARK_GRAY, "Mark Gray", PHASE_SWEEP_MARK },
+ { PHASE_SWEEP_MARK_GRAY_WEAK, "Mark Gray and Weak", PHASE_SWEEP_MARK },
+ { PHASE_FINALIZE_START, "Finalize Start Callbacks", PHASE_SWEEP },
+ { PHASE_WEAK_ZONEGROUP_CALLBACK, "Per-Slice Weak Callback", PHASE_FINALIZE_START },
+ { PHASE_WEAK_COMPARTMENT_CALLBACK, "Per-Compartment Weak Callback", PHASE_FINALIZE_START },
+ { PHASE_SWEEP_ATOMS, "Sweep Atoms", PHASE_SWEEP },
+ { PHASE_SWEEP_SYMBOL_REGISTRY, "Sweep Symbol Registry", PHASE_SWEEP },
+ { PHASE_SWEEP_COMPARTMENTS, "Sweep Compartments", PHASE_SWEEP },
+ { PHASE_SWEEP_DISCARD_CODE, "Sweep Discard Code", PHASE_SWEEP_COMPARTMENTS },
+ { PHASE_SWEEP_INNER_VIEWS, "Sweep Inner Views", PHASE_SWEEP_COMPARTMENTS },
+ { PHASE_SWEEP_CC_WRAPPER, "Sweep Cross Compartment Wrappers", PHASE_SWEEP_COMPARTMENTS },
+ { PHASE_SWEEP_BASE_SHAPE, "Sweep Base Shapes", PHASE_SWEEP_COMPARTMENTS },
+ { PHASE_SWEEP_INITIAL_SHAPE, "Sweep Initial Shapes", PHASE_SWEEP_COMPARTMENTS },
+ { PHASE_SWEEP_TYPE_OBJECT, "Sweep Type Objects", PHASE_SWEEP_COMPARTMENTS },
+ { PHASE_SWEEP_BREAKPOINT, "Sweep Breakpoints", PHASE_SWEEP_COMPARTMENTS },
+ { PHASE_SWEEP_REGEXP, "Sweep Regexps", PHASE_SWEEP_COMPARTMENTS },
+ { PHASE_SWEEP_MISC, "Sweep Miscellaneous", PHASE_SWEEP_COMPARTMENTS },
+ { PHASE_SWEEP_TYPES, "Sweep type information", PHASE_SWEEP_COMPARTMENTS },
+ { PHASE_SWEEP_TYPES_BEGIN, "Sweep type tables and compilations", PHASE_SWEEP_TYPES },
+ { PHASE_SWEEP_TYPES_END, "Free type arena", PHASE_SWEEP_TYPES },
+ { PHASE_SWEEP_OBJECT, "Sweep Object", PHASE_SWEEP },
+ { PHASE_SWEEP_STRING, "Sweep String", PHASE_SWEEP },
+ { PHASE_SWEEP_SCRIPT, "Sweep Script", PHASE_SWEEP },
+ { PHASE_SWEEP_SCOPE, "Sweep Scope", PHASE_SWEEP },
+ { PHASE_SWEEP_SHAPE, "Sweep Shape", PHASE_SWEEP },
+ { PHASE_SWEEP_JITCODE, "Sweep JIT code", PHASE_SWEEP },
+ { PHASE_FINALIZE_END, "Finalize End Callback", PHASE_SWEEP },
+ { PHASE_DESTROY, "Deallocate", PHASE_SWEEP },
+ { PHASE_COMPACT, "Compact", PHASE_NO_PARENT },
+ { PHASE_COMPACT_MOVE, "Compact Move", PHASE_COMPACT },
+ { PHASE_COMPACT_UPDATE, "Compact Update", PHASE_COMPACT },
/* PHASE_MARK_ROOTS */
- { PHASE_COMPACT_UPDATE_CELLS, "Compact Update Cells", PHASE_COMPACT_UPDATE, 43 },
- { PHASE_GC_END, "End Callback", PHASE_NO_PARENT, 44 },
- { PHASE_MINOR_GC, "All Minor GCs", PHASE_NO_PARENT, 45 },
+ { PHASE_COMPACT_UPDATE_CELLS, "Compact Update Cells", PHASE_COMPACT_UPDATE },
+ { PHASE_GC_END, "End Callback", PHASE_NO_PARENT },
+ { PHASE_MINOR_GC, "All Minor GCs", PHASE_NO_PARENT },
/* PHASE_MARK_ROOTS */
- { PHASE_EVICT_NURSERY, "Minor GCs to Evict Nursery", PHASE_NO_PARENT, 46 },
+ { PHASE_EVICT_NURSERY, "Minor GCs to Evict Nursery", PHASE_NO_PARENT },
/* PHASE_MARK_ROOTS */
- { PHASE_TRACE_HEAP, "Trace Heap", PHASE_NO_PARENT, 47 },
+ { PHASE_TRACE_HEAP, "Trace Heap", PHASE_NO_PARENT },
/* PHASE_MARK_ROOTS */
- { PHASE_BARRIER, "Barriers", PHASE_NO_PARENT, 55 },
- { PHASE_UNMARK_GRAY, "Unmark gray", PHASE_BARRIER, 56 },
- { PHASE_MARK_ROOTS, "Mark Roots", PHASE_MULTI_PARENTS, 48 },
- { PHASE_BUFFER_GRAY_ROOTS, "Buffer Gray Roots", PHASE_MARK_ROOTS, 49 },
- { PHASE_MARK_CCWS, "Mark Cross Compartment Wrappers", PHASE_MARK_ROOTS, 50 },
- { PHASE_MARK_STACK, "Mark C and JS stacks", PHASE_MARK_ROOTS, 51 },
- { PHASE_MARK_RUNTIME_DATA, "Mark Runtime-wide Data", PHASE_MARK_ROOTS, 52 },
- { PHASE_MARK_EMBEDDING, "Mark Embedding", PHASE_MARK_ROOTS, 53 },
- { PHASE_MARK_COMPARTMENTS, "Mark Compartments", PHASE_MARK_ROOTS, 54 },
- { PHASE_PURGE_SHAPE_TABLES, "Purge ShapeTables", PHASE_NO_PARENT, 60 },
-
- { PHASE_LIMIT, nullptr, PHASE_NO_PARENT, 60 }
-
- // Current number of telemetryBuckets is 60. If you insert new phases
- // somewhere, start at that number and count up. Do not change any existing
- // numbers.
+ { PHASE_BARRIER, "Barriers", PHASE_NO_PARENT },
+ { PHASE_UNMARK_GRAY, "Unmark gray", PHASE_BARRIER },
+ { PHASE_MARK_ROOTS, "Mark Roots", PHASE_MULTI_PARENTS },
+ { PHASE_BUFFER_GRAY_ROOTS, "Buffer Gray Roots", PHASE_MARK_ROOTS },
+ { PHASE_MARK_CCWS, "Mark Cross Compartment Wrappers", PHASE_MARK_ROOTS },
+ { PHASE_MARK_STACK, "Mark C and JS stacks", PHASE_MARK_ROOTS },
+ { PHASE_MARK_RUNTIME_DATA, "Mark Runtime-wide Data", PHASE_MARK_ROOTS },
+ { PHASE_MARK_EMBEDDING, "Mark Embedding", PHASE_MARK_ROOTS },
+ { PHASE_MARK_COMPARTMENTS, "Mark Compartments", PHASE_MARK_ROOTS },
+ { PHASE_PURGE_SHAPE_TABLES, "Purge ShapeTables", PHASE_NO_PARENT },
+
+ { PHASE_LIMIT, nullptr, PHASE_NO_PARENT }
};
static ExtraPhaseInfo phaseExtra[PHASE_LIMIT] = { { 0, 0 } };
@@ -845,12 +833,6 @@ Statistics::~Statistics()
/* static */ bool
Statistics::initialize()
{
- for (size_t i = 0; i < PHASE_LIMIT; i++) {
- MOZ_ASSERT(phases[i].index == i);
- for (size_t j = 0; j < PHASE_LIMIT; j++)
- MOZ_ASSERT_IF(i != j, phases[i].telemetryBucket != phases[j].telemetryBucket);
- }
-
// Create a static table of descendants for every phase with multiple
// children. This assumes that all descendants come linearly in the
// list, which is reasonable since full dags are not supported; any
@@ -925,32 +907,6 @@ Statistics::getMaxGCPauseSinceClear()
return maxPauseInInterval;
}
-// Sum up the time for a phase, including instances of the phase with different
-// parents.
-static int64_t
-SumPhase(Phase phase, const Statistics::PhaseTimeTable times)
-{
- int64_t sum = 0;
- for (auto i : MakeRange(Statistics::NumTimingArrays))
- sum += times[i][phase];
- return sum;
-}
-
-static Phase
-LongestPhase(const Statistics::PhaseTimeTable times)
-{
- int64_t longestTime = 0;
- Phase longestPhase = PHASE_NONE;
- for (size_t i = 0; i < PHASE_LIMIT; ++i) {
- int64_t phaseTime = SumPhase(Phase(i), times);
- if (phaseTime > longestTime) {
- longestTime = phaseTime;
- longestPhase = Phase(i);
- }
- }
- return longestPhase;
-}
-
void
Statistics::printStats()
{
@@ -985,34 +941,6 @@ Statistics::endGC()
int64_t total, longest;
gcDuration(&total, &longest);
- int64_t sccTotal, sccLongest;
- sccDurations(&sccTotal, &sccLongest);
-
- runtime->addTelemetry(JS_TELEMETRY_GC_IS_ZONE_GC, !zoneStats.isCollectingAllZones());
- runtime->addTelemetry(JS_TELEMETRY_GC_MS, t(total));
- runtime->addTelemetry(JS_TELEMETRY_GC_MAX_PAUSE_MS, t(longest));
- int64_t markTotal = SumPhase(PHASE_MARK, phaseTimes);
- int64_t markRootsTotal = SumPhase(PHASE_MARK_ROOTS, phaseTimes);
- runtime->addTelemetry(JS_TELEMETRY_GC_MARK_MS, t(markTotal));
- runtime->addTelemetry(JS_TELEMETRY_GC_SWEEP_MS, t(phaseTimes[PHASE_DAG_NONE][PHASE_SWEEP]));
- if (runtime->gc.isCompactingGc()) {
- runtime->addTelemetry(JS_TELEMETRY_GC_COMPACT_MS,
- t(phaseTimes[PHASE_DAG_NONE][PHASE_COMPACT]));
- }
- runtime->addTelemetry(JS_TELEMETRY_GC_MARK_ROOTS_MS, t(markRootsTotal));
- runtime->addTelemetry(JS_TELEMETRY_GC_MARK_GRAY_MS, t(phaseTimes[PHASE_DAG_NONE][PHASE_SWEEP_MARK_GRAY]));
- runtime->addTelemetry(JS_TELEMETRY_GC_NON_INCREMENTAL, nonincremental());
- if (nonincremental())
- runtime->addTelemetry(JS_TELEMETRY_GC_NON_INCREMENTAL_REASON, uint32_t(nonincrementalReason_));
- runtime->addTelemetry(JS_TELEMETRY_GC_INCREMENTAL_DISABLED, !runtime->gc.isIncrementalGCAllowed());
- runtime->addTelemetry(JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS, t(sccTotal));
- runtime->addTelemetry(JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS, t(sccLongest));
-
- if (!aborted) {
- double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC);
- runtime->addTelemetry(JS_TELEMETRY_GC_MMU_50, mmu50 * 100);
- }
-
if (fp)
printStats();
@@ -1061,8 +989,6 @@ Statistics::beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind,
return;
}
- runtime->addTelemetry(JS_TELEMETRY_GC_REASON, reason);
-
// Slice callbacks should only fire for the outermost level.
if (gcDepth == 1) {
bool wasFullGC = zoneStats.isCollectingAllZones();
@@ -1082,25 +1008,6 @@ Statistics::endSlice()
slices.back().endFaults = GetPageFaultCount();
slices.back().finalState = runtime->gc.state();
- int64_t sliceTime = slices.back().end - slices.back().start;
- runtime->addTelemetry(JS_TELEMETRY_GC_SLICE_MS, t(sliceTime));
- runtime->addTelemetry(JS_TELEMETRY_GC_RESET, slices.back().wasReset());
- if (slices.back().wasReset())
- runtime->addTelemetry(JS_TELEMETRY_GC_RESET_REASON, uint32_t(slices.back().resetReason));
-
- if (slices.back().budget.isTimeBudget()) {
- int64_t budget_ms = slices.back().budget.timeBudget.budget;
- runtime->addTelemetry(JS_TELEMETRY_GC_BUDGET_MS, budget_ms);
- if (budget_ms == runtime->gc.defaultSliceBudget())
- runtime->addTelemetry(JS_TELEMETRY_GC_ANIMATION_MS, t(sliceTime));
-
- // Record any phase that goes more than 2x over its budget.
- if (sliceTime > 2 * budget_ms * 1000) {
- Phase longest = LongestPhase(slices.back().phaseTimes);
- runtime->addTelemetry(JS_TELEMETRY_GC_SLOW_PHASE, phases[longest].telemetryBucket);
- }
- }
-
sliceCount_++;
}
diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h
index ca1969b2c..08a2810cf 100644
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -10,7 +10,6 @@
#include "mozilla/EnumeratedArray.h"
#include "mozilla/IntegerRange.h"
#include "mozilla/Maybe.h"
-#include "mozilla/PodOperations.h"
#include "jsalloc.h"
#include "jsgc.h"
@@ -112,29 +111,26 @@ enum Stat {
struct ZoneGCStats
{
/* Number of zones collected in this GC. */
- int collectedZoneCount;
+ int collectedZoneCount = 0;
/* Total number of zones in the Runtime at the start of this GC. */
- int zoneCount;
+ int zoneCount = 0;
/* Number of zones swept in this GC. */
- int sweptZoneCount;
+ int sweptZoneCount = 0;
/* Total number of compartments in all zones collected. */
- int collectedCompartmentCount;
+ int collectedCompartmentCount = 0;
/* Total number of compartments in the Runtime at the start of this GC. */
- int compartmentCount;
+ int compartmentCount = 0;
/* Total number of compartments swept by this GC. */
- int sweptCompartmentCount;
+ int sweptCompartmentCount = 0;
bool isCollectingAllZones() const { return collectedZoneCount == zoneCount; }
- ZoneGCStats()
- : collectedZoneCount(0), zoneCount(0), sweptZoneCount(0),
- collectedCompartmentCount(0), compartmentCount(0), sweptCompartmentCount(0)
- {}
+ ZoneGCStats() = default;
};
#define FOR_EACH_GC_PROFILE_TIME(_) \
diff --git a/js/src/gc/Tracer.cpp b/js/src/gc/Tracer.cpp
index 63cd9b08a..3416464dd 100644
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -201,80 +201,15 @@ gc::TraceCycleCollectorChildren(JS::CallbackTracer* trc, Shape* shape)
} while (shape);
}
-// Object groups can point to other object groups via an UnboxedLayout or the
-// the original unboxed group link. There can potentially be deep or cyclic
-// chains of such groups to trace through without going through a thing that
-// participates in cycle collection. These need to be handled iteratively to
-// avoid blowing the stack when running the cycle collector's callback tracer.
-struct ObjectGroupCycleCollectorTracer : public JS::CallbackTracer
-{
- explicit ObjectGroupCycleCollectorTracer(JS::CallbackTracer* innerTracer)
- : JS::CallbackTracer(innerTracer->runtime(), DoNotTraceWeakMaps),
- innerTracer(innerTracer)
- {}
-
- void onChild(const JS::GCCellPtr& thing) override;
-
- JS::CallbackTracer* innerTracer;
- Vector<ObjectGroup*, 4, SystemAllocPolicy> seen, worklist;
-};
-
-void
-ObjectGroupCycleCollectorTracer::onChild(const JS::GCCellPtr& thing)
-{
- if (thing.is<BaseShape>()) {
- // The CC does not care about BaseShapes, and no additional GC things
- // will be reached by following this edge.
- return;
- }
-
- if (thing.is<JSObject>() || thing.is<JSScript>()) {
- // Invoke the inner cycle collector callback on this child. It will not
- // recurse back into TraceChildren.
- innerTracer->onChild(thing);
- return;
- }
-
- if (thing.is<ObjectGroup>()) {
- // If this group is required to be in an ObjectGroup chain, trace it
- // via the provided worklist rather than continuing to recurse.
- ObjectGroup& group = thing.as<ObjectGroup>();
- if (group.maybeUnboxedLayout()) {
- for (size_t i = 0; i < seen.length(); i++) {
- if (seen[i] == &group)
- return;
- }
- if (seen.append(&group) && worklist.append(&group)) {
- return;
- } else {
- // If append fails, keep tracing normally. The worst that will
- // happen is we end up overrecursing.
- }
- }
- }
-
- TraceChildren(this, thing.asCell(), thing.kind());
-}
-
void
gc::TraceCycleCollectorChildren(JS::CallbackTracer* trc, ObjectGroup* group)
{
MOZ_ASSERT(trc->isCallbackTracer());
- // Early return if this group is not required to be in an ObjectGroup chain.
- if (!group->maybeUnboxedLayout())
- return group->traceChildren(trc);
-
- ObjectGroupCycleCollectorTracer groupTracer(trc->asCallbackTracer());
- group->traceChildren(&groupTracer);
-
- while (!groupTracer.worklist.empty()) {
- ObjectGroup* innerGroup = groupTracer.worklist.popCopy();
- innerGroup->traceChildren(&groupTracer);
- }
+ group->traceChildren(trc);
}
-
+
/*** Traced Edge Printer *************************************************************************/
static size_t
diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp
index ed099341c..f0cdde012 100644
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -370,6 +370,21 @@ Zone::fixupAfterMovingGC()
fixupInitialShapeTable();
}
+bool
+Zone::addTypeDescrObject(JSContext* cx, HandleObject obj)
+{
+ // Type descriptor objects are always tenured so we don't need post barriers
+ // on the set.
+ MOZ_ASSERT(!IsInsideNursery(obj));
+
+ if (!typeDescrObjects.put(obj)) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ return true;
+}
+
ZoneList::ZoneList()
: head(nullptr), tail(nullptr)
{}
diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h
index 50d06319d..c8520ed55 100644
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -349,10 +349,17 @@ struct Zone : public JS::shadow::Zone,
// Keep track of all TypeDescr and related objects in this compartment.
// This is used by the GC to trace them all first when compacting, since the
// TypedObject trace hook may access these objects.
- using TypeDescrObjectSet = js::GCHashSet<js::HeapPtr<JSObject*>,
- js::MovableCellHasher<js::HeapPtr<JSObject*>>,
+
+ //
+ // There are no barriers here - the set contains only tenured objects so no
+ // post-barrier is required, and these are weak references so no pre-barrier
+ // is required.
+ using TypeDescrObjectSet = js::GCHashSet<JSObject*,
+ js::MovableCellHasher<JSObject*>,
js::SystemAllocPolicy>;
JS::WeakCache<TypeDescrObjectSet> typeDescrObjects;
+
+ bool addTypeDescrObject(JSContext* cx, HandleObject obj);
// Malloc counter to measure memory pressure for GC scheduling. It runs from
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/asm.js/testSource.js b/js/src/jit-test/tests/asm.js/testSource.js
index b44c52a6b..d7ad42864 100644
--- a/js/src/jit-test/tests/asm.js/testSource.js
+++ b/js/src/jit-test/tests/asm.js/testSource.js
@@ -32,7 +32,7 @@ var f0 = function() {
}
-funcBody1 = funcBody.replace('function f0','function ');
+funcBody1 = funcBody.replace('function f0','function');
assertEq(f0.toString(), funcBody1);
assertEq(f0.toSource(), '(' + funcBody1 + ')');
@@ -48,14 +48,14 @@ assertEq(g.toString(), funcBody2);
assertEq(g.toSource(), '(' + funcBody2 + ')');
f0 = new Function(bodyOnly);
-assertEq(f0.toString(), "function anonymous() {\n" + bodyOnly + "\n}");
-assertEq(f0.toSource(), "(function anonymous() {\n" + bodyOnly + "\n})");
+assertEq(f0.toString(), "function anonymous(\n) {\n" + bodyOnly + "\n}");
+assertEq(f0.toSource(), "(function anonymous(\n) {\n" + bodyOnly + "\n})");
if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
var m = new Function(bodyOnly);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
- assertEq(m.toString(), "function anonymous() {\n" + bodyOnly + "\n}");
- assertEq(m.toSource(), "(function anonymous() {\n" + bodyOnly + "\n})");
+ assertEq(m.toString(), "function anonymous(\n) {\n" + bodyOnly + "\n}");
+ assertEq(m.toSource(), "(function anonymous(\n) {\n" + bodyOnly + "\n})");
}
})();
@@ -91,7 +91,7 @@ f1 = function(glob) {
}
-funcBody1 = funcBody.replace('function f1', 'function ');
+funcBody1 = funcBody.replace('function f1', 'function');
assertEq(f1.toString(), funcBody1);
assertEq(f1.toSource(), '(' + funcBody1 + ')');
@@ -107,14 +107,14 @@ assertEq(g.toString(), funcBody2);
assertEq(g.toSource(), '(' + funcBody2 + ')');
f1 = new Function('glob', bodyOnly);
-assertEq(f1.toString(), "function anonymous(glob) {\n" + bodyOnly + "\n}");
-assertEq(f1.toSource(), "(function anonymous(glob) {\n" + bodyOnly + "\n})");
+assertEq(f1.toString(), "function anonymous(glob\n) {\n" + bodyOnly + "\n}");
+assertEq(f1.toSource(), "(function anonymous(glob\n) {\n" + bodyOnly + "\n})");
if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
var m = new Function('glob', bodyOnly);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
- assertEq(m.toString(), "function anonymous(glob) {\n" + bodyOnly + "\n}");
- assertEq(m.toSource(), "(function anonymous(glob) {\n" + bodyOnly + "\n})");
+ assertEq(m.toString(), "function anonymous(glob\n) {\n" + bodyOnly + "\n}");
+ assertEq(m.toSource(), "(function anonymous(glob\n) {\n" + bodyOnly + "\n})");
}
})();
@@ -144,14 +144,14 @@ var funcBody = 'function f2(glob, ffi) {\n\
assertEq(f2.toString(), funcBody);
assertEq(f2.toSource(), funcBody);
-f2 = function (glob, ffi) {
+f2 = function(glob, ffi) {
"use asm";
function g() {}
return g;
}
-funcBody1 = funcBody.replace('function f2', 'function ');
+funcBody1 = funcBody.replace('function f2', 'function');
assertEq(f2.toString(), funcBody1);
assertEq(f2.toSource(), '(' + funcBody1 + ')');
@@ -167,14 +167,14 @@ assertEq(g.toString(), funcBody2);
assertEq(g.toSource(), '(' + funcBody2 + ')');
f2 = new Function('glob', 'ffi', bodyOnly);
-assertEq(f2.toString(), "function anonymous(glob, ffi) {\n" + bodyOnly + "\n}");
-assertEq(f2.toSource(), "(function anonymous(glob, ffi) {\n" + bodyOnly + "\n})");
+assertEq(f2.toString(), "function anonymous(glob,ffi\n) {\n" + bodyOnly + "\n}");
+assertEq(f2.toSource(), "(function anonymous(glob,ffi\n) {\n" + bodyOnly + "\n})");
if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
var m = new Function('glob', 'ffi', bodyOnly);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
- assertEq(m.toString(), "function anonymous(glob, ffi) {\n" + bodyOnly + "\n}");
- assertEq(m.toSource(), "(function anonymous(glob, ffi) {\n" + bodyOnly + "\n})");
+ assertEq(m.toString(), "function anonymous(glob,ffi\n) {\n" + bodyOnly + "\n}");
+ assertEq(m.toSource(), "(function anonymous(glob,ffi\n) {\n" + bodyOnly + "\n})");
}
})();
@@ -204,14 +204,14 @@ var funcBody = 'function f3(glob, ffi, heap) {\n\
assertEq(f3.toString(), funcBody);
assertEq(f3.toSource(), funcBody);
-f3 = function (glob, ffi, heap) {
+f3 = function(glob, ffi, heap) {
"use asm";
function g() {}
return g;
}
-funcBody1 = funcBody.replace('function f3', 'function ');
+funcBody1 = funcBody.replace('function f3', 'function');
assertEq(f3.toString(), funcBody1);
assertEq(f3.toSource(), '(' + funcBody1 + ')');
@@ -227,14 +227,14 @@ assertEq(g.toString(), funcBody2);
assertEq(g.toSource(), '(' + funcBody2 + ')');
f3 = new Function('glob', 'ffi', 'heap', bodyOnly);
-assertEq(f3.toString(), "function anonymous(glob, ffi, heap) {\n" + bodyOnly + "\n}");
-assertEq(f3.toSource(), "(function anonymous(glob, ffi, heap) {\n" + bodyOnly + "\n})");
+assertEq(f3.toString(), "function anonymous(glob,ffi,heap\n) {\n" + bodyOnly + "\n}");
+assertEq(f3.toSource(), "(function anonymous(glob,ffi,heap\n) {\n" + bodyOnly + "\n})");
if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
var m = new Function('glob', 'ffi', 'heap', bodyOnly);
assertEq(isAsmJSModuleLoadedFromCache(m), true);
- assertEq(m.toString(), "function anonymous(glob, ffi, heap) {\n" + bodyOnly + "\n}");
- assertEq(m.toSource(), "(function anonymous(glob, ffi, heap) {\n" + bodyOnly + "\n})");
+ assertEq(m.toString(), "function anonymous(glob,ffi,heap\n) {\n" + bodyOnly + "\n}");
+ assertEq(m.toSource(), "(function anonymous(glob,ffi,heap\n) {\n" + bodyOnly + "\n})");
}
})();
@@ -243,7 +243,7 @@ if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
(function() {
var funcSource =
- `function (glob, ffi, heap) {
+ `function(glob, ffi, heap) {
"use asm";
function g() {}
return g;
@@ -252,7 +252,7 @@ var funcSource =
var f4 = eval("\"use strict\";\n(" + funcSource + ")");
var expectedToString = funcSource;
-var expectedToSource = '(' + expectedToString + ')'
+var expectedToSource = '(' + expectedToString + ')';
assertEq(f4.toString(), expectedToString);
assertEq(f4.toSource(), expectedToSource);
diff --git a/js/src/jit-test/tests/auto-regress/bug466654.js b/js/src/jit-test/tests/auto-regress/bug466654.js
deleted file mode 100644
index 6c82c425b..000000000
--- a/js/src/jit-test/tests/auto-regress/bug466654.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// |jit-test| error:TypeError
-
-// Binary: cache/js-dbg-32-29add08d84ae-linux
-// Flags: -j
-//
-this.watch('y', /x/g );
-for each (y in ['q', 'q', 'q']) continue;
-gc();
diff --git a/js/src/jit-test/tests/auto-regress/bug516897.js b/js/src/jit-test/tests/auto-regress/bug516897.js
deleted file mode 100644
index e3caf4e6e..000000000
--- a/js/src/jit-test/tests/auto-regress/bug516897.js
+++ /dev/null
@@ -1,6 +0,0 @@
-// Binary: cache/js-dbg-64-38754465ffde-linux
-// Flags:
-//
-this.__defineSetter__("x", gc);
-this.watch("x",function(){return});
-x = 3;
diff --git a/js/src/jit-test/tests/auto-regress/bug537854.js b/js/src/jit-test/tests/auto-regress/bug537854.js
deleted file mode 100644
index 80fb3c14a..000000000
--- a/js/src/jit-test/tests/auto-regress/bug537854.js
+++ /dev/null
@@ -1,4 +0,0 @@
-// Binary: cache/js-dbg-64-9d51f2a931f7-linux
-// Flags:
-//
-({x:function(){}}).watch('x',function(){});
diff --git a/js/src/jit-test/tests/auto-regress/bug560796.js b/js/src/jit-test/tests/auto-regress/bug560796.js
deleted file mode 100644
index 4ab93567e..000000000
--- a/js/src/jit-test/tests/auto-regress/bug560796.js
+++ /dev/null
@@ -1,9 +0,0 @@
-// Binary: cache/js-dbg-64-a6d7a5677b4c-linux
-// Flags:
-//
-this.__defineSetter__("x", function(){})
-this.watch("x", "".localeCompare)
-window = x
-Object.defineProperty(this, "x", ({
- set: window
-}))
diff --git a/js/src/jit-test/tests/auto-regress/bug638735.js b/js/src/jit-test/tests/auto-regress/bug638735.js
index 63071aa7c..c941f5369 100644
--- a/js/src/jit-test/tests/auto-regress/bug638735.js
+++ b/js/src/jit-test/tests/auto-regress/bug638735.js
@@ -4,7 +4,6 @@
var o9 = Function.prototype;
var o13 = Array;
function f5(o) {
-o.watch('p3', function() {});
ox1 = new Proxy(o, {});
}
f5(o9);
diff --git a/js/src/jit-test/tests/auto-regress/bug639413.js b/js/src/jit-test/tests/auto-regress/bug639413.js
deleted file mode 100644
index d8dd58eaf..000000000
--- a/js/src/jit-test/tests/auto-regress/bug639413.js
+++ /dev/null
@@ -1,9 +0,0 @@
-// |jit-test| error:TypeError
-
-// Binary: cache/js-dbg-32-1c8e91b2e3a4-linux
-// Flags:
-//
-a = evalcx("lazy");
-a.watch("x", function() {});
-({}).watch("x", function() {});
-a.__defineGetter__("y", {});
diff --git a/js/src/jit-test/tests/auto-regress/bug698899.js b/js/src/jit-test/tests/auto-regress/bug698899.js
deleted file mode 100644
index 644f45ec2..000000000
--- a/js/src/jit-test/tests/auto-regress/bug698899.js
+++ /dev/null
@@ -1,9 +0,0 @@
-// Binary: cache/js-dbg-32-f951e9151626-linux
-// Flags: -m -n
-//
-o = evalcx("lazy").__proto__
-gc()
-try {
- o.watch()
-} catch (e) {}
-o.constructor()
diff --git a/js/src/jit-test/tests/auto-regress/bug746397.js b/js/src/jit-test/tests/auto-regress/bug746397.js
deleted file mode 100644
index d915ca7bb..000000000
--- a/js/src/jit-test/tests/auto-regress/bug746397.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// |jit-test| error:ReferenceError
-
-// Binary: cache/js-dbg-64-67bf9a4a1f77-linux
-// Flags: --ion-eager
-//
-
-(function () {
- var a = ['x', 'y'];
- obj.watch(a[+("0")], counter);
-})();
diff --git a/js/src/jit-test/tests/auto-regress/bug769192.js b/js/src/jit-test/tests/auto-regress/bug769192.js
deleted file mode 100644
index 531e37912..000000000
--- a/js/src/jit-test/tests/auto-regress/bug769192.js
+++ /dev/null
@@ -1,6 +0,0 @@
-// |jit-test| error:TypeError
-
-// Binary: cache/js-dbg-64-bf8f2961d0cc-linux
-// Flags:
-//
-Object.watch.call(new Uint8ClampedArray, "length", function() {});
diff --git a/js/src/jit-test/tests/baseline/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/baseline/bug843444.js b/js/src/jit-test/tests/baseline/bug843444.js
deleted file mode 100644
index 3a77402ac..000000000
--- a/js/src/jit-test/tests/baseline/bug843444.js
+++ /dev/null
@@ -1,8 +0,0 @@
-gczeal(8, 1)
-function recurse(x) {
- recurse;
- if (x < 20)
- recurse(x + 1);
-};
-this.watch(5, (function () {}))
-recurse(0)
diff --git a/js/src/jit-test/tests/basic/bug510437.js b/js/src/jit-test/tests/basic/bug510437.js
deleted file mode 100644
index 2418b9b11..000000000
--- a/js/src/jit-test/tests/basic/bug510437.js
+++ /dev/null
@@ -1,13 +0,0 @@
-// Don't crash or assert.
-
-var d;
-this.watch("d", eval);
-(function () {
- (eval("\
- (function () {\
- for (let x = 0; x < 2; ++x) {\
- d = x\
- }\
- })\
-"))()
-})()
diff --git a/js/src/jit-test/tests/basic/bug605015.js b/js/src/jit-test/tests/basic/bug605015.js
deleted file mode 100644
index a35f7b6c7..000000000
--- a/js/src/jit-test/tests/basic/bug605015.js
+++ /dev/null
@@ -1,9 +0,0 @@
-// |jit-test| error: TypeError
-// don't assert
-
-print(this.watch("x",
-function() {
- Object.defineProperty(this, "x", ({
- get: (Int8Array)
- }))
-}))(x = /x/)
diff --git a/js/src/jit-test/tests/basic/bug631305.js b/js/src/jit-test/tests/basic/bug631305.js
deleted file mode 100644
index b0cbbbb24..000000000
--- a/js/src/jit-test/tests/basic/bug631305.js
+++ /dev/null
@@ -1,9 +0,0 @@
-var n = 0;
-var a = [];
-for (var i = 0; i < 20; i++)
- a[i] = {};
-a[18].watch("p", function () { n++; });
-delete a[18].p;
-for (var i = 0; i < 20; i++)
- a[i].p = 0;
-assertEq(n, 1);
diff --git a/js/src/jit-test/tests/basic/bug662562.js b/js/src/jit-test/tests/basic/bug662562.js
deleted file mode 100644
index 45b48589a..000000000
--- a/js/src/jit-test/tests/basic/bug662562.js
+++ /dev/null
@@ -1,6 +0,0 @@
-// |jit-test| error: TypeError
-function f(o) {
- o.watch("x", this);
-}
-var c = evalcx("");
-f(c);
diff --git a/js/src/jit-test/tests/basic/bug690292.js b/js/src/jit-test/tests/basic/bug690292.js
deleted file mode 100644
index 43ab56dd7..000000000
--- a/js/src/jit-test/tests/basic/bug690292.js
+++ /dev/null
@@ -1,12 +0,0 @@
-
-done = false;
-try {
- function x() {}
- print(this.watch("d", Object.create))
- var d = {}
-} catch (e) {}
-try {
- eval("d = ''")
- done = true;
-} catch (e) {}
-assertEq(done, false);
diff --git a/js/src/jit-test/tests/basic/bug696748.js b/js/src/jit-test/tests/basic/bug696748.js
index fe171f976..33fb4d52f 100644
--- a/js/src/jit-test/tests/basic/bug696748.js
+++ b/js/src/jit-test/tests/basic/bug696748.js
@@ -1,6 +1,3 @@
-try {
-this.watch("b", "".substring);
-} catch(exc1) {}
eval("\
var URI = '';\
test();\
diff --git a/js/src/jit-test/tests/basic/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/bug831846.js b/js/src/jit-test/tests/basic/bug831846.js
deleted file mode 100644
index 30bb3aa86..000000000
--- a/js/src/jit-test/tests/basic/bug831846.js
+++ /dev/null
@@ -1,3 +0,0 @@
-// |jit-test| error:TypeError
-
-evalcx('').watch("", /()/);
diff --git a/js/src/jit-test/tests/basic/function-tosource-bug779694.js b/js/src/jit-test/tests/basic/function-tosource-bug779694.js
index 3893d3ff2..782b2594b 100644
--- a/js/src/jit-test/tests/basic/function-tosource-bug779694.js
+++ b/js/src/jit-test/tests/basic/function-tosource-bug779694.js
@@ -5,4 +5,4 @@ for (var i=0; i<400; ++i) {
x += String.fromCharCode(i * 289);
}
var s = "'" + x + "'";
-assertEq(Function("evt", s).toString(), "function anonymous(evt) {\n" + s + "\n}");
+assertEq(Function("evt", s).toString(), "function anonymous(evt\n) {\n" + s + "\n}");
diff --git a/js/src/jit-test/tests/basic/function-tosource-constructor.js b/js/src/jit-test/tests/basic/function-tosource-constructor.js
index e1d144364..9a8961fe2 100644
--- a/js/src/jit-test/tests/basic/function-tosource-constructor.js
+++ b/js/src/jit-test/tests/basic/function-tosource-constructor.js
@@ -1,14 +1,14 @@
var f = Function("a", "b", "return a + b;");
-assertEq(f.toString(), "function anonymous(a, b) {\nreturn a + b;\n}");
-assertEq(f.toSource(), "(function anonymous(a, b) {\nreturn a + b;\n})");
+assertEq(f.toString(), "function anonymous(a,b\n) {\nreturn a + b;\n}");
+assertEq(f.toSource(), "(function anonymous(a,b\n) {\nreturn a + b;\n})");
assertEq(decompileFunction(f), f.toString());
f = Function("a", "...rest", "return rest[42] + b;");
-assertEq(f.toString(), "function anonymous(a, ...rest) {\nreturn rest[42] + b;\n}");
-assertEq(f.toSource(), "(function anonymous(a, ...rest) {\nreturn rest[42] + b;\n})")
+assertEq(f.toString(), "function anonymous(a,...rest\n) {\nreturn rest[42] + b;\n}");
+assertEq(f.toSource(), "(function anonymous(a,...rest\n) {\nreturn rest[42] + b;\n})")
assertEq(decompileFunction(f), f.toString());
f = Function("");
-assertEq(f.toString(), "function anonymous() {\n\n}");
+assertEq(f.toString(), "function anonymous(\n) {\n\n}");
f = Function("", "(abc)");
-assertEq(f.toString(), "function anonymous() {\n(abc)\n}");
-f = Function("", "return function (a, b) a + b;")();
-assertEq(f.toString(), "function (a, b) a + b");
+assertEq(f.toString(), "function anonymous(\n) {\n(abc)\n}");
+f = Function("", "return function (a,b) a + b;")();
+assertEq(f.toString(), "function (a,b) a + b");
diff --git a/js/src/jit-test/tests/basic/function-tosource-getset.js b/js/src/jit-test/tests/basic/function-tosource-getset.js
index 36c6d010e..1804d38f2 100644
--- a/js/src/jit-test/tests/basic/function-tosource-getset.js
+++ b/js/src/jit-test/tests/basic/function-tosource-getset.js
@@ -1,7 +1,7 @@
var o = {get prop() a + b, set prop(x) a + b};
var prop = Object.getOwnPropertyDescriptor(o, "prop");
-assertEq(prop.get.toString(), "function get prop() a + b");
-assertEq(prop.get.toSource(), "(function get prop() a + b)");
-assertEq(prop.set.toString(), "function set prop(x) a + b");
-assertEq(prop.set.toSource(), "(function set prop(x) a + b)");
-assertEq(o.toSource(), "({get prop () a + b, set prop (x) a + b})");
+assertEq(prop.get.toString(), "get prop() { a + b; }");
+assertEq(prop.get.toSource(), "get prop() { a + b; }");
+assertEq(prop.set.toString(), "set prop(x) { a + b; }");
+assertEq(prop.set.toSource(), "set prop(x) { a + b; }");
+assertEq(o.toSource(), "({get prop() { a + b; }, set prop(x) { a + b; }})");
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/testAssigningWatchedDeletedProperty.js b/js/src/jit-test/tests/basic/testAssigningWatchedDeletedProperty.js
deleted file mode 100644
index c22eabed0..000000000
--- a/js/src/jit-test/tests/basic/testAssigningWatchedDeletedProperty.js
+++ /dev/null
@@ -1,7 +0,0 @@
-var o = {};
-o.watch("p", function() { });
-
-for (var i = 0; i < 10; i++) {
- o.p = 123;
- delete o.p;
-}
diff --git a/js/src/jit-test/tests/basic/testBug566556.js b/js/src/jit-test/tests/basic/testBug566556.js
deleted file mode 100644
index 244be57d2..000000000
--- a/js/src/jit-test/tests/basic/testBug566556.js
+++ /dev/null
@@ -1,9 +0,0 @@
-var msg = "";
-try {
- this.__defineSetter__('x', Object.create);
- this.watch('x', function() {});
- x = 3;
-} catch (e) {
- msg = e.toString();
-}
-assertEq(msg, "TypeError: undefined is not an object or null");
diff --git a/js/src/jit-test/tests/basic/testBug578044.js b/js/src/jit-test/tests/basic/testBug578044.js
deleted file mode 100644
index c5b811dc9..000000000
--- a/js/src/jit-test/tests/basic/testBug578044.js
+++ /dev/null
@@ -1,13 +0,0 @@
-this.watch("x", Object.create)
-try {
- (function() {
- this.__defineGetter__("x",
- function() {
- return this
- })
- })()
-} catch(e) {}
-Object.defineProperty(x, "x", ({
- set: Uint16Array
-}))
-
diff --git a/js/src/jit-test/tests/basic/testBug584650.js b/js/src/jit-test/tests/basic/testBug584650.js
deleted file mode 100644
index b6c9d8ab7..000000000
--- a/js/src/jit-test/tests/basic/testBug584650.js
+++ /dev/null
@@ -1,9 +0,0 @@
-if (typeof gczeal != "function")
- gczeal = function() {}
-
-// don't crash
-x = (evalcx('lazy'))
-x.watch("", function () {})
-gczeal(1)
-for (w in x) {}
-
diff --git a/js/src/jit-test/tests/basic/testBug780288-1.js b/js/src/jit-test/tests/basic/testBug780288-1.js
deleted file mode 100644
index 90746a04a..000000000
--- a/js/src/jit-test/tests/basic/testBug780288-1.js
+++ /dev/null
@@ -1,20 +0,0 @@
-s = newGlobal()
-try {
- evalcx("\
- Object.defineProperty(this,\"i\",{enumerable:true,get:function(){t}});\
- for each(y in this)true\
- ", s)
-} catch (e) {}
-try {
- evalcx("\
- for(z=0,(7).watch(\"\",eval);;g){\
- if(z=1){({t:function(){}})\
- }\
- ", s)
-} catch (e) {}
-try {
- evalcx("\
- Object.defineProperty(this,\"g2\",{get:function(){return this}});\
- g2.y()\
- ", s)
-} catch (e) {}
diff --git a/js/src/jit-test/tests/basic/testBug780288-2.js b/js/src/jit-test/tests/basic/testBug780288-2.js
deleted file mode 100644
index 8c4c1737c..000000000
--- a/js/src/jit-test/tests/basic/testBug780288-2.js
+++ /dev/null
@@ -1,20 +0,0 @@
-s = newGlobal()
-try {
- evalcx("\
- Object.defineProperty(this,\"i\",{enumerable:true,get:function(){t}});\
- for each(y in this)true\
- ", s)
-} catch (e) {}
-try {
- evalcx("\
- for(z=0,(7).watch(\"\",eval);;g){\
- if(z=1){({t:function(){}})\
- }\
- ", s)
-} catch (e) {}
-try {
- evalcx("\
- Object.defineProperty(this,\"g2\",{get:function(){return this}});\
- g2.y(\"\")\
- ", s)
-} catch (e) {}
diff --git a/js/src/jit-test/tests/basic/testEvalCalledFromWatchOverSetter.js b/js/src/jit-test/tests/basic/testEvalCalledFromWatchOverSetter.js
deleted file mode 100644
index bcd60fd80..000000000
--- a/js/src/jit-test/tests/basic/testEvalCalledFromWatchOverSetter.js
+++ /dev/null
@@ -1,3 +0,0 @@
-this.__defineSetter__("x", function(){});
-this.watch("x", eval);
-x = 0;
diff --git a/js/src/jit-test/tests/basic/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/testLet.js b/js/src/jit-test/tests/basic/testLet.js
index 263c3eb8a..9a2f39197 100644
--- a/js/src/jit-test/tests/basic/testLet.js
+++ b/js/src/jit-test/tests/basic/testLet.js
@@ -9,7 +9,7 @@ function test(str, arg, result)
var fun = new Function('x', str);
var got = fun.toSource();
- var expect = '(function anonymous(x) {\n' + str + '\n})';
+ var expect = '(function anonymous(x\n) {\n' + str + '\n})';
if (got !== expect) {
print("GOT: " + got);
print("EXPECT: " + expect);
diff --git a/js/src/jit-test/tests/basic/testNonStubGetter.js b/js/src/jit-test/tests/basic/testNonStubGetter.js
deleted file mode 100644
index 58a698eb4..000000000
--- a/js/src/jit-test/tests/basic/testNonStubGetter.js
+++ /dev/null
@@ -1,7 +0,0 @@
-function testNonStubGetter() {
- { let [] = []; (this.watch("x", function(p, o, n) { return /a/g.exec(p, o, n); })); };
- (function () { (eval("(function(){for each (x in [1, 2, 2]);});"))(); })();
- this.unwatch("x");
- return "ok";
-}
-assertEq(testNonStubGetter(), "ok");
diff --git a/js/src/jit-test/tests/basic/testSettingWatchPointOnReadOnlyProp.js b/js/src/jit-test/tests/basic/testSettingWatchPointOnReadOnlyProp.js
deleted file mode 100644
index 78c281f05..000000000
--- a/js/src/jit-test/tests/basic/testSettingWatchPointOnReadOnlyProp.js
+++ /dev/null
@@ -1,7 +0,0 @@
-for (var i = 0; i < 5; ++i) {
- var o = {}
- Object.defineProperty(o, 'x', { value:"cow", writable:false });
- var r = o.watch('x', function() {});
- assertEq(r, undefined);
- o.x = 4;
-}
diff --git a/js/src/jit-test/tests/basic/testTrueShiftTrue.js b/js/src/jit-test/tests/basic/testTrueShiftTrue.js
deleted file mode 100644
index 44c1290d8..000000000
--- a/js/src/jit-test/tests/basic/testTrueShiftTrue.js
+++ /dev/null
@@ -1,16 +0,0 @@
-// Test no assert or crash from outer recorders (bug 465145)
-function testBug465145() {
- this.__defineSetter__("x", function(){});
- this.watch("x", function(){});
- y = this;
- for (var z = 0; z < 2; ++z) { x = y };
- this.__defineSetter__("x", function(){});
- for (var z = 0; z < 2; ++z) { x = y };
-}
-
-function testTrueShiftTrue() {
- var a = new Array(5);
- for (var i=0;i<5;++i) a[i] = "" + (true << true);
- return a.join(",");
-}
-assertEq(testTrueShiftTrue(), "2,2,2,2,2");
diff --git a/js/src/jit-test/tests/basic/testWatchRecursion.js b/js/src/jit-test/tests/basic/testWatchRecursion.js
deleted file mode 100644
index e5d5877df..000000000
--- a/js/src/jit-test/tests/basic/testWatchRecursion.js
+++ /dev/null
@@ -1,63 +0,0 @@
-// Test that the watch handler is not called recursively for the same object
-// and property.
-(function() {
- var obj1 = {}, obj2 = {};
- var handler_entry_count = 0;
- var handler_exit_count = 0;
-
- obj1.watch('x', handler);
- obj1.watch('y', handler);
- obj2.watch('x', handler);
- obj1.x = 1;
- assertEq(handler_entry_count, 3);
- assertEq(handler_exit_count, 3);
-
- function handler(id) {
- handler_entry_count++;
- assertEq(handler_exit_count, 0);
- switch (true) {
- case this === obj1 && id === "x":
- assertEq(handler_entry_count, 1);
- obj2.x = 3;
- assertEq(handler_exit_count, 2);
- break;
- case this === obj2 && id === "x":
- assertEq(handler_entry_count, 2);
- obj1.y = 4;
- assertEq(handler_exit_count, 1);
- break;
- default:
- assertEq(this, obj1);
- assertEq(id, "y");
- assertEq(handler_entry_count, 3);
-
- // We expect no more watch handler invocations
- obj1.x = 5;
- obj1.y = 6;
- obj2.x = 7;
- assertEq(handler_exit_count, 0);
- break;
- }
- ++handler_exit_count;
- assertEq(handler_entry_count, 3);
- }
-})();
-
-
-// Test that run-away recursion in watch handlers is properly handled.
-(function() {
- var obj = {};
- var i = 0;
- try {
- handler();
- throw new Error("Unreachable");
- } catch(e) {
- assertEq(e instanceof InternalError, true);
- }
-
- function handler() {
- var prop = "a" + ++i;
- obj.watch(prop, handler);
- obj[prop] = 2;
- }
-})();
diff --git a/js/src/jit-test/tests/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/ctypes/function-definition.js b/js/src/jit-test/tests/ctypes/function-definition.js
index 4df317a09..5882ba889 100644
--- a/js/src/jit-test/tests/ctypes/function-definition.js
+++ b/js/src/jit-test/tests/ctypes/function-definition.js
@@ -27,7 +27,7 @@ function test() {
let lib;
try {
- lib = ctypes.open(ctypes.libraryName("c"));
+ lib = ctypes.open(ctypes.libraryName("m"));
} catch (e) {
}
if (!lib)
diff --git a/js/src/jit-test/tests/debug/Script-gc-02.js b/js/src/jit-test/tests/debug/Script-gc-02.js
index 04dd4b220..9689a6ebe 100644
--- a/js/src/jit-test/tests/debug/Script-gc-02.js
+++ b/js/src/jit-test/tests/debug/Script-gc-02.js
@@ -10,5 +10,5 @@ assertEq(arr.length, 10);
gc();
for (var i = 0; i < arr.length; i++)
- assertEq(arr[i].lineCount, 3);
+ assertEq(arr[i].lineCount, 4);
diff --git a/js/src/jit-test/tests/debug/Script-gc-03.js b/js/src/jit-test/tests/debug/Script-gc-03.js
index 30c3e8dbc..5ecb4556f 100644
--- a/js/src/jit-test/tests/debug/Script-gc-03.js
+++ b/js/src/jit-test/tests/debug/Script-gc-03.js
@@ -10,6 +10,6 @@ assertEq(arr.length, 100);
gc(g);
for (var i = 0; i < arr.length; i++)
- assertEq(arr[i].lineCount, 3);
+ assertEq(arr[i].lineCount, 4);
gc();
diff --git a/js/src/jit-test/tests/debug/Script-sourceStart-04.js b/js/src/jit-test/tests/debug/Script-sourceStart-04.js
index 2aa382b7b..4546818e4 100644
--- a/js/src/jit-test/tests/debug/Script-sourceStart-04.js
+++ b/js/src/jit-test/tests/debug/Script-sourceStart-04.js
@@ -20,6 +20,6 @@ function test(string, range) {
}
test("eval('2 * 3')", [0, 5]);
-test("new Function('2 * 3')", [0, 12]);
-test("new Function('x', 'x * x')", [0, 13]);
+test("new Function('2 * 3')", [0, 31]);
+test("new Function('x', 'x * x')", [0, 32]);
assertEq(count, 6);
diff --git a/js/src/jit-test/tests/debug/Source-text-02.js b/js/src/jit-test/tests/debug/Source-text-02.js
index 64cfce92a..46e76015e 100644
--- a/js/src/jit-test/tests/debug/Source-text-02.js
+++ b/js/src/jit-test/tests/debug/Source-text-02.js
@@ -3,6 +3,7 @@
let g = newGlobal();
let dbg = new Debugger(g);
+var text;
var count = 0;
dbg.onNewScript = function (script) {
++count;
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/gc/bug-900405.js b/js/src/jit-test/tests/gc/bug-900405.js
deleted file mode 100644
index eeec6f25f..000000000
--- a/js/src/jit-test/tests/gc/bug-900405.js
+++ /dev/null
@@ -1,3 +0,0 @@
-(function() {
- [{ "9": [] }.watch([], function(){})]
-})()
diff --git a/js/src/jit-test/tests/gc/bug-913261.js b/js/src/jit-test/tests/gc/bug-913261.js
deleted file mode 100644
index 43066053f..000000000
--- a/js/src/jit-test/tests/gc/bug-913261.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// |jit-test| error: InternalError: too much recursion
-(function f() {
- "".watch(2, function() {});
- f();
-})()
diff --git a/js/src/jit-test/tests/gc/bug-986864.js b/js/src/jit-test/tests/gc/bug-986864.js
deleted file mode 100644
index abb8de6b2..000000000
--- a/js/src/jit-test/tests/gc/bug-986864.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// |jit-test| slow
-function x() {}
-for (var j = 0; j < 9999; ++j) {
- (function() {
- x += x.watch("endsWith", ArrayBuffer);
- return 0 >> Function(x)
- })()
-}
diff --git a/js/src/jit-test/tests/ion/bug1063182.js b/js/src/jit-test/tests/ion/bug1063182.js
deleted file mode 100644
index 9cda48099..000000000
--- a/js/src/jit-test/tests/ion/bug1063182.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// |jit-test| error: ReferenceError
-
-eval("(function() { " + "\
-var o = {};\
-o.watch('p', function() { });\
-for (var i = 0; i < 10; \u5ede ++)\
- o.p = 123;\
-" + " })();");
diff --git a/js/src/jit-test/tests/ion/bug1493900-1.js b/js/src/jit-test/tests/ion/bug1493900-1.js
new file mode 100644
index 000000000..643c1943d
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1493900-1.js
@@ -0,0 +1,17 @@
+function f() {
+ var objs = [];
+ for (var i = 0; i < 100; i++) {
+ objs[i] = {};
+ }
+ var o = objs[0];
+ var a = new Float64Array(1024);
+ function g(a, b) {
+ let p = b;
+ for (; p.x < 0; p = p.x) {
+ while (p === p) {}
+ }
+ for (var i = 0; i < 10000; ++i) {}
+ }
+ g(a, o);
+}
+f();
diff --git a/js/src/jit-test/tests/ion/bug1493900-2.js b/js/src/jit-test/tests/ion/bug1493900-2.js
new file mode 100644
index 000000000..7e7f5fdec
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1493900-2.js
@@ -0,0 +1,7 @@
+function f(a, b) {
+ for (; b.x < 0; b = b.x) {
+ while (b === b) {};
+ }
+ for (var i = 0; i < 99999; ++i) {}
+}
+f(0, 0);
diff --git a/js/src/jit-test/tests/ion/bug772901.js b/js/src/jit-test/tests/ion/bug772901.js
index eb71f6afb..164afd151 100644
--- a/js/src/jit-test/tests/ion/bug772901.js
+++ b/js/src/jit-test/tests/ion/bug772901.js
@@ -4,4 +4,4 @@ function f(x) {
delete ((x)++);
arguments[0] !== undefined;
}
-f(1, x = [f.ArrayBuffer,unwatch.Int32Array], this, this, this) ;
+f(1, x = [f.ArrayBuffer, undefined], this, this, this) ;
diff --git a/js/src/jit-test/tests/ion/bug774257-1.js b/js/src/jit-test/tests/ion/bug774257-1.js
deleted file mode 100644
index 9c998a028..000000000
--- a/js/src/jit-test/tests/ion/bug774257-1.js
+++ /dev/null
@@ -1,8 +0,0 @@
-Object.defineProperty(Object.prototype, 'x', {
- set: function() { evalcx('lazy'); }
-});
-var obj = {};
-obj.watch("x", function (id, oldval, newval) {});
-for (var str in 'A') {
- obj.x = 1;
-}
diff --git a/js/src/jit-test/tests/ion/bug774257-2.js b/js/src/jit-test/tests/ion/bug774257-2.js
deleted file mode 100644
index b31043b08..000000000
--- a/js/src/jit-test/tests/ion/bug774257-2.js
+++ /dev/null
@@ -1,10 +0,0 @@
-Object.defineProperty(Object.prototype, 'x', {
- set: function() { evalcx('lazy'); }
-});
-var obj = {};
-var prot = {};
-obj.__proto__ = prot;
-obj.watch("x", function (id, oldval, newval) {});
-for (var str in 'A') {
- obj.x = 1;
-}
diff --git a/js/src/jit-test/tests/ion/bug779631.js b/js/src/jit-test/tests/ion/bug779631.js
deleted file mode 100644
index 087aa01ac..000000000
--- a/js/src/jit-test/tests/ion/bug779631.js
+++ /dev/null
@@ -1,9 +0,0 @@
-var flag = 0;
-var a = {};
-Object.defineProperty(a, "value", {set: function(x) {}});
-a.watch("value", function(){flag++;});
-
-for(var i = 0; i < 100; i++) {
- a.value = i;
- assertEq(flag, i+1);
-}
diff --git a/js/src/jit-test/tests/ion/bug783590.js b/js/src/jit-test/tests/ion/bug783590.js
index d48cb609e..9d277e02c 100644
--- a/js/src/jit-test/tests/ion/bug783590.js
+++ b/js/src/jit-test/tests/ion/bug783590.js
@@ -7,7 +7,6 @@ Object.defineProperty(arr, 0, {
glob.__proto__;
})
});
-this.watch("s", function() {});
try {
arr.pop();
} catch (e) {}
diff --git a/js/src/jit-test/tests/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/jaeger/bug550665.js b/js/src/jit-test/tests/jaeger/bug550665.js
deleted file mode 100644
index 816888b5e..000000000
--- a/js/src/jit-test/tests/jaeger/bug550665.js
+++ /dev/null
@@ -1,8 +0,0 @@
-(function () {
- var a;
- eval("for(w in ((function(x,y){b:0})())) ;");
-})();
-
-this.__defineSetter__("l", function() { gc() });
-this.watch("l", function(x) { yield {} });
-l = true;
diff --git a/js/src/jit-test/tests/jaeger/bug557063.js b/js/src/jit-test/tests/jaeger/bug557063.js
deleted file mode 100644
index ea77fd2c8..000000000
--- a/js/src/jit-test/tests/jaeger/bug557063.js
+++ /dev/null
@@ -1,7 +0,0 @@
-(function() {
- for (a = 0; a < 2; a++)
- ''.watch("", function() {})
-})()
-
-/* Don't crash or assert. */
-
diff --git a/js/src/jit-test/tests/jaeger/bug588338.js b/js/src/jit-test/tests/jaeger/bug588338.js
index 457be238a..c400d7687 100644
--- a/js/src/jit-test/tests/jaeger/bug588338.js
+++ b/js/src/jit-test/tests/jaeger/bug588338.js
@@ -9,7 +9,6 @@ function f() {
}
}
})(/x/)))
-for (z = 0; z < 100; x.unwatch(), z++)
for (e in [0]) {
gczeal(2)
} ( [1,2,3])("")
diff --git a/js/src/jit-test/tests/jaeger/bug625438.js b/js/src/jit-test/tests/jaeger/bug625438.js
deleted file mode 100644
index 32586d8ab..000000000
--- a/js/src/jit-test/tests/jaeger/bug625438.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// vim: set ts=8 sts=4 et sw=4 tw=99:
-
-var count = 0;
-this.watch("x", function() {
- count++;
-});
-for(var i=0; i<10; i++) {
- x = 2;
-}
-assertEq(count, 10);
diff --git a/js/src/jit-test/tests/jaeger/bug630366.js b/js/src/jit-test/tests/jaeger/bug630366.js
deleted file mode 100644
index acac8d3ef..000000000
--- a/js/src/jit-test/tests/jaeger/bug630366.js
+++ /dev/null
@@ -1,7 +0,0 @@
-var o = {};
-for(var i=0; i<5; i++) {
- o.p = 2;
- o.watch("p", function() { });
- o.p = 2;
- delete o.p;
-}
diff --git a/js/src/jit-test/tests/jaeger/recompile/bug641225.js b/js/src/jit-test/tests/jaeger/recompile/bug641225.js
index a6e3a86c7..7b7978197 100644
--- a/js/src/jit-test/tests/jaeger/recompile/bug641225.js
+++ b/js/src/jit-test/tests/jaeger/recompile/bug641225.js
@@ -118,7 +118,6 @@ for(var o2 in f5) {
f2(o5);
f2(o5);
f0(o3);
- o9.watch('p3', function() {});
o8[o8] = o8;
f0(o5);
f1(o6);
diff --git a/js/src/jit-test/tests/latin1/assorted.js b/js/src/jit-test/tests/latin1/assorted.js
index cef79cb96..1389a1ada 100644
--- a/js/src/jit-test/tests/latin1/assorted.js
+++ b/js/src/jit-test/tests/latin1/assorted.js
@@ -12,18 +12,18 @@ var o = {};
Object.defineProperty(o, "prop", {get: function() { return 1; },
set: function() { return 2; },
enumerable: true, configurable: true});
-assertEq(o.toSource(), "({get prop () { return 1; }, set prop () { return 2; }})");
+assertEq(o.toSource(), "({get prop() { return 1; }, set prop() { return 2; }})");
// obj.toSource TwoByte
Object.defineProperty(o, "prop", {get: function() { return "\u1200"; },
set: function() { return "\u1200"; },
enumerable: true});
-assertEq(o.toSource(), '({get prop () { return "\\u1200"; }, set prop () { return "\\u1200"; }})');
+assertEq(o.toSource(), '({get prop() { return "\\u1200"; }, set prop() { return "\\u1200"; }})');
var ff = function() { return 10; };
ff.toSource = function() { return "((11))"; }
Object.defineProperty(o, "prop", {get: ff, set: ff, enumerable: true});
-assertEq(o.toSource(), "({prop:((11)), prop:((11))})");
+assertEq(o.toSource(), "({get prop(11), set prop(11)})");
// XDR
load(libdir + 'bytecode-cache.js');
diff --git a/js/src/jit-test/tests/latin1/function.js b/js/src/jit-test/tests/latin1/function.js
index a0dedf251..07a76733a 100644
--- a/js/src/jit-test/tests/latin1/function.js
+++ b/js/src/jit-test/tests/latin1/function.js
@@ -6,11 +6,11 @@ function test() {
var f = Function(arg1TwoByte, arg2Latin1, bodyLatin1);
assertEq(f(10, 20), 60);
- assertEq(f.toSource().includes("arg1\u1200, arg2"), true);
+ assertEq(f.toSource().includes("arg1\u1200,arg2"), true);
var bodyTwoByte = "return arg1\u1200 + arg2;";
f = Function(arg1TwoByte, arg2Latin1, bodyTwoByte);
assertEq(f(30, 40), 70);
- assertEq(f.toSource().includes("arg1\u1200, arg2"), true);
+ assertEq(f.toSource().includes("arg1\u1200,arg2"), true);
}
test();
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/pic/fuzz1.js b/js/src/jit-test/tests/pic/fuzz1.js
deleted file mode 100644
index 2481a1314..000000000
--- a/js/src/jit-test/tests/pic/fuzz1.js
+++ /dev/null
@@ -1,4 +0,0 @@
-(function() {
- for (a = 0; a < 2; a++)
- ''.watch("", function() {})
-})()
diff --git a/js/src/jit-test/tests/pic/fuzz3.js b/js/src/jit-test/tests/pic/fuzz3.js
deleted file mode 100644
index 17613b6f5..000000000
--- a/js/src/jit-test/tests/pic/fuzz3.js
+++ /dev/null
@@ -1,3 +0,0 @@
-for each(let w in [[], 0, [], 0]) {
- w.unwatch()
-}
diff --git a/js/src/jit-test/tests/pic/watch1.js b/js/src/jit-test/tests/pic/watch1.js
deleted file mode 100644
index 09d6347bf..000000000
--- a/js/src/jit-test/tests/pic/watch1.js
+++ /dev/null
@@ -1,7 +0,0 @@
-// assignments to watched objects must not be cached
-var obj = {x: 0};
-var hits = 0;
-obj.watch("x", function (id, oldval, newval) { hits++; return newval; });
-for (var i = 0; i < 10; i++)
- obj.x = i;
-assertEq(hits, 10);
diff --git a/js/src/jit-test/tests/pic/watch1a.js b/js/src/jit-test/tests/pic/watch1a.js
deleted file mode 100644
index 4b404f507..000000000
--- a/js/src/jit-test/tests/pic/watch1a.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// assignments to watched objects must not be traced
-var hits = 0;
-function counter(id, oldval, newval) {
- hits++;
- return newval;
-}
-
-(function () {
- var obj = {x: 0, y: 0};
- var a = ['x', 'y'];
- obj.watch('z', counter);
- for (var i = 0; i < 14; i++) {
- obj.watch(a[+(i > 8)], counter);
- obj.y = i;
- }
-})();
-assertEq(hits, 5);
diff --git a/js/src/jit-test/tests/pic/watch2.js b/js/src/jit-test/tests/pic/watch2.js
deleted file mode 100644
index fb3e29617..000000000
--- a/js/src/jit-test/tests/pic/watch2.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// assignments to watched properties via ++ must not be cached
-var obj = {x: 0};
-var hits = 0;
-obj.watch("x", function (id, oldval, newval) { hits++; return newval; });
-for (var i = 0; i < 10; i++)
- obj.x++;
-assertEq(hits, 10);
-
diff --git a/js/src/jit-test/tests/pic/watch2a.js b/js/src/jit-test/tests/pic/watch2a.js
deleted file mode 100644
index ce3294ba3..000000000
--- a/js/src/jit-test/tests/pic/watch2a.js
+++ /dev/null
@@ -1,18 +0,0 @@
-// assignments to watched properties via ++ must not be traced
-var hits = 0;
-function counter(id, oldval, newval) {
- hits++;
- return newval;
-}
-
-(function () {
- var obj = {x: 0, y: 0};
- var a = ['x', 'y'];
- obj.watch('z', counter);
- for (var i = 0; i < 14; i++) {
- obj.watch(a[+(i > 8)], counter);
- obj.y++;
- }
-})();
-assertEq(hits, 5);
-
diff --git a/js/src/jit-test/tests/pic/watch3.js b/js/src/jit-test/tests/pic/watch3.js
deleted file mode 100644
index 4c5c93d8b..000000000
--- a/js/src/jit-test/tests/pic/watch3.js
+++ /dev/null
@@ -1,7 +0,0 @@
-// assignment to watched global properties must not be cached
-x = 0;
-var hits = 0;
-this.watch("x", function (id, oldval, newval) { hits++; return newval; });
-for (var i = 0; i < 10; i++)
- x = i;
-assertEq(hits, 10);
diff --git a/js/src/jit-test/tests/pic/watch3a.js b/js/src/jit-test/tests/pic/watch3a.js
deleted file mode 100644
index 700daf6af..000000000
--- a/js/src/jit-test/tests/pic/watch3a.js
+++ /dev/null
@@ -1,19 +0,0 @@
-// assignment to watched global properties must not be traced
-var hits = 0;
-function counter(id, oldval, newval) {
- hits++;
- return newval;
-}
-
-var x = 0;
-var y = 0;
-(function () {
- var a = ['x', 'y'];
- this.watch('z', counter);
- for (var i = 0; i < 14; i++) {
- this.watch(a[+(i > 8)], counter);
- y = 1;
- }
-})();
-assertEq(hits, 5);
-
diff --git a/js/src/jit-test/tests/pic/watch3b.js b/js/src/jit-test/tests/pic/watch3b.js
deleted file mode 100644
index 9b0dc8cc9..000000000
--- a/js/src/jit-test/tests/pic/watch3b.js
+++ /dev/null
@@ -1,20 +0,0 @@
-// assignment to watched global properties must not be traced
-var hits = 0;
-function counter(id, oldval, newval) {
- hits++;
- return newval;
-}
-
-var x = 0;
-var y = 0;
-function f() {
- var a = [{}, this];
- for (var i = 0; i < 14; i++) {
- print(shapeOf(this));
- Object.prototype.watch.call(a[+(i > 8)], "y", counter);
- y++;
- }
-}
-f();
-assertEq(hits, 5);
-
diff --git a/js/src/jit-test/tests/pic/watch4.js b/js/src/jit-test/tests/pic/watch4.js
deleted file mode 100644
index 4b640c4df..000000000
--- a/js/src/jit-test/tests/pic/watch4.js
+++ /dev/null
@@ -1,9 +0,0 @@
-// adding assignment + watchpoint vs. caching
-var hits = 0;
-var obj = {};
-obj.watch("x", function (id, oldval, newval) { hits++; return newval; });
-for (var i = 0; i < 10; i++) {
- obj.x = 1;
- delete obj.x;
-}
-assertEq(hits, 10);
diff --git a/js/src/jit-test/tests/pic/watch5.js b/js/src/jit-test/tests/pic/watch5.js
deleted file mode 100644
index 6b22951a4..000000000
--- a/js/src/jit-test/tests/pic/watch5.js
+++ /dev/null
@@ -1,27 +0,0 @@
-// test against future pic support for symbols
-
-// assignments to watched objects must not be cached
-var obj = {};
-var x = Symbol.for("x");
-obj[x] = 0;
-var hits = 0;
-obj.watch(x, function (id, oldval, newval) { hits++; return newval; });
-for (var i = 0; i < 10; i++)
- obj[x] = i;
-assertEq(hits, 10);
-
-// assignments to watched properties via ++ must not be cached
-hits = 0;
-for (var i = 0; i < 10; i++)
- obj[x]++;
-assertEq(hits, 10);
-
-// adding assignment + watchpoint vs. caching
-hits = 0;
-obj = {};
-obj.watch(x, function (id, oldval, newval) { hits++; return newval; });
-for (var i = 0; i < 10; i++) {
- obj[x] = 1;
- delete obj[x];
-}
-assertEq(hits, 10);
diff --git a/js/src/jit-test/tests/profiler/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-test/tests/profiler/bug1140643.js b/js/src/jit-test/tests/profiler/bug1140643.js
deleted file mode 100644
index 1b171aea2..000000000
--- a/js/src/jit-test/tests/profiler/bug1140643.js
+++ /dev/null
@@ -1,14 +0,0 @@
-// |jit-test| allow-oom
-enableSPSProfiling();
-loadFile('\
-for (var i = 0; i < 2; i++) {\
- obj = { m: function () {} };\
- obj.watch("m", function () { float32 = 0 + obj.foo; });\
- obj.m = 0;\
-}\
-');
-gcparam("maxBytes", gcparam("gcBytes") + (1)*1024);
-newGlobal("same-compartment");
-function loadFile(lfVarx) {
- evaluate(lfVarx, { noScriptRval : true, isRunOnce : true });
-}
diff --git a/js/src/jit/AliasAnalysisShared.cpp b/js/src/jit/AliasAnalysisShared.cpp
index ae28327ca..0f0d4a66a 100644
--- a/js/src/jit/AliasAnalysisShared.cpp
+++ b/js/src/jit/AliasAnalysisShared.cpp
@@ -93,16 +93,11 @@ 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:
case MDefinition::Op_ArrayPopShift:
case MDefinition::Op_ArrayPush:
- case MDefinition::Op_ArraySlice:
case MDefinition::Op_LoadTypedArrayElementHole:
case MDefinition::Op_StoreTypedArrayElementHole:
case MDefinition::Op_LoadFixedSlot:
@@ -115,8 +110,6 @@ GetObject(const MDefinition* ins)
case MDefinition::Op_GuardObjectGroup:
case MDefinition::Op_GuardObjectIdentity:
case MDefinition::Op_GuardClass:
- case MDefinition::Op_GuardUnboxedExpando:
- case MDefinition::Op_LoadUnboxedExpando:
case MDefinition::Op_LoadSlot:
case MDefinition::Op_StoreSlot:
case MDefinition::Op_InArray:
@@ -126,6 +119,7 @@ GetObject(const MDefinition* ins)
object = ins->getOperand(0);
break;
case MDefinition::Op_GetPropertyCache:
+ case MDefinition::Op_CallGetProperty:
case MDefinition::Op_LoadTypedArrayElementStatic:
case MDefinition::Op_StoreTypedArrayElementStatic:
case MDefinition::Op_GetDOMProperty:
@@ -148,6 +142,7 @@ GetObject(const MDefinition* ins)
case MDefinition::Op_WasmLoadGlobalVar:
case MDefinition::Op_WasmStoreGlobalVar:
case MDefinition::Op_ArrayJoin:
+ case MDefinition::Op_ArraySlice:
return nullptr;
default:
#ifdef DEBUG
diff --git a/js/src/jit/BacktrackingAllocator.cpp b/js/src/jit/BacktrackingAllocator.cpp
index 94ef25785..645aefc4f 100644
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -378,7 +378,6 @@ BacktrackingAllocator::init()
size_t numVregs = graph.numVirtualRegisters();
if (!vregs.init(mir->alloc(), numVregs))
return false;
- memset(&vregs[0], 0, sizeof(VirtualRegister) * numVregs);
for (uint32_t i = 0; i < numVregs; i++)
new(&vregs[i]) VirtualRegister();
@@ -1101,9 +1100,9 @@ BacktrackingAllocator::mergeAndQueueRegisters()
if (iter->isParameter()) {
for (size_t i = 0; i < iter->numDefs(); i++) {
DebugOnly<bool> found = false;
- VirtualRegister &paramVreg = vreg(iter->getDef(i));
+ VirtualRegister& paramVreg = vreg(iter->getDef(i));
for (; original < paramVreg.vreg(); original++) {
- VirtualRegister &originalVreg = vregs[original];
+ VirtualRegister& originalVreg = vregs[original];
if (*originalVreg.def()->output() == *iter->getDef(i)->output()) {
MOZ_ASSERT(originalVreg.ins()->isParameter());
if (!tryMergeBundles(originalVreg.firstBundle(), paramVreg.firstBundle()))
@@ -1136,7 +1135,7 @@ BacktrackingAllocator::mergeAndQueueRegisters()
LBlock* block = graph.getBlock(i);
for (size_t j = 0; j < block->numPhis(); j++) {
LPhi* phi = block->getPhi(j);
- VirtualRegister &outputVreg = vreg(phi->getDef(0));
+ VirtualRegister& outputVreg = vreg(phi->getDef(0));
for (size_t k = 0, kend = phi->numOperands(); k < kend; k++) {
VirtualRegister& inputVreg = vreg(phi->getOperand(k)->toUse());
if (!tryMergeBundles(inputVreg.firstBundle(), outputVreg.firstBundle()))
@@ -1334,7 +1333,7 @@ BacktrackingAllocator::computeRequirement(LiveBundle* bundle,
for (LiveRange::BundleLinkIterator iter = bundle->rangesBegin(); iter; iter++) {
LiveRange* range = LiveRange::get(*iter);
- VirtualRegister &reg = vregs[range->vreg()];
+ VirtualRegister& reg = vregs[range->vreg()];
if (range->hasDefinition()) {
// Deal with any definition constraints/hints.
@@ -1396,7 +1395,7 @@ BacktrackingAllocator::tryAllocateRegister(PhysicalRegister& r, LiveBundle* bund
for (LiveRange::BundleLinkIterator iter = bundle->rangesBegin(); iter; iter++) {
LiveRange* range = LiveRange::get(*iter);
- VirtualRegister &reg = vregs[range->vreg()];
+ VirtualRegister& reg = vregs[range->vreg()];
if (!reg.isCompatible(r.reg))
return true;
@@ -1737,6 +1736,18 @@ BacktrackingAllocator::deadRange(LiveRange* range)
}
bool
+BacktrackingAllocator::moveAtEdge(LBlock* predecessor, LBlock* successor, LiveRange* from,
+ LiveRange* to, LDefinition::Type type)
+{
+ if (successor->mir()->numPredecessors() > 1) {
+ MOZ_ASSERT(predecessor->mir()->numSuccessors() == 1);
+ return moveAtExit(predecessor, from, to, type);
+ }
+
+ return moveAtEntry(successor, from, to, type);
+}
+
+bool
BacktrackingAllocator::resolveControlFlow()
{
// Add moves to handle changing assignments for vregs over their lifetime.
@@ -1844,10 +1855,15 @@ BacktrackingAllocator::resolveControlFlow()
LiveRange* from = vreg(input).rangeFor(exitOf(predecessor), /* preferRegister = */ true);
MOZ_ASSERT(from);
- if (!alloc().ensureBallast())
+ if (!alloc().ensureBallast()) {
return false;
- if (!moveAtExit(predecessor, from, to, def->type()))
+ }
+
+ // Note: we have to use moveAtEdge both here and below (for edge
+ // resolution) to avoid conflicting moves. See bug 1493900.
+ if (!moveAtEdge(predecessor, successor, from, to, def->type())) {
return false;
+ }
}
}
}
@@ -1876,16 +1892,12 @@ BacktrackingAllocator::resolveControlFlow()
if (targetRange->covers(exitOf(predecessor)))
continue;
- if (!alloc().ensureBallast())
+ if (!alloc().ensureBallast()) {
return false;
+ }
LiveRange* from = reg.rangeFor(exitOf(predecessor), true);
- if (successor->mir()->numPredecessors() > 1) {
- MOZ_ASSERT(predecessor->mir()->numSuccessors() == 1);
- if (!moveAtExit(predecessor, from, targetRange, reg.type()))
- return false;
- } else {
- if (!moveAtEntry(successor, from, targetRange, reg.type()))
- return false;
+ if (!moveAtEdge(predecessor, successor, from, targetRange, reg.type())) {
+ return false;
}
}
}
diff --git a/js/src/jit/BacktrackingAllocator.h b/js/src/jit/BacktrackingAllocator.h
index 6d14ffacd..c6cf26695 100644
--- a/js/src/jit/BacktrackingAllocator.h
+++ b/js/src/jit/BacktrackingAllocator.h
@@ -108,8 +108,9 @@ class Requirement
}
MOZ_ASSERT(newRequirement.kind() == Requirement::REGISTER);
- if (kind() == Requirement::FIXED)
+ if (kind() == Requirement::FIXED) {
return allocation().isRegister();
+ }
*this = newRequirement;
return true;
@@ -353,10 +354,12 @@ class LiveRange : public TempObject
// Comparator for use in range splay trees.
static int compare(LiveRange* v0, LiveRange* v1) {
// LiveRange includes 'from' but excludes 'to'.
- if (v0->to() <= v1->from())
+ if (v0->to() <= v1->from()) {
return -1;
- if (v0->from() >= v1->to())
+ }
+ if (v0->from() >= v1->to()) {
return 1;
+ }
return 0;
}
};
@@ -478,34 +481,31 @@ class LiveBundle : public TempObject
class VirtualRegister
{
// Instruction which defines this register.
- LNode* ins_;
+ LNode* ins_ = nullptr;
// Definition in the instruction for this register.
- LDefinition* def_;
+ LDefinition* def_ = nullptr;
// All live ranges for this register. These may overlap each other, and are
// ordered by their start position.
InlineForwardList<LiveRange::RegisterLink> ranges_;
// Whether def_ is a temp or an output.
- bool isTemp_;
+ bool isTemp_ = false;
// Whether this vreg is an input for some phi. This use is not reflected in
// any range on the vreg.
- bool usedByPhi_;
+ bool usedByPhi_ = false;
// If this register's definition is MUST_REUSE_INPUT, whether a copy must
// be introduced before the definition that relaxes the policy.
- bool mustCopyInput_;
+ bool mustCopyInput_ = false;
void operator=(const VirtualRegister&) = delete;
VirtualRegister(const VirtualRegister&) = delete;
public:
- explicit VirtualRegister()
- {
- // Note: This class is zeroed before it is constructed.
- }
+ VirtualRegister() = default;
void init(LNode* ins, LDefinition* def, bool isTemp) {
MOZ_ASSERT(!ins_);
@@ -645,10 +645,12 @@ class BacktrackingAllocator : protected RegisterAllocator
// Comparator for use in splay tree.
static int compare(CallRange* v0, CallRange* v1) {
- if (v0->range.to <= v1->range.from)
+ if (v0->range.to <= v1->range.from) {
return -1;
- if (v0->range.from >= v1->range.to)
+ }
+ if (v0->range.from >= v1->range.to) {
return 1;
+ }
return 0;
}
};
@@ -747,36 +749,43 @@ class BacktrackingAllocator : protected RegisterAllocator
MOZ_MUST_USE bool moveInput(LInstruction* ins, LiveRange* from, LiveRange* to,
LDefinition::Type type) {
- if (from->bundle()->allocation() == to->bundle()->allocation())
+ if (from->bundle()->allocation() == to->bundle()->allocation()) {
return true;
+ }
LMoveGroup* moves = getInputMoveGroup(ins);
return addMove(moves, from, to, type);
}
MOZ_MUST_USE bool moveAfter(LInstruction* ins, LiveRange* from, LiveRange* to,
LDefinition::Type type) {
- if (from->bundle()->allocation() == to->bundle()->allocation())
+ if (from->bundle()->allocation() == to->bundle()->allocation()) {
return true;
+ }
LMoveGroup* moves = getMoveGroupAfter(ins);
return addMove(moves, from, to, type);
}
MOZ_MUST_USE bool moveAtExit(LBlock* block, LiveRange* from, LiveRange* to,
LDefinition::Type type) {
- if (from->bundle()->allocation() == to->bundle()->allocation())
+ if (from->bundle()->allocation() == to->bundle()->allocation()) {
return true;
+ }
LMoveGroup* moves = block->getExitMoveGroup(alloc());
return addMove(moves, from, to, type);
}
MOZ_MUST_USE bool moveAtEntry(LBlock* block, LiveRange* from, LiveRange* to,
LDefinition::Type type) {
- if (from->bundle()->allocation() == to->bundle()->allocation())
+ if (from->bundle()->allocation() == to->bundle()->allocation()) {
return true;
+ }
LMoveGroup* moves = block->getEntryMoveGroup(alloc());
return addMove(moves, from, to, type);
}
+ MOZ_MUST_USE bool moveAtEdge(LBlock* predecessor, LBlock* successor, LiveRange* from,
+ LiveRange* to, LDefinition::Type type);
+
// Debugging methods.
void dumpAllocations();
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..9530f65fa 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);
@@ -4245,9 +4053,6 @@ TryAttachSetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, IC
{
MOZ_ASSERT(!*attached);
- if (obj->watched())
- return true;
-
RootedShape shape(cx);
RootedObject holder(cx);
if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape))
@@ -4256,18 +4061,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;
@@ -4354,9 +4148,6 @@ TryAttachSetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecode* pc,
MOZ_ASSERT(!*attached);
MOZ_ASSERT(!*isTemporarilyUnoptimizable);
- if (obj->watched())
- return true;
-
RootedShape shape(cx);
RootedObject holder(cx);
if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape))
@@ -4418,40 +4209,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 +4286,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 +4363,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 +4445,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 +4484,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 +4621,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 +4663,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 +5135,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 +5166,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 +5184,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 +5208,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 +5421,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 +5497,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 +5521,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 +5569,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 +5610,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 +5647,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 +6472,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 +6698,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 +7948,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 +7983,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 +8120,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 +8323,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 c9e09bed7..3b852debf 100644
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -96,32 +96,19 @@ VectorAppendNoDuplicate(S& list, T value)
static bool
AddReceiver(const ReceiverGuard& receiver,
- BaselineInspector::ReceiverVector& receivers,
- BaselineInspector::ObjectGroupVector& convertUnboxedGroups)
+ BaselineInspector::ReceiverVector& receivers)
{
- if (receiver.group && receiver.group->maybeUnboxedLayout()) {
- if (receiver.group->unboxedLayout().nativeGroup())
- return VectorAppendNoDuplicate(convertUnboxedGroups, receiver.group);
- }
return VectorAppendNoDuplicate(receivers, 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());
@@ -130,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);
@@ -146,40 +125,13 @@ 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,
- ObjectGroupVector& convertUnboxedGroups)
+BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers)
{
// Return a list of the receivers seen by the baseline IC for the current
// op. Empty lists indicate no receivers are known, or there was an
- // uncacheable access. convertUnboxedGroups is used for unboxed object
- // groups which have been seen, but have had instances converted to native
- // objects and should be eagerly converted by Ion.
+ // uncacheable access.
MOZ_ASSERT(receivers.empty());
- MOZ_ASSERT(convertUnboxedGroups.empty());
if (!hasBaselineScript())
return true;
@@ -191,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;
@@ -200,14 +151,12 @@ 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;
}
- if (!AddReceiver(receiver, receivers, convertUnboxedGroups))
+ if (!AddReceiver(receiver, receivers))
return false;
stub = stub->next();
@@ -589,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;
@@ -700,14 +649,12 @@ bool
BaselineInspector::commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
JSFunction** commonGetter, Shape** globalShape,
bool* isOwnProperty,
- ReceiverVector& receivers,
- ObjectGroupVector& convertUnboxedGroups)
+ ReceiverVector& receivers)
{
if (!hasBaselineScript())
return false;
MOZ_ASSERT(receivers.empty());
- MOZ_ASSERT(convertUnboxedGroups.empty());
*holder = nullptr;
const ICEntry& entry = icEntryFromPC(pc);
@@ -719,7 +666,7 @@ BaselineInspector::commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shap
{
ICGetPropCallGetter* nstub = static_cast<ICGetPropCallGetter*>(stub);
bool isOwn = nstub->isOwnGetter();
- if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers, convertUnboxedGroups))
+ if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers))
return false;
if (!*holder) {
@@ -751,21 +698,19 @@ BaselineInspector::commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shap
if (!*holder)
return false;
- MOZ_ASSERT(*isOwnProperty == (receivers.empty() && convertUnboxedGroups.empty()));
+ MOZ_ASSERT(*isOwnProperty == (receivers.empty()));
return true;
}
bool
BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
JSFunction** commonSetter, bool* isOwnProperty,
- ReceiverVector& receivers,
- ObjectGroupVector& convertUnboxedGroups)
+ ReceiverVector& receivers)
{
if (!hasBaselineScript())
return false;
MOZ_ASSERT(receivers.empty());
- MOZ_ASSERT(convertUnboxedGroups.empty());
*holder = nullptr;
const ICEntry& entry = icEntryFromPC(pc);
@@ -774,7 +719,7 @@ BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shap
if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) {
ICSetPropCallSetter* nstub = static_cast<ICSetPropCallSetter*>(stub);
bool isOwn = nstub->isOwnSetter();
- if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers, convertUnboxedGroups))
+ if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers))
return false;
if (!*holder) {
diff --git a/js/src/jit/BaselineInspector.h b/js/src/jit/BaselineInspector.h
index 961df18aa..1ed4b5547 100644
--- a/js/src/jit/BaselineInspector.h
+++ b/js/src/jit/BaselineInspector.h
@@ -95,8 +95,7 @@ class BaselineInspector
public:
typedef Vector<ReceiverGuard, 4, JitAllocPolicy> ReceiverVector;
typedef Vector<ObjectGroup*, 4, JitAllocPolicy> ObjectGroupVector;
- MOZ_MUST_USE bool maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers,
- ObjectGroupVector& convertUnboxedGroups);
+ MOZ_MUST_USE bool maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers);
SetElemICInspector setElemICInspector(jsbytecode* pc) {
return makeICInspector<SetElemICInspector>(pc, ICStub::SetElem_Fallback);
@@ -114,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);
@@ -131,12 +130,10 @@ class BaselineInspector
MOZ_MUST_USE bool commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
JSFunction** commonGetter, Shape** globalShape,
- bool* isOwnProperty, ReceiverVector& receivers,
- ObjectGroupVector& convertUnboxedGroups);
+ bool* isOwnProperty, ReceiverVector& receivers);
MOZ_MUST_USE bool commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
JSFunction** commonSetter, bool* isOwnProperty,
- ReceiverVector& receivers,
- ObjectGroupVector& convertUnboxedGroups);
+ ReceiverVector& receivers);
MOZ_MUST_USE bool instanceOfData(jsbytecode* pc, Shape** shape, uint32_t* slot,
JSObject** prototypeObject);
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 16d026092..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,19 +3028,10 @@ 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);
-
- Address expandoAddress(obj, UnboxedPlainObject::offsetOfExpando());
- if (guard.shape) {
- masm.loadPtr(expandoAddress, scratch);
- masm.branchPtr(Assembler::Equal, scratch, ImmWord(0), miss);
- masm.branchTestObjShape(Assembler::NotEqual, scratch, guard.shape, miss);
- } else if (checkNullExpando) {
- masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), miss);
- }
} else {
masm.branchTestObjShape(Assembler::NotEqual, obj, guard.shape, miss);
}
@@ -3058,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()) {
@@ -3077,13 +3067,6 @@ CodeGenerator::emitGetPropertyPolymorphic(LInstruction* ins, Register obj, Regis
masm.loadPtr(Address(target, NativeObject::offsetOfSlots()), scratch);
masm.loadTypedOrValue(Address(scratch, offset), output);
}
- } else {
- masm.comment("loadUnboxedProperty");
- const UnboxedLayout::Property* property =
- receiver.group->unboxedLayout().lookup(mir->name());
- Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset);
-
- masm.loadUnboxedProperty(propertyAddr, property->type, output);
}
if (i == mir->numReceivers() - 1) {
@@ -3124,8 +3107,6 @@ EmitUnboxedPreBarrier(MacroAssembler &masm, T address, JSValueType type)
masm.patchableCallPreBarrier(address, MIRType::Object);
else if (type == JSVAL_TYPE_STRING)
masm.patchableCallPreBarrier(address, MIRType::String);
- else
- MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(type));
}
void
@@ -3139,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()) {
@@ -3161,13 +3140,6 @@ CodeGenerator::emitSetPropertyPolymorphic(LInstruction* ins, Register obj, Regis
emitPreBarrier(addr);
masm.storeConstantOrRegister(value, addr);
}
- } else {
- const UnboxedLayout::Property* property =
- receiver.group->unboxedLayout().lookup(mir->name());
- Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset);
-
- EmitUnboxedPreBarrier(masm, propertyAddr, property->type);
- masm.storeUnboxedProperty(propertyAddr, property->type, value, nullptr);
}
if (i == mir->numReceivers() - 1) {
@@ -3208,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()));
}
@@ -3319,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());
@@ -3333,27 +3303,6 @@ CodeGenerator::visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir)
}
void
-CodeGenerator::visitGuardUnboxedExpando(LGuardUnboxedExpando* lir)
-{
- Label miss;
-
- Register obj = ToRegister(lir->object());
- masm.branchPtr(lir->mir()->requireExpando() ? Assembler::Equal : Assembler::NotEqual,
- Address(obj, UnboxedPlainObject::offsetOfExpando()), ImmWord(0), &miss);
-
- bailoutFrom(&miss, lir->snapshot());
-}
-
-void
-CodeGenerator::visitLoadUnboxedExpando(LLoadUnboxedExpando* lir)
-{
- Register obj = ToRegister(lir->object());
- Register result = ToRegister(lir->getDef(0));
-
- masm.loadPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), result);
-}
-
-void
CodeGenerator::visitTypeBarrierV(LTypeBarrierV* lir)
{
ValueOperand operand = ToValue(lir, LTypeBarrierV::Input);
@@ -3739,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);
@@ -3896,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.
@@ -3910,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*.
@@ -3996,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);
@@ -4052,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;
}
@@ -4096,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);
@@ -4123,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*.
@@ -4479,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)
{
@@ -5217,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;
}
@@ -5367,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");
@@ -5387,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) {
@@ -7823,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");
@@ -7858,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(),
@@ -8196,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());
-
- 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());
+ Address initLength(elements, ObjectElements::offsetOfInitializedLength());
+ masm.branch32(Assembler::BelowOrEqual, initLength, 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, 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());
}
@@ -8255,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());
+ 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());
- 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());
}
@@ -8366,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)
@@ -8380,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();
@@ -8390,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());
@@ -8399,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());
@@ -8411,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());
@@ -8423,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);
@@ -8435,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
@@ -8511,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());
@@ -8568,29 +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");
-
-void
-CodeGenerator::visitConvertUnboxedObjectToNative(LConvertUnboxedObjectToNative* lir)
-{
- Register object = ToRegister(lir->getOperand(0));
-
- OutOfLineCode* ool = oolCallVM(lir->mir()->group()->unboxedLayoutDontCheckGeneration().isArray()
- ? ConvertUnboxedArrayObjectToNativeInfo
- : ConvertUnboxedPlainObjectToNativeInfo,
- lir, ArgList(object), StoreNothing());
-
- masm.branchPtr(Assembler::Equal, Address(object, JSObject::offsetOfGroup()),
- ImmGCPtr(lir->mir()->group()), ool->entry());
- masm.bind(ool->rejoin());
-}
-
typedef bool (*ArrayPopShiftFn)(JSContext*, HandleObject, MutableHandleValue);
static const VMFunction ArrayPopDenseInfo =
FunctionInfo<ArrayPopShiftFn>(jit::ArrayPopDense, "ArrayPopDense");
@@ -8615,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
@@ -8640,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);
@@ -8658,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.
@@ -8731,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");
@@ -8742,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());
+ // Load elements and length.
+ masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
+ masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length);
- // Guard length < capacity.
- Address capacity(elementsTemp, ObjectElements::offsetOfCapacity());
- masm.branch32(Assembler::BelowOrEqual, capacity, key, ool->entry());
+ // Guard length == initializedLength.
+ Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
+ masm.branch32(Assembler::NotEqual, initLength, key, ool->entry());
- // 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 < capacity.
+ Address capacity(elementsTemp, ObjectElements::offsetOfCapacity());
+ masm.branch32(Assembler::BelowOrEqual, capacity, key, ool->entry());
- // Guard length < capacity.
- masm.checkUnboxedArrayCapacity(obj, key, elementsTemp, 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());
}
@@ -8934,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);
@@ -8950,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);
}
@@ -10590,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.
@@ -10983,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);
@@ -10996,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 b226f6cc9..12f1238ef 100644
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -148,8 +148,6 @@ class CodeGenerator final : public CodeGeneratorSpecific
void visitMaybeCopyElementsForWrite(LMaybeCopyElementsForWrite* lir);
void visitGuardObjectIdentity(LGuardObjectIdentity* guard);
void visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir);
- void visitGuardUnboxedExpando(LGuardUnboxedExpando* lir);
- void visitLoadUnboxedExpando(LLoadUnboxedExpando* lir);
void visitTypeBarrierV(LTypeBarrierV* lir);
void visitTypeBarrierO(LTypeBarrierO* lir);
void visitMonitorTypes(LMonitorTypes* lir);
@@ -167,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,
@@ -236,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);
@@ -257,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);
@@ -310,7 +303,6 @@ class CodeGenerator final : public CodeGeneratorSpecific
void visitFallibleStoreElementV(LFallibleStoreElementV* lir);
void visitFallibleStoreElementT(LFallibleStoreElementT* lir);
void visitStoreUnboxedPointer(LStoreUnboxedPointer* lir);
- void visitConvertUnboxedObjectToNative(LConvertUnboxedObjectToNative* lir);
void emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, Register obj,
Register elementsTemp, Register lengthTemp, TypedOrValueRegister out);
void visitArrayPopShiftV(LArrayPopShiftV* lir);
diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h
index 23d3143a7..9c864515d 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 2c9ffb607..ace6cd81e 100644
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -2306,7 +2306,7 @@ jit::RemoveUnmarkedBlocks(MIRGenerator* mir, MIRGraph& graph, uint32_t numMarked
// bailout.
for (PostorderIterator it(graph.poBegin()); it != graph.poEnd();) {
MBasicBlock* block = *it++;
- if (!block->isMarked())
+ if (block->isMarked())
continue;
FlagAllOperandsAsHavingRemovedUses(mir, block);
@@ -3127,6 +3127,15 @@ ExtractMathSpace(MDefinition* ins)
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unknown TruncateKind");
}
+static bool MonotoneAdd(int32_t lhs, int32_t rhs) {
+ return (lhs >= 0 && rhs >= 0) || (lhs <= 0 && rhs <= 0);
+}
+
+static bool MonotoneSub(int32_t lhs, int32_t rhs) {
+ return (lhs >= 0 && rhs <= 0) || (lhs <= 0 && rhs >= 0);
+}
+
+
// Extract a linear sum from ins, if possible (otherwise giving the sum 'ins + 0').
SimpleLinearSum
jit::ExtractLinearSum(MDefinition* ins, MathSpace space)
@@ -3168,10 +3177,12 @@ jit::ExtractLinearSum(MDefinition* ins, MathSpace space)
// Check if this is of the form <SUM> + n or n + <SUM>.
if (ins->isAdd()) {
int32_t constant;
- if (space == MathSpace::Modulo)
+ if (space == MathSpace::Modulo) {
constant = lsum.constant + rsum.constant;
- else if (!SafeAdd(lsum.constant, rsum.constant, &constant))
+ } else if (!SafeAdd(lsum.constant, rsum.constant, &constant) ||
+ !MonotoneAdd(lsum.constant, rsum.constant)) {
return SimpleLinearSum(ins, 0);
+ }
return SimpleLinearSum(lsum.term ? lsum.term : rsum.term, constant);
}
@@ -3179,10 +3190,12 @@ jit::ExtractLinearSum(MDefinition* ins, MathSpace space)
// Check if this is of the form <SUM> - n.
if (lsum.term) {
int32_t constant;
- if (space == MathSpace::Modulo)
+ if (space == MathSpace::Modulo) {
constant = lsum.constant - rsum.constant;
- else if (!SafeSub(lsum.constant, rsum.constant, &constant))
+ } else if (!SafeSub(lsum.constant, rsum.constant, &constant) ||
+ !MonotoneSub(lsum.constant, rsum.constant)) {
return SimpleLinearSum(ins, 0);
+ }
return SimpleLinearSum(lsum.term, constant);
}
@@ -3502,8 +3515,6 @@ PassthroughOperand(MDefinition* def)
return def->toConvertElementsToDoubles()->elements();
if (def->isMaybeCopyElementsForWrite())
return def->toMaybeCopyElementsForWrite()->object();
- if (def->isConvertUnboxedObjectToNative())
- return def->toConvertUnboxedObjectToNative()->object();
return nullptr;
}
@@ -3994,7 +4005,7 @@ jit::ConvertLinearInequality(TempAllocator& alloc, MBasicBlock* block, const Lin
}
static bool
-AnalyzePoppedThis(JSContext* cx, ObjectGroup* group,
+AnalyzePoppedThis(JSContext* cx, DPAConstraintInfo& constraintInfo, ObjectGroup* group,
MDefinition* thisValue, MInstruction* ins, bool definitelyExecuted,
HandlePlainObject baseobj,
Vector<TypeNewScript::Initializer>* initializerList,
@@ -4035,7 +4046,12 @@ AnalyzePoppedThis(JSContext* cx, ObjectGroup* group,
return true;
RootedId id(cx, NameToId(setprop->name()));
- if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, group, id)) {
+ bool added = false;
+ if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, constraintInfo,
+ group, id, &added)) {
+ return false;
+ }
+ if (!added) {
// The prototype chain already contains a getter/setter for this
// property, or type information is too imprecise.
return true;
@@ -4044,7 +4060,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());
@@ -4095,7 +4111,12 @@ AnalyzePoppedThis(JSContext* cx, ObjectGroup* group,
if (!baseobj->lookup(cx, id) && !accessedProperties->append(get->name()))
return false;
- if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, group, id)) {
+ bool added = false;
+ if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, constraintInfo,
+ group, id, &added)) {
+ return false;
+ }
+ if (!added) {
// The |this| value can escape if any property reads it does go
// through a getter.
return true;
@@ -4121,8 +4142,11 @@ CmpInstructions(const void* a, const void* b)
}
bool
-jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx, JSFunction* fun,
- ObjectGroup* group, HandlePlainObject baseobj,
+jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx,
+ DPAConstraintInfo& constraintInfo,
+ HandleFunction fun,
+ ObjectGroup* group,
+ HandlePlainObject baseobj,
Vector<TypeNewScript::Initializer>* initializerList)
{
MOZ_ASSERT(cx->zone()->types.activeAnalysis);
@@ -4131,7 +4155,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;
@@ -4282,7 +4306,7 @@ jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx, JSFunction* fun,
bool handled = false;
size_t slotSpan = baseobj->slotSpan();
- if (!AnalyzePoppedThis(cx, group, thisValue, ins, definitelyExecuted,
+ if (!AnalyzePoppedThis(cx, constraintInfo, group, thisValue, ins, definitelyExecuted,
baseobj, initializerList, &accessedProperties, &handled))
{
return false;
@@ -4301,7 +4325,6 @@ jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx, JSFunction* fun,
// contingent on the correct frames being inlined. Add constraints to
// invalidate the definite properties if additional functions could be
// called at the inline frame sites.
- Vector<MBasicBlock*> exitBlocks(cx);
for (MBasicBlockIterator block(graph.begin()); block != graph.end(); block++) {
// Inlining decisions made after the last new property was added to
// the object don't need to be frozen.
@@ -4309,9 +4332,11 @@ jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx, JSFunction* fun,
break;
if (MResumePoint* rp = block->callerResumePoint()) {
if (block->numPredecessors() == 1 && block->getPredecessor(0) == rp->block()) {
- JSScript* script = rp->block()->info().script();
- if (!AddClearDefiniteFunctionUsesInScript(cx, group, script, block->info().script()))
+ JSScript* caller = rp->block()->info().script();
+ JSScript* callee = block->info().script();
+ if (!constraintInfo.addInliningConstraint(caller, callee)) {
return false;
+ }
}
}
}
diff --git a/js/src/jit/IonAnalysis.h b/js/src/jit/IonAnalysis.h
index 1ce8edc80..49bc0b591 100644
--- a/js/src/jit/IonAnalysis.h
+++ b/js/src/jit/IonAnalysis.h
@@ -196,8 +196,11 @@ MCompare*
ConvertLinearInequality(TempAllocator& alloc, MBasicBlock* block, const LinearSum& sum);
MOZ_MUST_USE bool
-AnalyzeNewScriptDefiniteProperties(JSContext* cx, JSFunction* fun,
- ObjectGroup* group, HandlePlainObject baseobj,
+AnalyzeNewScriptDefiniteProperties(JSContext* cx,
+ DPAConstraintInfo& constraintInfo,
+ HandleFunction fun,
+ ObjectGroup* group,
+ HandlePlainObject baseobj,
Vector<TypeNewScript::Initializer>* initializerList);
MOZ_MUST_USE bool
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index 54d05cac4..0c69729a4 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -32,7 +32,6 @@
#include "vm/EnvironmentObject-inl.h"
#include "vm/NativeObject-inl.h"
#include "vm/ObjectGroup-inl.h"
-#include "vm/UnboxedObject-inl.h"
using namespace js;
using namespace js::jit;
@@ -467,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;
@@ -1939,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:
@@ -1946,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:
@@ -2219,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;
}
@@ -5872,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);
@@ -5911,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;
@@ -6087,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);
@@ -6392,7 +6396,7 @@ IonBuilder::createThisScriptedSingleton(JSFunction* target, MDefinition* callee)
JSObject* templateObject = inspector->getTemplateObject(pc);
if (!templateObject)
return nullptr;
- if (!templateObject->is<PlainObject>() && !templateObject->is<UnboxedPlainObject>())
+ if (!templateObject->is<PlainObject>())
return nullptr;
if (templateObject->staticPrototype() != proto)
return nullptr;
@@ -6429,7 +6433,7 @@ IonBuilder::createThisScriptedBaseline(MDefinition* callee)
JSObject* templateObject = inspector->getTemplateObject(pc);
if (!templateObject)
return nullptr;
- if (!templateObject->is<PlainObject>() && !templateObject->is<UnboxedPlainObject>())
+ if (!templateObject->is<PlainObject>())
return nullptr;
Shape* shape = target->lookupPure(compartment->runtime()->names().prototype);
@@ -6536,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);
@@ -6561,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;
@@ -6598,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);
@@ -6627,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);
@@ -6736,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();
@@ -6782,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();
@@ -6808,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;
@@ -6944,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;
@@ -7045,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)
@@ -7061,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();
@@ -7100,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;
@@ -7117,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
@@ -7337,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 =
@@ -7598,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() ||
@@ -7609,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;
@@ -7634,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;
@@ -7718,8 +7703,6 @@ IonBuilder::jsop_initprop(PropertyName* name)
if (templateObject->is<PlainObject>()) {
if (!templateObject->as<PlainObject>().containsPure(name))
useSlowPath = true;
- } else {
- MOZ_ASSERT(templateObject->as<UnboxedPlainObject>().layout().lookup(name));
}
} else {
useSlowPath = true;
@@ -8178,9 +8161,7 @@ IonBuilder::maybeMarkEmpty(MDefinition* ins)
static bool
ClassHasEffectlessLookup(const Class* clasp)
{
- return (clasp == &UnboxedPlainObject::class_) ||
- (clasp == &UnboxedArrayObject::class_) ||
- IsTypedObjectClass(clasp) ||
+ return IsTypedObjectClass(clasp) ||
(clasp->isNative() && !clasp->getOpsLookupProperty());
}
@@ -8914,10 +8895,8 @@ IonBuilder::jsop_getimport(PropertyName* name)
if (!emitted) {
// This can happen if we don't have type information.
- TypeSet::ObjectKey* staticKey = TypeSet::ObjectKey::get(targetEnv);
TemporaryTypeSet* types = bytecodeTypes(pc);
- BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), staticKey,
- name, types, /* updateObserved = */ true);
+ BarrierKind barrier = BarrierKind::TypeSet;
if (!loadStaticSlot(targetEnv, barrier, types, shape->slot()))
return false;
@@ -9012,8 +8991,6 @@ IonBuilder::jsop_getelem()
}
obj = maybeUnboxForPropertyAccess(obj);
- if (obj->type() == MIRType::Object)
- obj = convertUnboxedObjects(obj);
bool emitted = false;
@@ -9458,12 +9435,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
@@ -9480,7 +9454,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();
@@ -9832,7 +9806,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);
@@ -9856,7 +9830,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.
@@ -9865,13 +9839,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.
@@ -9887,7 +9861,6 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType
}
bool loadDouble =
- unboxedType == JSVAL_TYPE_MAGIC &&
barrier == BarrierKind::NoBarrier &&
loopDepth_ &&
inBounds &&
@@ -9906,18 +9879,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
@@ -9927,8 +9895,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);
}
@@ -10137,7 +10104,7 @@ IonBuilder::jsop_setelem()
MDefinition* value = current->pop();
MDefinition* index = current->pop();
- MDefinition* object = convertUnboxedObjects(current->pop());
+ MDefinition* object = current->pop();
trackTypeInfo(TrackedTypeSite::Receiver, object->type(), object->resultTypeSet());
trackTypeInfo(TrackedTypeSite::Index, index->type(), index->resultTypeSet());
@@ -10375,12 +10342,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,
@@ -10414,7 +10378,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) {
@@ -10504,13 +10468,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
@@ -10540,7 +10502,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.
@@ -10577,7 +10539,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;
@@ -10589,27 +10551,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);
}
@@ -10727,18 +10685,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()) {
@@ -10972,11 +10918,8 @@ IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_
}
// Definite slots will always be fixed slots when they are in the
- // allowable range for fixed slots, except for objects which were
- // converted from unboxed objects and have a smaller allocation size.
+ // allowable range for fixed slots.
size_t nfixed = NativeObject::MAX_FIXED_SLOTS;
- if (ObjectGroup* group = key->group()->maybeOriginalUnboxedGroup())
- nfixed = gc::GetGCKindSlots(group->unboxedLayout().getAllocKind());
uint32_t propertySlot = property.maybeTypes()->definiteSlot();
if (slot == UINT32_MAX) {
@@ -10991,65 +10934,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;
- }
-
- key->watchStateChangeForUnboxedConvertedToNative(constraints());
-
- 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()
{
@@ -11496,8 +11380,6 @@ IonBuilder::jsop_getprop(PropertyName* name)
}
obj = maybeUnboxForPropertyAccess(obj);
- if (obj->type() == MIRType::Object)
- obj = convertUnboxedObjects(obj);
BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
obj, name, types);
@@ -11569,11 +11451,6 @@ IonBuilder::jsop_getprop(PropertyName* name)
if (!getPropTryDefiniteSlot(&emitted, obj, name, barrier, types) || emitted)
return emitted;
- // Try to emit loads from unboxed objects.
- trackOptimizationAttempt(TrackedStrategy::GetProp_Unboxed);
- if (!getPropTryUnboxed(&emitted, obj, name, barrier, types) || emitted)
- return emitted;
-
// Try to inline a common property getter, or make a call.
trackOptimizationAttempt(TrackedStrategy::GetProp_CommonGetter);
if (!getPropTryCommonGetter(&emitted, obj, name, types) || emitted)
@@ -11939,49 +11816,6 @@ IonBuilder::getPropTryComplexPropOfTypedObject(bool* emitted,
fieldPrediction, fieldTypeObj);
}
-MDefinition*
-IonBuilder::convertUnboxedObjects(MDefinition* obj)
-{
- // If obj might be in any particular unboxed group which should be
- // converted to a native representation, perform that conversion. This does
- // not guarantee the object will not have such a group afterwards, if the
- // object's possible groups are not precisely known.
- TemporaryTypeSet* types = obj->resultTypeSet();
- if (!types || types->unknownObject() || !types->objectOrSentinel())
- return obj;
-
- BaselineInspector::ObjectGroupVector list(alloc());
- for (size_t i = 0; i < types->getObjectCount(); i++) {
- TypeSet::ObjectKey* key = obj->resultTypeSet()->getObject(i);
- if (!key || !key->isGroup())
- continue;
-
- if (UnboxedLayout* layout = key->group()->maybeUnboxedLayout()) {
- AutoEnterOOMUnsafeRegion oomUnsafe;
- if (layout->nativeGroup() && !list.append(key->group()))
- oomUnsafe.crash("IonBuilder::convertUnboxedObjects");
- }
- }
-
- return convertUnboxedObjects(obj, list);
-}
-
-MDefinition*
-IonBuilder::convertUnboxedObjects(MDefinition* obj,
- const BaselineInspector::ObjectGroupVector& list)
-{
- for (size_t i = 0; i < list.length(); i++) {
- ObjectGroup* group = list[i];
- if (TemporaryTypeSet* types = obj->resultTypeSet()) {
- if (!types->hasType(TypeSet::ObjectType(group)))
- continue;
- }
- obj = MConvertUnboxedObjectToNative::New(alloc(), obj, group);
- current->add(obj->toInstruction());
- }
- return obj;
-}
-
bool
IonBuilder::getPropTryDefiniteSlot(bool* emitted, MDefinition* obj, PropertyName* name,
BarrierKind barrier, TemporaryTypeSet* types)
@@ -12066,111 +11900,14 @@ 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;
-}
-
-bool
-IonBuilder::getPropTryUnboxed(bool* emitted, MDefinition* obj, PropertyName* name,
- BarrierKind barrier, TemporaryTypeSet* types)
-{
- MOZ_ASSERT(*emitted == false);
-
- JSValueType unboxedType;
- uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), name, &unboxedType);
- if (offset == UINT32_MAX)
- return true;
-
- if (obj->type() != MIRType::Object) {
- MGuardObject* guard = MGuardObject::New(alloc(), obj);
- current->add(guard);
- obj = guard;
- }
-
- MInstruction* load = loadUnboxedProperty(obj, offset, unboxedType, barrier, types);
- current->push(load);
-
- if (!pushTypeBarrier(load, types, barrier))
- return false;
-
- trackOptimizationSuccess();
- *emitted = true;
- return true;
-}
-
MDefinition*
IonBuilder::addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape,
const BaselineInspector::ReceiverVector& receivers,
- const BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
bool isOwnProperty)
{
MOZ_ASSERT(holder);
MOZ_ASSERT(holderShape);
- obj = convertUnboxedObjects(obj, convertUnboxedGroups);
-
if (isOwnProperty) {
MOZ_ASSERT(receivers.empty());
return addShapeGuard(obj, holderShape, Bailout_ShapeGuard);
@@ -12194,10 +11931,8 @@ IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName
JSObject* foundProto = nullptr;
bool isOwnProperty = false;
BaselineInspector::ReceiverVector receivers(alloc());
- BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
if (!inspector->commonGetPropFunction(pc, &foundProto, &lastProperty, &commonGetter,
- &globalShape, &isOwnProperty,
- receivers, convertUnboxedGroups))
+ &globalShape, &isOwnProperty, receivers))
{
return true;
}
@@ -12213,8 +11948,7 @@ IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName
// If type information is bad, we can still optimize the getter if we
// shape guard.
obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty,
- receivers, convertUnboxedGroups,
- isOwnProperty);
+ receivers, isOwnProperty);
if (!obj)
return false;
}
@@ -12275,7 +12009,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;
@@ -12381,15 +12116,12 @@ IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName
MOZ_ASSERT(*emitted == false);
BaselineInspector::ReceiverVector receivers(alloc());
- BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
- if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups))
+ if (!inspector->maybeInfoForPropertyOp(pc, receivers))
return false;
if (!canInlinePropertyOpShapes(receivers))
return true;
- obj = convertUnboxedObjects(obj, convertUnboxedGroups);
-
MIRType rvalType = types->getKnownMIRType();
if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType))
rvalType = MIRType::Value;
@@ -12412,45 +12144,6 @@ IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName
return true;
}
- if (receivers[0].shape) {
- // Monomorphic load from an unboxed object expando.
- spew("Inlining monomorphic unboxed expando GETPROP");
-
- obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard);
- obj = addUnboxedExpandoGuard(obj, /* hasExpando = */ true, Bailout_ShapeGuard);
-
- MInstruction* expando = MLoadUnboxedExpando::New(alloc(), obj);
- current->add(expando);
-
- expando = addShapeGuard(expando, receivers[0].shape, Bailout_ShapeGuard);
-
- Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
- MOZ_ASSERT(shape);
-
- if (!loadSlot(expando, shape, rvalType, barrier, types))
- return false;
-
- trackOptimizationOutcome(TrackedOutcome::Monomorphic);
- *emitted = true;
- return true;
- }
-
- // Monomorphic load from an unboxed object.
- ObjectGroup* group = receivers[0].group;
- if (obj->resultTypeSet() && !obj->resultTypeSet()->hasType(TypeSet::ObjectType(group)))
- return true;
-
- obj = addGroupGuard(obj, group, Bailout_ShapeGuard);
-
- const UnboxedLayout::Property* property = group->unboxedLayout().lookup(name);
- MInstruction* load = loadUnboxedProperty(obj, property->offset, property->type, barrier, types);
- current->push(load);
-
- if (!pushTypeBarrier(load, types, barrier))
- return false;
-
- trackOptimizationOutcome(TrackedOutcome::Monomorphic);
- *emitted = true;
return true;
}
@@ -12692,7 +12385,7 @@ bool
IonBuilder::jsop_setprop(PropertyName* name)
{
MDefinition* value = current->pop();
- MDefinition* obj = convertUnboxedObjects(current->pop());
+ MDefinition* obj = current->pop();
bool emitted = false;
startTrackingOptimizations();
@@ -12725,13 +12418,6 @@ IonBuilder::jsop_setprop(PropertyName* name)
bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value,
/* canModify = */ true);
- if (!forceInlineCaches()) {
- // Try to emit stores to unboxed objects.
- trackOptimizationAttempt(TrackedStrategy::SetProp_Unboxed);
- if (!setPropTryUnboxed(&emitted, obj, name, value, barrier, objTypes) || emitted)
- return emitted;
- }
-
// Add post barrier if needed. The instructions above manage any post
// barriers they need directly.
if (NeedsPostBarrier(value))
@@ -12765,10 +12451,8 @@ IonBuilder::setPropTryCommonSetter(bool* emitted, MDefinition* obj,
JSObject* foundProto = nullptr;
bool isOwnProperty;
BaselineInspector::ReceiverVector receivers(alloc());
- BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
if (!inspector->commonSetPropFunction(pc, &foundProto, &lastProperty, &commonSetter,
- &isOwnProperty,
- receivers, convertUnboxedGroups))
+ &isOwnProperty, receivers))
{
trackOptimizationOutcome(TrackedOutcome::NoProtoFound);
return true;
@@ -12783,8 +12467,7 @@ IonBuilder::setPropTryCommonSetter(bool* emitted, MDefinition* obj,
// If type information is bad, we can still optimize the setter if we
// shape guard.
obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty,
- receivers, convertUnboxedGroups,
- isOwnProperty);
+ receivers, isOwnProperty);
if (!obj)
return false;
}
@@ -12822,7 +12505,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;
@@ -13039,100 +12723,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::setPropTryUnboxed(bool* emitted, MDefinition* obj,
- PropertyName* name, MDefinition* value,
- bool barrier, TemporaryTypeSet* objTypes)
-{
- MOZ_ASSERT(*emitted == false);
-
- if (barrier) {
- trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
- return true;
- }
-
- JSValueType unboxedType;
- uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), name, &unboxedType);
- if (offset == UINT32_MAX)
- return true;
-
- if (obj->type() != MIRType::Object) {
- MGuardObject* guard = MGuardObject::New(alloc(), obj);
- current->add(guard);
- obj = guard;
- }
-
- MInstruction* store = storeUnboxedProperty(obj, offset, unboxedType, value);
-
- current->push(value);
-
- if (!resumeAfter(store))
- return false;
-
- *emitted = true;
- return true;
-}
-
bool
IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj,
PropertyName* name, MDefinition* value,
@@ -13146,15 +12736,12 @@ IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj,
}
BaselineInspector::ReceiverVector receivers(alloc());
- BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
- if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups))
+ if (!inspector->maybeInfoForPropertyOp(pc, receivers))
return false;
if (!canInlinePropertyOpShapes(receivers))
return true;
- obj = convertUnboxedObjects(obj, convertUnboxedGroups);
-
if (receivers.length() == 1) {
if (!receivers[0].group) {
// Monomorphic store to a native object.
@@ -13174,46 +12761,6 @@ IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj,
return true;
}
- if (receivers[0].shape) {
- // Monomorphic store to an unboxed object expando.
- spew("Inlining monomorphic unboxed expando SETPROP");
-
- obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard);
- obj = addUnboxedExpandoGuard(obj, /* hasExpando = */ true, Bailout_ShapeGuard);
-
- MInstruction* expando = MLoadUnboxedExpando::New(alloc(), obj);
- current->add(expando);
-
- expando = addShapeGuard(expando, receivers[0].shape, Bailout_ShapeGuard);
-
- Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
- MOZ_ASSERT(shape);
-
- bool needsBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
- if (!storeSlot(expando, shape, value, needsBarrier))
- return false;
-
- trackOptimizationOutcome(TrackedOutcome::Monomorphic);
- *emitted = true;
- return true;
- }
-
- // Monomorphic store to an unboxed object.
- spew("Inlining monomorphic unboxed SETPROP");
-
- ObjectGroup* group = receivers[0].group;
- if (!objTypes->hasType(TypeSet::ObjectType(group)))
- return true;
-
- obj = addGroupGuard(obj, group, Bailout_ShapeGuard);
-
- const UnboxedLayout::Property* property = group->unboxedLayout().lookup(name);
- storeUnboxedProperty(obj, property->offset, property->type, value);
-
- current->push(value);
-
- trackOptimizationOutcome(TrackedOutcome::Monomorphic);
- *emitted = true;
return true;
}
@@ -13884,7 +13431,7 @@ IonBuilder::jsop_setaliasedvar(EnvironmentCoordinate ec)
bool
IonBuilder::jsop_in()
{
- MDefinition* obj = convertUnboxedObjects(current->pop());
+ MDefinition* obj = current->pop();
MDefinition* id = current->pop();
bool emitted = false;
@@ -13911,11 +13458,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;
@@ -13930,10 +13474,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_) {
@@ -13943,8 +13487,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);
@@ -14241,19 +13784,6 @@ IonBuilder::addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bail
}
MInstruction*
-IonBuilder::addUnboxedExpandoGuard(MDefinition* obj, bool hasExpando, BailoutKind bailoutKind)
-{
- MGuardUnboxedExpando* guard = MGuardUnboxedExpando::New(alloc(), obj, hasExpando, bailoutKind);
- current->add(guard);
-
- // If a shape guard failed in the past, don't optimize group guards.
- if (failedShapeGuard_)
- guard->setNotMovable();
-
- return guard;
-}
-
-MInstruction*
IonBuilder::addGuardReceiverPolymorphic(MDefinition* obj,
const BaselineInspector::ReceiverVector& receivers)
{
@@ -14262,15 +13792,6 @@ IonBuilder::addGuardReceiverPolymorphic(MDefinition* obj,
// Monomorphic guard on a native object.
return addShapeGuard(obj, receivers[0].shape, Bailout_ShapeGuard);
}
-
- if (!receivers[0].shape) {
- // Guard on an unboxed object that does not have an expando.
- obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard);
- return addUnboxedExpandoGuard(obj, /* hasExpando = */ false, Bailout_ShapeGuard);
- }
-
- // Monomorphic receiver guards are not yet supported when the receiver
- // is an unboxed object with an expando.
}
MGuardReceiverPolymorphic* guard = MGuardReceiverPolymorphic::New(alloc(), obj);
@@ -14644,32 +14165,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 f24ef30c8..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);
@@ -401,7 +400,6 @@ class IonBuilder
MInstruction* addBoundsCheck(MDefinition* index, MDefinition* length);
MInstruction* addShapeGuard(MDefinition* obj, Shape* const shape, BailoutKind bailoutKind);
MInstruction* addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bailoutKind);
- MInstruction* addUnboxedExpandoGuard(MDefinition* obj, bool hasExpando, BailoutKind bailoutKind);
MInstruction* addSharedTypedArrayGuard(MDefinition* obj);
MInstruction*
@@ -441,8 +439,6 @@ class IonBuilder
BarrierKind barrier, TemporaryTypeSet* types);
MOZ_MUST_USE bool getPropTryModuleNamespace(bool* emitted, MDefinition* obj, PropertyName* name,
BarrierKind barrier, TemporaryTypeSet* types);
- MOZ_MUST_USE bool getPropTryUnboxed(bool* emitted, MDefinition* obj, PropertyName* name,
- BarrierKind barrier, TemporaryTypeSet* types);
MOZ_MUST_USE bool getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName* name,
TemporaryTypeSet* types);
MOZ_MUST_USE bool getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName* name,
@@ -475,9 +471,6 @@ class IonBuilder
MOZ_MUST_USE bool setPropTryDefiniteSlot(bool* emitted, MDefinition* obj,
PropertyName* name, MDefinition* value,
bool barrier, TemporaryTypeSet* objTypes);
- MOZ_MUST_USE bool setPropTryUnboxed(bool* emitted, MDefinition* obj,
- PropertyName* name, MDefinition* value,
- bool barrier, TemporaryTypeSet* objTypes);
MOZ_MUST_USE bool setPropTryInlineAccess(bool* emitted, MDefinition* obj,
PropertyName* name, MDefinition* value,
bool barrier, TemporaryTypeSet* objTypes);
@@ -617,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.
@@ -707,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();
@@ -729,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);
@@ -830,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);
@@ -1041,7 +1031,6 @@ class IonBuilder
MDefinition*
addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape,
const BaselineInspector::ReceiverVector& receivers,
- const BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
bool isOwnProperty);
MOZ_MUST_USE bool annotateGetPropertyCache(MDefinition* obj, PropertyName* name,
@@ -1059,22 +1048,6 @@ class IonBuilder
ResultWithOOM<bool> testNotDefinedProperty(MDefinition* obj, jsid id);
uint32_t getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed);
- MDefinition* convertUnboxedObjects(MDefinition* obj);
- MDefinition* convertUnboxedObjects(MDefinition* obj,
- const BaselineInspector::ObjectGroupVector& list);
- 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);
@@ -1379,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)
{ }
@@ -1397,6 +1375,7 @@ class CallInfo
fun_ = callInfo.fun();
thisArg_ = callInfo.thisArg();
+ ignoresReturnValue_ = callInfo.ignoresReturnValue();
if (constructing())
newTargetArg_ = callInfo.getNewTarget();
@@ -1493,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 96e488ea8..fb4291188 100644
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -619,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);
@@ -758,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());
@@ -777,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()) {
@@ -838,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;
}
@@ -869,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,
@@ -1187,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.
@@ -1533,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)
{
@@ -2196,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;
}
@@ -2383,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()));
@@ -3026,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.
@@ -3063,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());
@@ -3076,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);
@@ -3098,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())
@@ -3126,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.
@@ -3370,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)
@@ -3586,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)
@@ -3613,7 +3232,7 @@ SetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript*
MOZ_ASSERT(!*emitted);
MOZ_ASSERT(!*tryNativeAddSlot);
- if (!canAttachStub() || obj->watched())
+ if (!canAttachStub())
return true;
// Fail cache emission if the object is frozen
@@ -3630,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()) {
@@ -3687,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;
}
@@ -3713,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);
@@ -4025,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());
@@ -4056,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();
}
@@ -4139,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);
@@ -4325,9 +3897,6 @@ IsDenseElementSetInlineable(JSObject* obj, const Value& idval, const ConstantOrR
if (!obj->is<ArrayObject>())
return false;
- if (obj->watched())
- return false;
-
if (!idval.isInt32())
return false;
diff --git a/js/src/jit/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/IonCode.h b/js/src/jit/IonCode.h
index c581aa62e..55c3d4dad 100644
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -9,7 +9,6 @@
#include "mozilla/Atomics.h"
#include "mozilla/MemoryReporting.h"
-#include "mozilla/PodOperations.h"
#include "jstypes.h"
@@ -692,17 +691,15 @@ struct IonScriptCounts
{
private:
// Any previous invalidated compilation(s) for the script.
- IonScriptCounts* previous_;
+ IonScriptCounts* previous_ = nullptr;
// Information about basic blocks in this script.
- size_t numBlocks_;
- IonBlockCounts* blocks_;
+ size_t numBlocks_ = 0;
+ IonBlockCounts* blocks_ = nullptr;
public:
- IonScriptCounts() {
- mozilla::PodZero(this);
- }
+ IonScriptCounts() = default;
~IonScriptCounts() {
for (size_t i = 0; i < numBlocks_; i++)
diff --git a/js/src/jit/JitOptions.cpp b/js/src/jit/JitOptions.cpp
index eb5a6c1c2..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, false);
-
// 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 709de9987..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;
}
@@ -3252,14 +3204,6 @@ LIRGenerator::visitStoreUnboxedString(MStoreUnboxedString* ins)
}
void
-LIRGenerator::visitConvertUnboxedObjectToNative(MConvertUnboxedObjectToNative* ins)
-{
- LInstruction* check = new(alloc()) LConvertUnboxedObjectToNative(useRegister(ins->object()));
- add(check, ins);
- assignSafepoint(check, ins);
-}
-
-void
LIRGenerator::visitEffectiveAddress(MEffectiveAddress* ins)
{
define(new(alloc()) LEffectiveAddress(useRegister(ins->base()), useRegister(ins->index())), ins);
@@ -3777,24 +3721,6 @@ LIRGenerator::visitGuardReceiverPolymorphic(MGuardReceiverPolymorphic* ins)
}
void
-LIRGenerator::visitGuardUnboxedExpando(MGuardUnboxedExpando* ins)
-{
- LGuardUnboxedExpando* guard =
- new(alloc()) LGuardUnboxedExpando(useRegister(ins->object()));
- assignSnapshot(guard, ins->bailoutKind());
- add(guard, ins);
- redefine(ins, ins->object());
-}
-
-void
-LIRGenerator::visitLoadUnboxedExpando(MLoadUnboxedExpando* ins)
-{
- LLoadUnboxedExpando* lir =
- new(alloc()) LLoadUnboxedExpando(useRegisterAtStart(ins->object()));
- define(lir, ins);
-}
-
-void
LIRGenerator::visitAssertRange(MAssertRange* ins)
{
MDefinition* input = ins->input();
diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h
index 9b4095aec..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);
@@ -232,7 +227,6 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitFallibleStoreElement(MFallibleStoreElement* ins);
void visitStoreUnboxedObjectOrNull(MStoreUnboxedObjectOrNull* ins);
void visitStoreUnboxedString(MStoreUnboxedString* ins);
- void visitConvertUnboxedObjectToNative(MConvertUnboxedObjectToNative* ins);
void visitEffectiveAddress(MEffectiveAddress* ins);
void visitArrayPopShift(MArrayPopShift* ins);
void visitArrayPush(MArrayPush* ins);
@@ -256,8 +250,6 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitGuardObject(MGuardObject* ins);
void visitGuardString(MGuardString* ins);
void visitGuardReceiverPolymorphic(MGuardReceiverPolymorphic* ins);
- void visitGuardUnboxedExpando(MGuardUnboxedExpando* ins);
- void visitLoadUnboxedExpando(MLoadUnboxedExpando* ins);
void visitPolyInlineGuard(MPolyInlineGuard* ins);
void visitAssertRange(MAssertRange* ins);
void visitCallGetProperty(MCallGetProperty* ins);
diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp
index 736c6c892..236354530 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:
@@ -473,11 +470,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();
@@ -525,7 +517,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.
@@ -540,16 +532,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;
}
@@ -582,7 +573,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));
@@ -613,34 +604,27 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
OBJECT_FLAG_LENGTH_OVERFLOW |
OBJECT_FLAG_ITERATED;
- MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
+ MDefinition* obj = callInfo.thisArg();
TemporaryTypeSet* thisTypes = obj->resultTypeSet();
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);
@@ -651,8 +635,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);
@@ -667,46 +650,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()) {
@@ -741,7 +684,7 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo)
return InliningStatus_NotInlined;
}
- MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
+ MDefinition* obj = callInfo.thisArg();
MDefinition* value = callInfo.getArg(0);
if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
&obj, nullptr, &value, /* canModify = */ false))
@@ -759,16 +702,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;
}
@@ -780,13 +717,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 ||
@@ -797,13 +727,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);
@@ -820,7 +749,7 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo)
return InliningStatus_NotInlined;
}
- MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
+ MDefinition* obj = callInfo.thisArg();
// Ensure |this| and result are objects.
if (getInlineReturnType() != MIRType::Object)
@@ -844,24 +773,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;
}
@@ -880,15 +796,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();
@@ -907,16 +816,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);
@@ -1435,7 +1340,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;
@@ -1461,13 +1366,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)
@@ -1498,8 +1403,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.
@@ -1510,11 +1413,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;
@@ -2150,7 +2053,7 @@ IonBuilder::inlineDefineDataProperty(CallInfo& callInfo)
if (callInfo.argc() != 3)
return InliningStatus_NotInlined;
- MDefinition* obj = convertUnboxedObjects(callInfo.getArg(0));
+ MDefinition* obj = callInfo.getArg(0);
MDefinition* id = callInfo.getArg(1);
MDefinition* value = callInfo.getArg(2);
diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp
index 287b87582..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)
{
@@ -4810,67 +4776,31 @@ MBeta::printOpcode(GenericPrinter& out) const
bool
MCreateThisWithTemplate::canRecoverOnBailout() const
{
- MOZ_ASSERT(templateObject()->is<PlainObject>() || templateObject()->is<UnboxedPlainObject>());
- MOZ_ASSERT_IF(templateObject()->is<PlainObject>(),
- !templateObject()->as<PlainObject>().denseElementsAreCopyOnWrite());
- return true;
-}
-
-bool
-OperandIndexMap::init(TempAllocator& alloc, JSObject* templateObject)
-{
- const UnboxedLayout& layout =
- templateObject->as<UnboxedPlainObject>().layoutDontCheckGeneration();
-
- const UnboxedLayout::PropertyVector& properties = layout.properties();
- MOZ_ASSERT(properties.length() < 255);
-
- // Allocate an array of indexes, where the top of each field correspond to
- // the index of the operand in the MObjectState instance.
- if (!map.init(alloc, layout.size()))
- return false;
-
- // Reset all indexes to 0, which is an error code.
- for (size_t i = 0; i < map.length(); i++)
- map[i] = 0;
-
- // Map the property offsets to the indexes of MObjectState operands.
- uint8_t index = 1;
- for (size_t i = 0; i < properties.length(); i++, index++)
- map[properties[i].offset] = index;
-
+ MOZ_ASSERT(templateObject()->is<PlainObject>());
+ MOZ_ASSERT(!templateObject()->as<PlainObject>().denseElementsAreCopyOnWrite());
return true;
}
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);
setRecoveredOnBailout();
- if (templateObject->is<NativeObject>()) {
- NativeObject* nativeObject = &templateObject->as<NativeObject>();
- numSlots_ = nativeObject->slotSpan();
- numFixedSlots_ = nativeObject->numFixedSlots();
- } else {
- const UnboxedLayout& layout =
- templateObject->as<UnboxedPlainObject>().layoutDontCheckGeneration();
- // Same as UnboxedLayout::makeNativeGroup
- numSlots_ = layout.properties().length();
- numFixedSlots_ = gc::GetGCKindSlots(layout.getAllocKind());
- }
+ MOZ_ASSERT(templateObject->is<NativeObject>());
- operandIndex_ = operandIndex;
+ NativeObject* nativeObject = &templateObject->as<NativeObject>();
+ numSlots_ = nativeObject->slotSpan();
+ numFixedSlots_ = nativeObject->numFixedSlots();
}
JSObject*
@@ -4905,39 +4835,21 @@ MObjectState::initFromTemplateObject(TempAllocator& alloc, MDefinition* undefine
// the template object. This is needed to account values which are baked in
// the template objects and not visible in IonMonkey, such as the
// uninitialized-lexical magic value of call objects.
- if (templateObject->is<UnboxedPlainObject>()) {
- UnboxedPlainObject& unboxedObject = templateObject->as<UnboxedPlainObject>();
- const UnboxedLayout& layout = unboxedObject.layoutDontCheckGeneration();
- const UnboxedLayout::PropertyVector& properties = layout.properties();
-
- for (size_t i = 0; i < properties.length(); i++) {
- Value val = unboxedObject.getValue(properties[i], /* maybeUninitialized = */ true);
- MDefinition *def = undefinedVal;
- if (!val.isUndefined()) {
- MConstant* ins = val.isObject() ?
- MConstant::NewConstraintlessObject(alloc, &val.toObject()) :
- MConstant::New(alloc, val);
- block()->insertBefore(this, ins);
- def = ins;
- }
- initSlot(i, def);
- }
- } else {
- NativeObject& nativeObject = templateObject->as<NativeObject>();
- MOZ_ASSERT(nativeObject.slotSpan() == numSlots());
-
- for (size_t i = 0; i < numSlots(); i++) {
- Value val = nativeObject.getSlot(i);
- MDefinition *def = undefinedVal;
- if (!val.isUndefined()) {
- MConstant* ins = val.isObject() ?
- MConstant::NewConstraintlessObject(alloc, &val.toObject()) :
- MConstant::New(alloc, val);
- block()->insertBefore(this, ins);
- def = ins;
- }
- initSlot(i, def);
+ NativeObject& nativeObject = templateObject->as<NativeObject>();
+ MOZ_ASSERT(nativeObject.slotSpan() == numSlots());
+
+ MOZ_ASSERT(templateObject->is<NativeObject>());
+ for (size_t i = 0; i < numSlots(); i++) {
+ Value val = nativeObject.getSlot(i);
+ MDefinition *def = undefinedVal;
+ if (!val.isUndefined()) {
+ MConstant* ins = val.isObject() ?
+ MConstant::NewConstraintlessObject(alloc, &val.toObject()) :
+ MConstant::New(alloc, val);
+ block()->insertBefore(this, ins);
+ def = ins;
}
+ initSlot(i, def);
}
return true;
}
@@ -4948,14 +4860,7 @@ MObjectState::New(TempAllocator& alloc, MDefinition* obj)
JSObject* templateObject = templateObjectOf(obj);
MOZ_ASSERT(templateObject, "Unexpected object creation.");
- OperandIndexMap* operandIndex = nullptr;
- if (templateObject->is<UnboxedPlainObject>()) {
- operandIndex = new(alloc) OperandIndexMap;
- if (!operandIndex || !operandIndex->init(alloc, templateObject))
- return nullptr;
- }
-
- MObjectState* res = new(alloc) MObjectState(templateObject, operandIndex);
+ MObjectState* res = new(alloc) MObjectState(templateObject);
if (!res || !res->init(alloc, obj))
return nullptr;
return res;
@@ -5862,35 +5767,6 @@ MGetFirstDollarIndex::foldsTo(TempAllocator& alloc)
return MConstant::New(alloc, Int32Value(index));
}
-MConvertUnboxedObjectToNative*
-MConvertUnboxedObjectToNative::New(TempAllocator& alloc, MDefinition* obj, ObjectGroup* group)
-{
- MConvertUnboxedObjectToNative* res = new(alloc) MConvertUnboxedObjectToNative(obj, group);
-
- ObjectGroup* nativeGroup = group->unboxedLayout().nativeGroup();
-
- // Make a new type set for the result of this instruction which replaces
- // the input group with the native group we will convert it to.
- TemporaryTypeSet* types = obj->resultTypeSet();
- if (types && !types->unknownObject()) {
- TemporaryTypeSet* newTypes = types->cloneWithoutObjects(alloc.lifoAlloc());
- if (newTypes) {
- for (size_t i = 0; i < types->getObjectCount(); i++) {
- TypeSet::ObjectKey* key = types->getObject(i);
- if (!key)
- continue;
- if (key->unknownProperties() || !key->isGroup() || key->group() != group)
- newTypes->addType(TypeSet::ObjectType(key), alloc.lifoAlloc());
- else
- newTypes->addType(TypeSet::ObjectType(nativeGroup), alloc.lifoAlloc());
- }
- res->setResultTypeSet(newTypes);
- }
- }
-
- return res;
-}
-
bool
jit::ElementAccessIsDenseNative(CompilerConstraintList* constraints,
MDefinition* obj, MDefinition* id)
@@ -5910,48 +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;
-
- key->watchStateChangeForUnboxedConvertedToNative(constraints);
- }
-
- return elementType;
-}
-
bool
jit::ElementAccessIsTypedArray(CompilerConstraintList* constraints,
MDefinition* obj, MDefinition* id,
@@ -6111,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;
}
@@ -6355,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)
@@ -6579,23 +6399,6 @@ jit::PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList*
}
}
- // Perform additional filtering to make sure that any unboxed property
- // being written can accommodate the value.
- for (size_t i = 0; i < types->getObjectCount(); i++) {
- TypeSet::ObjectKey* key = types->getObject(i);
- if (key && key->isGroup() && key->group()->maybeUnboxedLayout()) {
- const UnboxedLayout& layout = key->group()->unboxedLayout();
- if (name) {
- const UnboxedLayout::Property* property = layout.lookup(name);
- if (property && !CanStoreUnboxedType(alloc, property->type, *pvalue))
- return true;
- } else {
- if (layout.isArray() && !CanStoreUnboxedType(alloc, layout.elementType(), *pvalue))
- return true;
- }
- }
- }
-
if (success)
return false;
@@ -6626,17 +6429,6 @@ jit::PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList*
MOZ_ASSERT(excluded);
- // If the excluded object is a group with an unboxed layout, make sure it
- // does not have a corresponding native group. Objects with the native
- // group might appear even though they are not in the type set.
- if (excluded->isGroup()) {
- if (UnboxedLayout* layout = excluded->group()->maybeUnboxedLayout()) {
- if (layout->nativeGroup())
- return true;
- excluded->watchStateChangeForUnboxedConvertedToNative(constraints);
- }
- }
-
*pobj = AddGroupGuard(alloc, current, *pobj, excluded, /* bailOnEquality = */ true);
return false;
}
diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h
index 6ec05af76..6c376d528 100644
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -30,7 +30,6 @@
#include "vm/EnvironmentObject.h"
#include "vm/SharedMem.h"
#include "vm/TypedArrayCommon.h"
-#include "vm/UnboxedObject.h"
// Undo windows.h damage on Win64
#undef MemoryBarrier
@@ -376,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
@@ -434,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;
@@ -3763,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);
@@ -3795,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 {
@@ -3831,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;
@@ -4066,14 +4043,18 @@ class MCall
uint32_t numActualArgs_;
// True if the call is for JSOP_NEW.
- bool construct_;
+ bool construct_:1;
- bool needsArgCheck_;
+ // True if the caller does not use the return value.
+ bool ignoresReturnValue_:1;
- MCall(WrappedFunction* target, uint32_t numActualArgs, bool construct)
+ bool needsArgCheck_:1;
+
+ MCall(WrappedFunction* target, uint32_t numActualArgs, bool construct, bool ignoresReturnValue)
: target_(target),
numActualArgs_(numActualArgs),
construct_(construct),
+ ignoresReturnValue_(ignoresReturnValue),
needsArgCheck_(true)
{
setResultType(MIRType::Value);
@@ -4082,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);
@@ -4127,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.
@@ -4170,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);
@@ -4185,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:
@@ -4200,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>,
@@ -8272,7 +8237,10 @@ class MGetFirstDollarIndex
: MUnaryInstruction(str)
{
setResultType(MIRType::Int32);
- setMovable();
+
+ // Codegen assumes string length > 0 but that's not guaranteed in RegExp.
+ // Don't allow LICM to move this.
+ MOZ_ASSERT(!isMovable());
}
public:
@@ -8743,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,
@@ -9332,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)
{
@@ -9370,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_;
}
@@ -9383,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())
@@ -9392,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;
@@ -9572,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);
@@ -9600,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)
@@ -9620,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);
@@ -9642,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_;
@@ -9739,59 +9589,6 @@ class MStoreUnboxedString
ALLOW_CLONE(MStoreUnboxedString)
};
-// Passes through an object, after ensuring it is converted from an unboxed
-// object to a native representation.
-class MConvertUnboxedObjectToNative
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- CompilerObjectGroup group_;
-
- explicit MConvertUnboxedObjectToNative(MDefinition* obj, ObjectGroup* group)
- : MUnaryInstruction(obj),
- group_(group)
- {
- setGuard();
- setMovable();
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(ConvertUnboxedObjectToNative)
- NAMED_OPERANDS((0, object))
-
- static MConvertUnboxedObjectToNative* New(TempAllocator& alloc, MDefinition* obj,
- ObjectGroup* group);
-
- ObjectGroup* group() const {
- return group_;
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!congruentIfOperandsEqual(ins))
- return false;
- return ins->toConvertUnboxedObjectToNative()->group() == group();
- }
- AliasSet getAliasSet() const override {
- // This instruction can read and write to all parts of the object, but
- // is marked as non-effectful so it can be consolidated by LICM and GVN
- // and avoid inhibiting other optimizations.
- //
- // This is valid to do because when unboxed objects might have a native
- // group they can be converted to, we do not optimize accesses to the
- // unboxed objects and do not guard on their group or shape (other than
- // in this opcode).
- //
- // Later accesses can assume the object has a native representation
- // and optimize accordingly. Those accesses cannot be reordered before
- // this instruction, however. This is prevented by chaining this
- // instruction with the object itself, in the same way as MBoundsCheck.
- return AliasSet::None();
- }
- bool appendRoots(MRootList& roots) const override {
- return roots.append(group_);
- }
-};
-
// Array.prototype.pop or Array.prototype.shift on a dense array.
class MArrayPopShift
: public MUnaryInstruction,
@@ -9805,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)
{ }
@@ -9829,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)
@@ -9845,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);
}
@@ -9858,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;
@@ -9877,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);
}
@@ -9903,14 +9687,6 @@ class MArraySlice
return initialHeap_;
}
- JSValueType unboxedType() const {
- return unboxedType_;
- }
-
- AliasSet getAliasSet() const override {
- return AliasSet::Store(AliasSet::BoxedOrUnboxedElements(unboxedType()) |
- AliasSet::ObjectFields);
- }
bool possiblyCalls() const override {
return true;
}
@@ -11175,11 +10951,6 @@ class MGuardShape
setMovable();
setResultType(MIRType::Object);
setResultTypeSet(obj->resultTypeSet());
-
- // Disallow guarding on unboxed object shapes. The group is better to
- // guard on, and guarding on the shape can interact badly with
- // MConvertUnboxedObjectToNative.
- MOZ_ASSERT(shape->getObjectClass() != &UnboxedPlainObject::class_);
}
public:
@@ -11274,11 +11045,6 @@ class MGuardObjectGroup
setGuard();
setMovable();
setResultType(MIRType::Object);
-
- // Unboxed groups which might be converted to natives can't be guarded
- // on, due to MConvertUnboxedObjectToNative.
- MOZ_ASSERT_IF(group->maybeUnboxedLayoutDontCheckGeneration(),
- !group->unboxedLayoutDontCheckGeneration().nativeGroup());
}
public:
@@ -11387,73 +11153,6 @@ class MGuardClass
ALLOW_CLONE(MGuardClass)
};
-// Guard on the presence or absence of an unboxed object's expando.
-class MGuardUnboxedExpando
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- bool requireExpando_;
- BailoutKind bailoutKind_;
-
- MGuardUnboxedExpando(MDefinition* obj, bool requireExpando, BailoutKind bailoutKind)
- : MUnaryInstruction(obj),
- requireExpando_(requireExpando),
- bailoutKind_(bailoutKind)
- {
- setGuard();
- setMovable();
- setResultType(MIRType::Object);
- }
-
- public:
- INSTRUCTION_HEADER(GuardUnboxedExpando)
- TRIVIAL_NEW_WRAPPERS
- NAMED_OPERANDS((0, object))
-
- bool requireExpando() const {
- return requireExpando_;
- }
- BailoutKind bailoutKind() const {
- return bailoutKind_;
- }
- bool congruentTo(const MDefinition* ins) const override {
- if (!congruentIfOperandsEqual(ins))
- return false;
- if (requireExpando() != ins->toGuardUnboxedExpando()->requireExpando())
- return false;
- return true;
- }
- AliasSet getAliasSet() const override {
- return AliasSet::Load(AliasSet::ObjectFields);
- }
-};
-
-// Load an unboxed plain object's expando.
-class MLoadUnboxedExpando
- : public MUnaryInstruction,
- public SingleObjectPolicy::Data
-{
- private:
- explicit MLoadUnboxedExpando(MDefinition* object)
- : MUnaryInstruction(object)
- {
- setResultType(MIRType::Object);
- setMovable();
- }
-
- public:
- INSTRUCTION_HEADER(LoadUnboxedExpando)
- 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);
- }
-};
-
// Load from vp[slot] (slots that are not inline in an object).
class MLoadSlot
: public MUnaryInstruction,
@@ -11834,7 +11533,8 @@ class MCallGetProperty
AliasSet getAliasSet() const override {
if (!idempotent_)
return AliasSet::Store(AliasSet::Any);
- return AliasSet::None();
+ return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot |
+ AliasSet::DynamicSlot);
}
bool possiblyCalls() const override {
return true;
@@ -12355,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();
@@ -12383,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);
@@ -12398,8 +12093,6 @@ class MInArray
return false;
if (needsNegativeIntCheck() != other->needsNegativeIntCheck())
return false;
- if (unboxedType() != other->unboxedType())
- return false;
return congruentIfOperandsEqual(other);
}
};
@@ -14300,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);
@@ -14333,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 fddc1e637..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) \
@@ -187,8 +186,6 @@ namespace jit {
_(GuardObjectGroup) \
_(GuardObjectIdentity) \
_(GuardClass) \
- _(GuardUnboxedExpando) \
- _(LoadUnboxedExpando) \
_(ArrayLength) \
_(SetArrayLength) \
_(GetNextEntryForIterator) \
@@ -200,10 +197,6 @@ namespace jit {
_(SetTypedObjectOffset) \
_(InitializedLength) \
_(SetInitializedLength) \
- _(UnboxedArrayLength) \
- _(UnboxedArrayInitializedLength) \
- _(IncrementUnboxedArrayInitializedLength) \
- _(SetUnboxedArrayInitializedLength) \
_(Not) \
_(BoundsCheck) \
_(BoundsCheckLower) \
@@ -219,7 +212,6 @@ namespace jit {
_(StoreUnboxedScalar) \
_(StoreUnboxedObjectOrNull) \
_(StoreUnboxedString) \
- _(ConvertUnboxedObjectToNative) \
_(ArrayPopShift) \
_(ArrayPush) \
_(ArraySlice) \
diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp
index 9dbbe7624..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)
{
@@ -2214,12 +1909,6 @@ MacroAssembler::finish()
}
MacroAssemblerSpecific::finish();
-
- MOZ_RELEASE_ASSERT(size() <= MaxCodeBytesPerProcess,
- "AssemblerBuffer should ensure we don't exceed MaxCodeBytesPerProcess");
-
- if (bytesNeeded() > MaxCodeBytesPerProcess)
- setOOM();
}
void
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 308def041..7d72795a0 100644
--- a/js/src/jit/OptimizationTracking.cpp
+++ b/js/src/jit/OptimizationTracking.cpp
@@ -844,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/ProcessExecutableMemory.cpp b/js/src/jit/ProcessExecutableMemory.cpp
index 301541541..71c2ab0dc 100644
--- a/js/src/jit/ProcessExecutableMemory.cpp
+++ b/js/src/jit/ProcessExecutableMemory.cpp
@@ -385,6 +385,14 @@ class PageBitSet
#endif
};
+// Limit on the number of bytes of executable memory to prevent JIT spraying
+// attacks.
+#if JS_BITS_PER_WORD == 32
+static const size_t MaxCodeBytesPerProcess = 128 * 1024 * 1024;
+#else
+static const size_t MaxCodeBytesPerProcess = 1 * 1024 * 1024 * 1024;
+#endif
+
// Per-process executable memory allocator. It reserves a block of memory of
// MaxCodeBytesPerProcess bytes, then allocates/deallocates pages from that.
//
diff --git a/js/src/jit/ProcessExecutableMemory.h b/js/src/jit/ProcessExecutableMemory.h
index a0e2fab98..078ce7cb7 100644
--- a/js/src/jit/ProcessExecutableMemory.h
+++ b/js/src/jit/ProcessExecutableMemory.h
@@ -17,14 +17,6 @@ namespace jit {
// alignment though.
static const size_t ExecutableCodePageSize = 64 * 1024;
-// Limit on the number of bytes of executable memory to prevent JIT spraying
-// attacks.
-#if JS_BITS_PER_WORD == 32
-static const size_t MaxCodeBytesPerProcess = 128 * 1024 * 1024;
-#else
-static const size_t MaxCodeBytesPerProcess = 1 * 1024 * 1024 * 1024;
-#endif
-
enum class ProtectionSetting {
Protected, // Not readable, writable, or executable.
Writable,
diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp
index 95484c249..d64f9b8ca 100644
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -2167,7 +2167,7 @@ RangeAnalysis::analyzeLoopPhi(MBasicBlock* header, LoopIterationBound* loopBound
if (initial->block()->isMarked())
return;
- SimpleLinearSum modified = ExtractLinearSum(phi->getLoopBackedgeOperand());
+ SimpleLinearSum modified = ExtractLinearSum(phi->getLoopBackedgeOperand(), MathSpace::Infinite);
if (modified.term != phi || modified.constant == 0)
return;
diff --git a/js/src/jit/Recover.cpp b/js/src/jit/Recover.cpp
index 13bf9224b..793b631df 100644
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -1354,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;
@@ -1539,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 4614b2162..97ba52349 100644
--- a/js/src/jit/ScalarReplacement.cpp
+++ b/js/src/jit/ScalarReplacement.cpp
@@ -13,7 +13,6 @@
#include "jit/MIR.h"
#include "jit/MIRGenerator.h"
#include "jit/MIRGraph.h"
-#include "vm/UnboxedObject.h"
#include "jsobjinlines.h"
@@ -183,25 +182,6 @@ IsObjectEscaped(MInstruction* ins, JSObject* objDefault)
JitSpewDef(JitSpew_Escape, "is escaped by\n", def);
return true;
- case MDefinition::Op_LoadUnboxedScalar:
- case MDefinition::Op_StoreUnboxedScalar:
- case MDefinition::Op_LoadUnboxedObjectOrNull:
- case MDefinition::Op_StoreUnboxedObjectOrNull:
- case MDefinition::Op_LoadUnboxedString:
- case MDefinition::Op_StoreUnboxedString:
- // Not escaped if it is the first argument.
- if (def->indexOf(*i) != 0) {
- JitSpewDef(JitSpew_Escape, "is escaped by\n", def);
- return true;
- }
-
- if (!def->getOperand(1)->isConstant()) {
- JitSpewDef(JitSpew_Escape, "is addressed with unknown index\n", def);
- return true;
- }
-
- break;
-
case MDefinition::Op_PostWriteBarrier:
break;
@@ -305,16 +285,6 @@ class ObjectMemoryView : public MDefinitionVisitorDefaultNoop
void visitGuardShape(MGuardShape* ins);
void visitFunctionEnvironment(MFunctionEnvironment* ins);
void visitLambda(MLambda* ins);
- void visitStoreUnboxedScalar(MStoreUnboxedScalar* ins);
- void visitLoadUnboxedScalar(MLoadUnboxedScalar* ins);
- void visitStoreUnboxedObjectOrNull(MStoreUnboxedObjectOrNull* ins);
- void visitLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull* ins);
- void visitStoreUnboxedString(MStoreUnboxedString* ins);
- void visitLoadUnboxedString(MLoadUnboxedString* 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";
@@ -656,121 +626,6 @@ ObjectMemoryView::visitLambda(MLambda* ins)
ins->setIncompleteObject();
}
-static size_t
-GetOffsetOf(MDefinition* index, size_t width, int32_t baseOffset)
-{
- int32_t idx = index->toConstant()->toInt32();
- MOZ_ASSERT(idx >= 0);
- MOZ_ASSERT(baseOffset >= 0 && size_t(baseOffset) >= UnboxedPlainObject::offsetOfData());
- return idx * width + baseOffset - UnboxedPlainObject::offsetOfData();
-}
-
-static size_t
-GetOffsetOf(MDefinition* index, Scalar::Type type, int32_t baseOffset)
-{
- return GetOffsetOf(index, Scalar::byteSize(type), baseOffset);
-}
-
-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);
-}
-
-void
-ObjectMemoryView::visitStoreUnboxedScalar(MStoreUnboxedScalar* ins)
-{
- // Skip stores made on other objects.
- if (ins->elements() != obj_)
- return;
-
- size_t offset = GetOffsetOf(ins->index(), ins->storageType(), ins->offsetAdjustment());
- storeOffset(ins, offset, ins->value());
-}
-
-void
-ObjectMemoryView::visitLoadUnboxedScalar(MLoadUnboxedScalar* ins)
-{
- // Skip loads made on other objects.
- if (ins->elements() != obj_)
- return;
-
- // Replace load by the slot value.
- size_t offset = GetOffsetOf(ins->index(), ins->storageType(), ins->offsetAdjustment());
- loadOffset(ins, offset);
-}
-
-void
-ObjectMemoryView::visitStoreUnboxedObjectOrNull(MStoreUnboxedObjectOrNull* ins)
-{
- // Skip stores made on other objects.
- if (ins->elements() != obj_)
- return;
-
- // Clone the state and update the slot value.
- size_t offset = GetOffsetOf(ins->index(), sizeof(uintptr_t), ins->offsetAdjustment());
- storeOffset(ins, offset, ins->value());
-}
-
-void
-ObjectMemoryView::visitLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull* ins)
-{
- // Skip loads made on other objects.
- if (ins->elements() != obj_)
- return;
-
- // Replace load by the slot value.
- size_t offset = GetOffsetOf(ins->index(), sizeof(uintptr_t), ins->offsetAdjustment());
- loadOffset(ins, offset);
-}
-
-void
-ObjectMemoryView::visitStoreUnboxedString(MStoreUnboxedString* ins)
-{
- // Skip stores made on other objects.
- if (ins->elements() != obj_)
- return;
-
- // Clone the state and update the slot value.
- size_t offset = GetOffsetOf(ins->index(), sizeof(uintptr_t), ins->offsetAdjustment());
- storeOffset(ins, offset, ins->value());
-}
-
-void
-ObjectMemoryView::visitLoadUnboxedString(MLoadUnboxedString* ins)
-{
- // Skip loads made on other objects.
- if (ins->elements() != obj_)
- return;
-
- // Replace load by the slot value.
- size_t offset = GetOffsetOf(ins->index(), sizeof(uintptr_t), ins->offsetAdjustment());
- loadOffset(ins, offset);
-}
-
static bool
IndexOf(MDefinition* ins, int32_t* res)
{
@@ -907,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 767cff661..05a95824f 100644
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -27,6 +27,7 @@
#endif
#include "jit/VMFunctions.h"
#include "vm/Interpreter.h"
+#include "vm/NativeObject-inl.h"
#include "jit/MacroAssembler-inl.h"
#include "vm/Interpreter-inl.h"
@@ -285,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");
@@ -2248,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;
}
@@ -2578,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;
@@ -2848,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);
}
}
@@ -4259,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/StupidAllocator.cpp b/js/src/jit/StupidAllocator.cpp
index 8e3ea6286..55431e8e0 100644
--- a/js/src/jit/StupidAllocator.cpp
+++ b/js/src/jit/StupidAllocator.cpp
@@ -407,7 +407,6 @@ StupidAllocator::allocateForDefinition(LInstruction* ins, LDefinition* def)
{
uint32_t vreg = def->virtualRegister();
- CodePosition from;
if ((def->output()->isRegister() && def->policy() == LDefinition::FIXED) ||
def->policy() == LDefinition::MUST_REUSE_INPUT)
{
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/arm/Assembler-arm.cpp b/js/src/jit/arm/Assembler-arm.cpp
index 2830f0695..1e20da1c8 100644
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -2401,7 +2401,12 @@ Assembler::as_b(Label* l, Condition c)
if (oom())
return BufferOffset();
- as_b(BufferOffset(l).diffB<BOffImm>(ret), c, ret);
+ BOffImm off = BufferOffset(l).diffB<BOffImm>(ret);
+ if (off.isInvalid()) {
+ m_buffer.fail_bail();
+ return BufferOffset();
+ }
+ as_b(off, c, ret);
#ifdef JS_DISASM_ARM
spewBranch(m_buffer.getInstOrNull(ret), l);
#endif
diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp
index d40578514..3421001f7 100644
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -3462,8 +3462,8 @@ MacroAssemblerARMCompat::storePayload(const Value& val, const Address& dest)
ScratchRegisterScope scratch(asMasm());
SecondScratchRegisterScope scratch2(asMasm());
- if (val.isMarkable())
- ma_mov(ImmGCPtr(val.toMarkablePointer()), scratch);
+ if (val.isGCThing())
+ ma_mov(ImmGCPtr(val.toGCThing()), scratch);
else
ma_mov(Imm32(val.toNunboxPayload()), scratch);
ma_str(scratch, ToPayload(dest), scratch2);
@@ -5012,7 +5012,10 @@ void
MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset)
{
BufferOffset inst(callerOffset - 4);
- as_bl(BufferOffset(calleeOffset).diffB<BOffImm>(inst), Always, inst);
+ BOffImm off = BufferOffset(calleeOffset).diffB<BOffImm>(inst);
+ MOZ_RELEASE_ASSERT(!off.isInvalid(),
+ "Failed to insert necessary far jump islands");
+ as_bl(off, Always, inst);
}
CodeOffset
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/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h
index aac9687b8..8044e75cb 100644
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -7,8 +7,6 @@
#ifndef jit_shared_Assembler_shared_h
#define jit_shared_Assembler_shared_h
-#include "mozilla/PodOperations.h"
-
#include <limits.h>
#include "jit/AtomicOp.h"
@@ -491,10 +489,10 @@ class CodeLabel
class CodeOffsetJump
{
- size_t offset_;
+ size_t offset_ = 0;
#ifdef JS_SMALL_BRANCH
- size_t jumpTableIndex_;
+ size_t jumpTableIndex_ = 0;
#endif
public:
@@ -510,9 +508,7 @@ class CodeOffsetJump
explicit CodeOffsetJump(size_t offset) : offset_(offset) {}
#endif
- CodeOffsetJump() {
- mozilla::PodZero(this);
- }
+ CodeOffsetJump() = default;
size_t offset() const {
return offset_;
diff --git a/js/src/jit/shared/IonAssemblerBuffer.h b/js/src/jit/shared/IonAssemblerBuffer.h
index 3a6552696..cc20e26d2 100644
--- a/js/src/jit/shared/IonAssemblerBuffer.h
+++ b/js/src/jit/shared/IonAssemblerBuffer.h
@@ -181,10 +181,6 @@ class AssemblerBuffer
protected:
virtual Slice* newSlice(LifoAlloc& a) {
- if (size() > MaxCodeBytesPerProcess - sizeof(Slice)) {
- fail_oom();
- return nullptr;
- }
Slice* tmp = static_cast<Slice*>(a.alloc(sizeof(Slice)));
if (!tmp) {
fail_oom();
diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h
index f4adcc63c..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 {
@@ -5891,22 +5792,6 @@ class LStoreUnboxedPointer : public LInstructionHelper<0, 3, 0>
}
};
-// If necessary, convert an unboxed object in a particular group to its native
-// representation.
-class LConvertUnboxedObjectToNative : public LInstructionHelper<0, 1, 0>
-{
- public:
- LIR_HEADER(ConvertUnboxedObjectToNative)
-
- explicit LConvertUnboxedObjectToNative(const LAllocation& object) {
- setOperand(0, object);
- }
-
- MConvertUnboxedObjectToNative* mir() {
- return mir_->toConvertUnboxedObjectToNative();
- }
-};
-
class LArrayPopShiftV : public LInstructionHelper<BOX_PIECES, 1, 2>
{
public:
@@ -7429,38 +7314,6 @@ class LGuardReceiverPolymorphic : public LInstructionHelper<0, 1, 1>
}
};
-class LGuardUnboxedExpando : public LInstructionHelper<0, 1, 0>
-{
- public:
- LIR_HEADER(GuardUnboxedExpando)
-
- explicit LGuardUnboxedExpando(const LAllocation& in) {
- setOperand(0, in);
- }
- const LAllocation* object() {
- return getOperand(0);
- }
- const MGuardUnboxedExpando* mir() const {
- return mir_->toGuardUnboxedExpando();
- }
-};
-
-class LLoadUnboxedExpando : public LInstructionHelper<1, 1, 0>
-{
- public:
- LIR_HEADER(LoadUnboxedExpando)
-
- explicit LLoadUnboxedExpando(const LAllocation& in) {
- setOperand(0, in);
- }
- const LAllocation* object() {
- return getOperand(0);
- }
- const MLoadUnboxedExpando* mir() const {
- return mir_->toLoadUnboxedExpando();
- }
-};
-
// Guard that a value is in a TypeSet.
class LTypeBarrierV : public LInstructionHelper<0, BOX_PIECES, 1>
{
diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h
index fe2ab5ea3..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) \
@@ -258,8 +257,6 @@
_(GuardObjectGroup) \
_(GuardObjectIdentity) \
_(GuardClass) \
- _(GuardUnboxedExpando) \
- _(LoadUnboxedExpando) \
_(TypeBarrierV) \
_(TypeBarrierO) \
_(MonitorTypes) \
@@ -269,10 +266,6 @@
_(PostWriteElementBarrierV) \
_(InitializedLength) \
_(SetInitializedLength) \
- _(UnboxedArrayLength) \
- _(UnboxedArrayInitializedLength) \
- _(IncrementUnboxedArrayInitializedLength) \
- _(SetUnboxedArrayInitializedLength) \
_(BoundsCheck) \
_(BoundsCheckRange) \
_(BoundsCheckLower) \
@@ -287,7 +280,6 @@
_(StoreElementT) \
_(StoreUnboxedScalar) \
_(StoreUnboxedPointer) \
- _(ConvertUnboxedObjectToNative) \
_(ArrayPopShiftV) \
_(ArrayPopShiftT) \
_(ArrayPushV) \
diff --git a/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h b/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h
index fe678fc7d..8343579c8 100644
--- a/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h
+++ b/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h
@@ -68,33 +68,6 @@ namespace js {
namespace jit {
- // AllocPolicy for AssemblerBuffer. OOMs when trying to allocate more than
- // MaxCodeBytesPerProcess bytes. Use private inheritance to make sure we
- // explicitly have to expose SystemAllocPolicy methods.
- class AssemblerBufferAllocPolicy : private SystemAllocPolicy
- {
- public:
- using SystemAllocPolicy::checkSimulatedOOM;
- using SystemAllocPolicy::reportAllocOverflow;
- using SystemAllocPolicy::free_;
-
- template <typename T> T* pod_realloc(T* p, size_t oldSize, size_t newSize) {
- static_assert(sizeof(T) == 1,
- "AssemblerBufferAllocPolicy should only be used with byte vectors");
- MOZ_ASSERT(oldSize <= MaxCodeBytesPerProcess);
- if (MOZ_UNLIKELY(newSize > MaxCodeBytesPerProcess))
- return nullptr;
- return SystemAllocPolicy::pod_realloc<T>(p, oldSize, newSize);
- }
- template <typename T> T* pod_malloc(size_t numElems) {
- static_assert(sizeof(T) == 1,
- "AssemblerBufferAllocPolicy should only be used with byte vectors");
- if (MOZ_UNLIKELY(numElems > MaxCodeBytesPerProcess))
- return nullptr;
- return SystemAllocPolicy::pod_malloc<T>(numElems);
- }
- };
-
class AssemblerBuffer
{
template<size_t size, typename T>
@@ -120,10 +93,8 @@ namespace jit {
void ensureSpace(size_t space)
{
- // This should only be called with small |space| values to ensure
- // we don't overflow below.
- MOZ_ASSERT(space <= 16);
- if (MOZ_UNLIKELY(!m_buffer.reserve(m_buffer.length() + space)))
+ if (MOZ_UNLIKELY(m_buffer.length() > (SIZE_MAX - space) ||
+ !m_buffer.reserve(m_buffer.length() + space)))
oomDetected();
}
@@ -198,7 +169,7 @@ namespace jit {
m_buffer.clear();
}
- PageProtectingVector<unsigned char, 256, AssemblerBufferAllocPolicy> m_buffer;
+ PageProtectingVector<unsigned char, 256, SystemAllocPolicy> m_buffer;
bool m_oom;
};
diff --git a/js/src/jit/x86/Assembler-x86.h b/js/src/jit/x86/Assembler-x86.h
index 3fb5efaff..5939583d9 100644
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -421,20 +421,11 @@ class Assembler : public AssemblerX86Shared
MOZ_ASSERT(dest.size() == 16);
masm.vhaddpd_rr(src.encoding(), dest.encoding());
}
- void vsubpd(const Operand& src1, FloatRegister src0, FloatRegister dest) {
+ void vsubpd(FloatRegister src1, FloatRegister src0, FloatRegister dest) {
MOZ_ASSERT(HasSSE2());
MOZ_ASSERT(src0.size() == 16);
MOZ_ASSERT(dest.size() == 16);
- switch (src1.kind()) {
- case Operand::MEM_REG_DISP:
- masm.vsubpd_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding());
- break;
- case Operand::MEM_ADDRESS32:
- masm.vsubpd_mr(src1.address(), src0.encoding(), dest.encoding());
- break;
- default:
- MOZ_CRASH("unexpected operand kind");
- }
+ masm.vsubpd_rr(src1.encoding(), src0.encoding(), dest.encoding());
}
void vpunpckldq(FloatRegister src1, FloatRegister src0, FloatRegister dest) {
diff --git a/js/src/jit/x86/BaseAssembler-x86.h b/js/src/jit/x86/BaseAssembler-x86.h
index 5b16311d0..caaef3f82 100644
--- a/js/src/jit/x86/BaseAssembler-x86.h
+++ b/js/src/jit/x86/BaseAssembler-x86.h
@@ -152,14 +152,6 @@ class BaseAssemblerX86 : public BaseAssembler
{
twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, src1, src0, dst);
}
- void vsubpd_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst)
- {
- twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, offset, base, src0, dst);
- }
- void vsubpd_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst)
- {
- twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, address, src0, dst);
- }
void vpunpckldq_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) {
twoByteOpSimd("vpunpckldq", VEX_PD, OP2_PUNPCKLDQ, src1, src0, dst);
diff --git a/js/src/jit/x86/MacroAssembler-x86.cpp b/js/src/jit/x86/MacroAssembler-x86.cpp
index dc97b5b5b..429a71fa9 100644
--- a/js/src/jit/x86/MacroAssembler-x86.cpp
+++ b/js/src/jit/x86/MacroAssembler-x86.cpp
@@ -21,15 +21,6 @@
using namespace js;
using namespace js::jit;
-// vpunpckldq requires 16-byte boundary for memory operand.
-// See convertUInt64ToDouble for the details.
-MOZ_ALIGNED_DECL(static const uint64_t, 16) TO_DOUBLE[4] = {
- 0x4530000043300000LL,
- 0x0LL,
- 0x4330000000000000LL,
- 0x4530000000000000LL
-};
-
static const double TO_DOUBLE_HIGH_SCALE = 0x100000000;
bool
@@ -90,8 +81,16 @@ MacroAssemblerX86::convertUInt64ToDouble(Register64 src, FloatRegister dest, Reg
// here, each 64-bit part of dest represents following double:
// HI(dest) = 0x 1.00000HHHHHHHH * 2**84 == 2**84 + 0x HHHHHHHH 00000000
// LO(dest) = 0x 1.00000LLLLLLLL * 2**52 == 2**52 + 0x 00000000 LLLLLLLL
- movePtr(ImmWord((uintptr_t)TO_DOUBLE), temp);
- vpunpckldq(Operand(temp, 0), dest128, dest128);
+ // See convertUInt64ToDouble for the details.
+ static const int32_t CST1[4] = {
+ 0x43300000,
+ 0x45300000,
+ 0x0,
+ 0x0,
+ };
+
+ loadConstantSimd128Int(SimdConstant::CreateX4(CST1), ScratchSimd128Reg);
+ vpunpckldq(ScratchSimd128Reg, dest128, dest128);
// Subtract a constant C2 from dest, for each 64-bit part:
// C2 = 0x 45300000 00000000 43300000 00000000
@@ -101,7 +100,15 @@ MacroAssemblerX86::convertUInt64ToDouble(Register64 src, FloatRegister dest, Reg
// after the operation each 64-bit part of dest represents following:
// HI(dest) = double(0x HHHHHHHH 00000000)
// LO(dest) = double(0x 00000000 LLLLLLLL)
- vsubpd(Operand(temp, sizeof(uint64_t) * 2), dest128, dest128);
+ static const int32_t CST2[4] = {
+ 0x0,
+ 0x43300000,
+ 0x0,
+ 0x45300000,
+ };
+
+ loadConstantSimd128Int(SimdConstant::CreateX4(CST2), ScratchSimd128Reg);
+ vsubpd(ScratchSimd128Reg, dest128, dest128);
// Add HI(dest) and LO(dest) in double and store it into LO(dest),
// LO(dest) = double(0x HHHHHHHH 00000000) + double(0x 00000000 LLLLLLLL)
diff --git a/js/src/js.msg b/js/src/js.msg
index a276dab94..1612c831d 100644
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -47,7 +47,6 @@ MSG_DEF(JSMSG_MORE_ARGS_NEEDED, 3, JSEXN_TYPEERR, "{0} requires more than
MSG_DEF(JSMSG_INCOMPATIBLE_PROTO, 3, JSEXN_TYPEERR, "{0}.prototype.{1} called on incompatible {2}")
MSG_DEF(JSMSG_NO_CONSTRUCTOR, 1, JSEXN_TYPEERR, "{0} has no constructor")
MSG_DEF(JSMSG_BAD_SORT_ARG, 0, JSEXN_TYPEERR, "invalid Array.prototype.sort argument")
-MSG_DEF(JSMSG_CANT_WATCH, 1, JSEXN_TYPEERR, "can't watch non-native objects of class {0}")
MSG_DEF(JSMSG_READ_ONLY, 1, JSEXN_TYPEERR, "{0} is read-only")
MSG_DEF(JSMSG_CANT_DELETE, 1, JSEXN_TYPEERR, "property {0} is non-configurable and can't be deleted")
MSG_DEF(JSMSG_CANT_TRUNCATE_ARRAY, 0, JSEXN_TYPEERR, "can't delete non-configurable array element")
@@ -63,6 +62,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}")
@@ -71,7 +71,6 @@ MSG_DEF(JSMSG_UNDEFINED_PROP, 1, JSEXN_REFERENCEERR, "reference to unde
MSG_DEF(JSMSG_INVALID_MAP_ITERABLE, 1, JSEXN_TYPEERR, "iterable for {0} should have array-like objects")
MSG_DEF(JSMSG_NESTING_GENERATOR, 0, JSEXN_TYPEERR, "already executing generator")
MSG_DEF(JSMSG_INCOMPATIBLE_METHOD, 3, JSEXN_TYPEERR, "{0} {1} called on incompatible {2}")
-MSG_DEF(JSMSG_OBJECT_WATCH_DEPRECATED, 0, JSEXN_WARN, "Object.prototype.watch and unwatch are very slow, non-standard, and deprecated; use a getter/setter instead")
MSG_DEF(JSMSG_ARRAYBUFFER_SLICE_DEPRECATED, 0, JSEXN_WARN, "ArrayBuffer.slice is deprecated; use ArrayBuffer.prototype.slice instead")
MSG_DEF(JSMSG_BAD_SURROGATE_CHAR, 1, JSEXN_TYPEERR, "bad surrogate character {0}")
MSG_DEF(JSMSG_UTF8_CHAR_TOO_LARGE, 1, JSEXN_TYPEERR, "UTF-8 character {0} too large")
@@ -186,7 +185,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_BAD_ANON_GENERATOR_RETURN, 0, JSEXN_TYPEERR, "anonymous generator function returns a value")
+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")
@@ -200,16 +199,18 @@ MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP, 0, JSEXN_SYNTAXERR, "invalid for each loo
MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE, 0, JSEXN_SYNTAXERR, "invalid for-in/of left-hand side")
MSG_DEF(JSMSG_LEXICAL_DECL_DEFINES_LET,0, JSEXN_SYNTAXERR, "a lexical declaration can't define a 'let' binding")
MSG_DEF(JSMSG_LET_STARTING_FOROF_LHS, 0, JSEXN_SYNTAXERR, "an expression X in 'for (X of Y)' must not start with 'let'")
-MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 1, JSEXN_TYPEERR, "generator function {0} returns a value")
+MSG_DEF(JSMSG_BAD_FUNCTION_YIELD, 0, JSEXN_TYPEERR, "can't use 'yield' in a function that can return a value")
+MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 0, JSEXN_TYPEERR, "generator function can't return a value")
MSG_DEF(JSMSG_BAD_GENEXP_BODY, 1, JSEXN_SYNTAXERR, "illegal use of {0} in generator expression")
-MSG_DEF(JSMSG_BAD_INCOP_OPERAND, 0, JSEXN_REFERENCEERR, "invalid increment/decrement operand")
+MSG_DEF(JSMSG_BAD_INCOP_OPERAND, 0, JSEXN_SYNTAXERR, "invalid increment/decrement operand")
MSG_DEF(JSMSG_BAD_METHOD_DEF, 0, JSEXN_SYNTAXERR, "bad method definition")
MSG_DEF(JSMSG_BAD_OCTAL, 1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant")
-MSG_DEF(JSMSG_BAD_OPERAND, 1, JSEXN_SYNTAXERR, "invalid {0} operand")
MSG_DEF(JSMSG_BAD_POW_LEFTSIDE, 0, JSEXN_SYNTAXERR, "unparenthesized unary expression can't appear on the left-hand side of '**'")
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")
@@ -217,6 +218,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")
@@ -227,6 +229,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")
@@ -262,7 +265,10 @@ MSG_DEF(JSMSG_GARBAGE_AFTER_INPUT, 2, JSEXN_SYNTAXERR, "unexpected garbage a
MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER, 0, JSEXN_SYNTAXERR, "identifier starts immediately after numeric literal")
MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 0, JSEXN_SYNTAXERR, "illegal character")
MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level of a module")
+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")
@@ -500,7 +506,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/testGCAllocator.cpp b/js/src/jsapi-tests/testGCAllocator.cpp
index 2c5c58a29..d203019ec 100644
--- a/js/src/jsapi-tests/testGCAllocator.cpp
+++ b/js/src/jsapi-tests/testGCAllocator.cpp
@@ -14,8 +14,6 @@
#if defined(XP_WIN)
#include "jswin.h"
#include <psapi.h>
-#elif defined(SOLARIS)
-// This test doesn't apply to Solaris.
#elif defined(XP_UNIX)
#include <algorithm>
#include <errno.h>
@@ -39,8 +37,6 @@ BEGIN_TEST(testGCAllocator)
# else // Various APIs are unavailable. This test is disabled.
return true;
# endif
-#elif defined(SOLARIS)
- return true;
#elif defined(XP_UNIX)
PageSize = size_t(sysconf(_SC_PAGESIZE));
#else
@@ -301,12 +297,6 @@ void* mapMemory(size_t length) { return nullptr; }
void unmapPages(void* p, size_t size) { }
# endif
-#elif defined(SOLARIS) // This test doesn't apply to Solaris.
-
-void* mapMemoryAt(void* desired, size_t length) { return nullptr; }
-void* mapMemory(size_t length) { return nullptr; }
-void unmapPages(void* p, size_t size) { }
-
#elif defined(XP_UNIX)
void*
@@ -377,7 +367,7 @@ unmapPages(void* p, size_t size)
MOZ_RELEASE_ASSERT(errno == ENOMEM);
}
-#else // !defined(XP_WIN) && !defined(SOLARIS) && !defined(XP_UNIX)
+#else // !defined(XP_WIN) && !defined(XP_UNIX)
#error "Memory mapping functions are not defined for your OS."
#endif
END_TEST(testGCAllocator)
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 85a38bba4..f9a0c6a6b 100644
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -39,7 +39,6 @@
#include "jsstr.h"
#include "jstypes.h"
#include "jsutil.h"
-#include "jswatchpoint.h"
#include "jsweakmap.h"
#include "jswrapper.h"
@@ -69,7 +68,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 +1020,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 +1064,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 +1097,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 +1153,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 +1162,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*)
@@ -2003,10 +1999,10 @@ JS_GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, const char* name,
}
JS_PUBLIC_API(bool)
-JS_GetOwnUCPropertyDescriptor(JSContext* cx, HandleObject obj, const char16_t* name,
+JS_GetOwnUCPropertyDescriptor(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
MutableHandle<PropertyDescriptor> desc)
{
- JSAtom* atom = AtomizeChars(cx, name, js_strlen(name));
+ JSAtom* atom = AtomizeChars(cx, name, namelen);
if (!atom)
return false;
RootedId id(cx, AtomToId(atom));
@@ -2014,6 +2010,28 @@ JS_GetOwnUCPropertyDescriptor(JSContext* cx, HandleObject obj, const char16_t* n
}
JS_PUBLIC_API(bool)
+JS_GetOwnElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::MutableHandleValue vp)
+{
+ RootedId id(cx);
+ if (!IndexToId(cx, index, &id)) {
+ return false;
+ }
+
+ Rooted<PropertyDescriptor> desc(cx);
+ if (!JS_GetOwnPropertyDescriptorById(cx, obj, id, &desc)) {
+ return false;
+ }
+
+ if (desc.object() && desc.isDataDescriptor()) {
+ vp.set(desc.value());
+ } else {
+ vp.setUndefined();
+ }
+
+ return true;
+}
+
+JS_PUBLIC_API(bool)
JS_GetPropertyDescriptorById(JSContext* cx, HandleObject obj, HandleId id,
MutableHandle<PropertyDescriptor> desc)
{
@@ -2028,7 +2046,19 @@ JS_GetPropertyDescriptor(JSContext* cx, HandleObject obj, const char* name,
if (!atom)
return false;
RootedId id(cx, AtomToId(atom));
- return atom && JS_GetPropertyDescriptorById(cx, obj, id, desc);
+ return JS_GetPropertyDescriptorById(cx, obj, id, desc);
+}
+
+JS_PUBLIC_API(bool)
+JS_GetUCPropertyDescriptor(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
+ MutableHandle<PropertyDescriptor> desc)
+{
+ JSAtom* atom = AtomizeChars(cx, name, namelen);
+ if (!atom) {
+ return false;
+ }
+ RootedId id(cx, AtomToId(atom));
+ return JS_GetPropertyDescriptorById(cx, obj, id, desc);
}
static bool
@@ -3477,7 +3507,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
@@ -3537,7 +3567,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;
}
@@ -3569,7 +3599,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());
@@ -4222,7 +4252,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;
@@ -4238,7 +4268,7 @@ JS_GetFunctionScript(JSContext* cx, HandleFunction fun)
*/
static bool
CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
- const char* name,
+ HandleAtom name, bool isInvalidName,
SourceBufferHolder& srcBuf, uint32_t parameterListEnd,
HandleObject enclosingEnv, HandleScope enclosingScope,
MutableHandleFunction fun)
@@ -4249,13 +4279,8 @@ CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
assertSameCompartment(cx, enclosingEnv);
RootedAtom funAtom(cx);
- if (name) {
- funAtom = Atomize(cx, name, strlen(name));
- if (!funAtom)
- return false;
- }
-
- fun.set(NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL, funAtom,
+ fun.set(NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL,
+ isInvalidName ? nullptr : name,
/* proto = */ nullptr,
gc::AllocKind::FUNCTION, TenuredObject,
enclosingEnv));
@@ -4273,11 +4298,17 @@ CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
return false;
}
+ // When function name is not a valid identifier, the generated function
+ // source in srcBuf doesn't have a function name. Set it here.
+ if (isInvalidName)
+ fun->setAtom(name);
+
return true;
}
static MOZ_MUST_USE bool
-BuildFunctionString(unsigned nargs, const char* const* argnames,
+BuildFunctionString(const char* name, size_t nameLen,
+ unsigned nargs, const char* const* argnames,
const SourceBufferHolder& srcBuf, StringBuffer* out,
uint32_t* parameterListEnd)
{
@@ -4286,6 +4317,12 @@ BuildFunctionString(unsigned nargs, const char* const* argnames,
if (!out->ensureTwoByteChars())
return false;
+ if (!out->append("function "))
+ return false;
+ if (name) {
+ if (!out->append(name, nameLen))
+ return false;
+ }
if (!out->append("("))
return false;
for (unsigned i = 0; i < nargs; i++) {
@@ -4322,15 +4359,32 @@ JS::CompileFunction(JSContext* cx, AutoObjectVector& envChain,
if (!CreateNonSyntacticEnvironmentChain(cx, envChain, &env, &scope))
return false;
+ size_t nameLen = 0;
+ bool isInvalidName = false;
+ RootedAtom nameAtom(cx);
+ if (name) {
+ nameLen = strlen(name);
+ nameAtom = Atomize(cx, name, nameLen);
+ if (!nameAtom)
+ return false;
+
+ // If name is not valid identifier
+ if (!js::frontend::IsIdentifier(name, nameLen))
+ isInvalidName = true;
+ }
+
uint32_t parameterListEnd;
StringBuffer funStr(cx);
- if (!BuildFunctionString(nargs, argnames, srcBuf, &funStr, &parameterListEnd))
+ if (!BuildFunctionString(isInvalidName ? nullptr : name, nameLen, nargs, argnames, srcBuf,
+ &funStr, &parameterListEnd)) {
return false;
+ }
size_t newLen = funStr.length();
SourceBufferHolder newSrcBuf(funStr.stealChars(), newLen, SourceBufferHolder::GiveOwnership);
- return CompileFunction(cx, options, name, newSrcBuf, parameterListEnd, env, scope, fun);
+ return CompileFunction(cx, options, nameAtom, isInvalidName, newSrcBuf, parameterListEnd, env,
+ scope, fun);
}
JS_PUBLIC_API(bool)
@@ -4369,14 +4423,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*)
@@ -4848,7 +4903,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;
}
@@ -4860,7 +4915,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;
}
@@ -4890,8 +4945,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)
@@ -5978,7 +6033,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;
@@ -5993,7 +6049,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;
@@ -6008,7 +6065,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;
@@ -6016,7 +6074,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)
@@ -6030,7 +6088,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);
}
@@ -6262,7 +6320,7 @@ JSErrorReport::freeLinebuf()
}
JSString*
-JSErrorReport::newMessageString(JSContext* cx)
+JSErrorBase::newMessageString(JSContext* cx)
{
if (!message_)
return cx->runtime()->emptyString;
@@ -6271,7 +6329,7 @@ JSErrorReport::newMessageString(JSContext* cx)
}
void
-JSErrorReport::freeMessage()
+JSErrorBase::freeMessage()
{
if (ownsMessage_) {
js_free((void*)message_.get());
@@ -6280,6 +6338,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 c1195cc00..dc00c650d 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;
@@ -2154,6 +2149,13 @@ namespace JS {
extern JS_PUBLIC_API(bool)
OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v, bool* bp);
+// Implementation of
+// https://www.ecma-international.org/ecma-262/6.0/#sec-instanceofoperator
+// This is almost identical to JS_HasInstance, except the latter may call a
+// custom hasInstance class op instead of InstanceofOperator.
+extern JS_PUBLIC_API(bool)
+InstanceofOperator(JSContext* cx, HandleObject obj, HandleValue v, bool* bp);
+
} // namespace JS
extern JS_PUBLIC_API(void*)
@@ -2917,9 +2919,12 @@ JS_GetOwnPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char* nam
JS::MutableHandle<JS::PropertyDescriptor> desc);
extern JS_PUBLIC_API(bool)
-JS_GetOwnUCPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char16_t* name,
+JS_GetOwnUCPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen,
JS::MutableHandle<JS::PropertyDescriptor> desc);
+extern JS_PUBLIC_API(bool)
+JS_GetOwnElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::MutableHandleValue vp);
+
/**
* Like JS_GetOwnPropertyDescriptorById, but also searches the prototype chain
* if no own property is found directly on obj. The object on which the
@@ -2934,6 +2939,10 @@ extern JS_PUBLIC_API(bool)
JS_GetPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char* name,
JS::MutableHandle<JS::PropertyDescriptor> desc);
+extern JS_PUBLIC_API(bool)
+JS_GetUCPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen,
+ JS::MutableHandle<JS::PropertyDescriptor> desc);
+
/**
* Define a property on obj.
*
@@ -5359,14 +5368,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'.
@@ -5378,28 +5503,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 {
@@ -5411,29 +5537,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();
-
- const JS::ConstUTF8CharsZ message() const {
- return message_;
- }
+ void initBorrowedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg,
+ size_t tokenOffsetArg);
- 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();
};
/*
@@ -6562,7 +6675,7 @@ struct JS_PUBLIC_API(PerformanceGroup) {
uint64_t refCount_;
};
-using PerformanceGroupVector = mozilla::Vector<RefPtr<js::PerformanceGroup>, 0, SystemAllocPolicy>;
+using PerformanceGroupVector = mozilla::Vector<RefPtr<js::PerformanceGroup>, 8, SystemAllocPolicy>;
/**
* Commit any Performance Monitoring data.
@@ -6601,10 +6714,6 @@ SetStopwatchIsMonitoringJank(JSContext*, bool);
extern JS_PUBLIC_API(bool)
GetStopwatchIsMonitoringJank(JSContext*);
-// Extract the CPU rescheduling data.
-extern JS_PUBLIC_API(void)
-GetPerfMonitoringTestCpuRescheduling(JSContext*, uint64_t* stayed, uint64_t* moved);
-
/**
* Add a number of microseconds to the time spent waiting on CPOWs
diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp
index 7a67c0095..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),
@@ -3169,6 +3144,11 @@ static const JSFunctionSpec array_methods[] = {
/* ES7 additions */
JS_SELF_HOSTED_FN("includes", "ArrayIncludes", 2,0),
+
+ /* ES2019 additions */
+ JS_SELF_HOSTED_FN("flat", "ArrayFlat", 0,0),
+ JS_SELF_HOSTED_FN("flatMap", "ArrayFlatMap", 1,0),
+
JS_FS_END
};
@@ -3239,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;
@@ -3265,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) {
@@ -3281,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;
@@ -3301,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;
@@ -3333,6 +3313,8 @@ array_proto_finish(JSContext* cx, JS::HandleObject ctor, JS::HandleObject proto)
!DefineProperty(cx, unscopables, cx->names().fill, value) ||
!DefineProperty(cx, unscopables, cx->names().find, value) ||
!DefineProperty(cx, unscopables, cx->names().findIndex, value) ||
+ !DefineProperty(cx, unscopables, cx->names().flat, value) ||
+ !DefineProperty(cx, unscopables, cx->names().flatMap, value) ||
!DefineProperty(cx, unscopables, cx->names().includes, value) ||
!DefineProperty(cx, unscopables, cx->names().keys, value) ||
!DefineProperty(cx, unscopables, cx->names().values, value))
@@ -3558,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));
@@ -3571,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;
@@ -3606,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);
@@ -3628,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)
{
@@ -3673,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));
@@ -3682,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..23b9d27ae 100644
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -37,7 +37,6 @@
#include "jsscript.h"
#include "jsstr.h"
#include "jstypes.h"
-#include "jswatchpoint.h"
#include "gc/Marking.h"
#include "jit/Ion.h"
@@ -381,49 +380,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 +419,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 +610,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 +633,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 +652,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 +736,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 +920,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 4e4ccdf2a..8ba186b08 100644
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -13,7 +13,6 @@
#include "jsfriendapi.h"
#include "jsgc.h"
#include "jsiter.h"
-#include "jswatchpoint.h"
#include "jswrapper.h"
#include "gc/Marking.h"
@@ -41,7 +40,6 @@ using namespace js::gc;
using namespace js::jit;
using mozilla::DebugOnly;
-using mozilla::PodArrayZero;
JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options = JS::CompartmentOptions())
: creationOptions_(options.creationOptions()),
@@ -77,7 +75,6 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
gcIncomingGrayPointers(nullptr),
debugModeBits(0),
randomKeyGenerator_(runtime_->forkRandomKeyGenerator()),
- watchpointMap(nullptr),
scriptCountsMap(nullptr),
debugScriptMap(nullptr),
debugEnvs(nullptr),
@@ -91,7 +88,6 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
unmappedArgumentsTemplate_(nullptr),
lcovOutput()
{
- PodArrayZero(sawDeprecatedLanguageExtension);
runtime_->numCompartments++;
MOZ_ASSERT_IF(creationOptions_.mergeable(),
creationOptions_.invisibleToDebugger());
@@ -99,15 +95,12 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
JSCompartment::~JSCompartment()
{
- reportTelemetry();
-
// Write the code coverage information in a file.
JSRuntime* rt = runtimeFromMainThread();
if (rt->lcovOutput.isEnabled())
rt->lcovOutput.writeLCovResult(lcovOutput);
js_delete(jitCompartment_);
- js_delete(watchpointMap);
js_delete(scriptCountsMap);
js_delete(debugScriptMap);
js_delete(debugEnvs);
@@ -116,13 +109,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--;
}
@@ -673,12 +659,6 @@ JSCompartment::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime t
if (traceOrMark == js::gc::GCRuntime::MarkRuntime && !zone()->isCollecting())
return;
- // During a GC, these are treated as weak pointers.
- if (traceOrMark == js::gc::GCRuntime::TraceRuntime) {
- if (watchpointMap)
- watchpointMap->markAll(trc);
- }
-
/* Mark debug scopes, if present */
if (debugEnvs)
debugEnvs->mark(trc);
@@ -723,9 +703,6 @@ JSCompartment::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime t
void
JSCompartment::finishRoots()
{
- if (watchpointMap)
- watchpointMap->clear();
-
if (debugEnvs)
debugEnvs->finish();
@@ -1086,18 +1063,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))
@@ -1268,39 +1245,6 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
*privateData += callback(mallocSizeOf, this);
}
-void
-JSCompartment::reportTelemetry()
-{
- // Only report telemetry for web content and add-ons, not chrome JS.
- if (isSystem_)
- return;
-
- // Hazard analysis can't tell that the telemetry callbacks don't GC.
- JS::AutoSuppressGCAnalysis nogc;
-
- int id = creationOptions_.addonIdOrNull()
- ? JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS
- : JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT;
-
- // Call back into Firefox's Telemetry reporter.
- for (size_t i = 0; i < DeprecatedLanguageExtensionCount; i++) {
- if (sawDeprecatedLanguageExtension[i])
- runtime_->addTelemetry(id, i);
- }
-}
-
-void
-JSCompartment::addTelemetry(const char* filename, DeprecatedLanguageExtension e)
-{
- // Only report telemetry for web content and add-ons, not chrome JS.
- if (isSystem_)
- return;
- if (!creationOptions_.addonIdOrNull() && (!filename || strncmp(filename, "http", 4) != 0))
- return;
-
- sawDeprecatedLanguageExtension[e] = true;
-}
-
HashNumber
JSCompartment::randomHashCode()
{
diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h
index 7bfeee1f6..05ff40b43 100644
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -282,7 +282,6 @@ class MOZ_RAII AutoSetNewObjectMetadata : private JS::CustomAutoRooter
namespace js {
class DebugEnvironments;
class ObjectWeakMap;
-class WatchpointMap;
class WeakMapBase;
} // namespace js
@@ -344,13 +343,6 @@ struct JSCompartment
isAtomsCompartment_ = true;
}
- // Used to approximate non-content code when reporting telemetry.
- inline bool isProbablySystemOrAddonCode() const {
- if (creationOptions_.addonIdOrNull())
- return true;
-
- return isSystem_;
- }
private:
JSPrincipals* principals_;
bool isSystem_;
@@ -536,9 +528,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;
@@ -821,8 +810,6 @@ struct JSCompartment
void sweepBreakpoints(js::FreeOp* fop);
public:
- js::WatchpointMap* watchpointMap;
-
js::ScriptCountsMap* scriptCountsMap;
js::DebugScriptMap* debugScriptMap;
@@ -879,34 +866,10 @@ struct JSCompartment
return jitCompartment_;
}
- enum DeprecatedLanguageExtension {
- DeprecatedForEach = 0, // JS 1.6+
- // NO LONGER USING 1
- DeprecatedLegacyGenerator = 2, // JS 1.7+
- DeprecatedExpressionClosure = 3, // Added in JS 1.8
- // NO LONGER USING 4
- // NO LONGER USING 5
- // NO LONGER USING 6
- // NO LONGER USING 7
- // NO LONGER USING 8
- // NO LONGER USING 9
- DeprecatedBlockScopeFunRedecl = 10,
- DeprecatedLanguageExtensionCount
- };
-
js::ArgumentsObject* getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped);
js::ArgumentsObject* maybeArgumentsTemplateObject(bool mapped) const;
- private:
- // Used for collecting telemetry on SpiderMonkey's deprecated language extensions.
- bool sawDeprecatedLanguageExtension[DeprecatedLanguageExtensionCount];
-
- void reportTelemetry();
-
- public:
- void addTelemetry(const char* filename, DeprecatedLanguageExtension e);
-
public:
// Aggregated output used to collect JSScript hit counts when code coverage
// is enabled.
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 9a8e364ed..3fc9200c1 100644
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -201,28 +201,92 @@ ErrorObject::classes[JSEXN_ERROR_LIMIT] = {
IMPLEMENT_ERROR_CLASS(RuntimeError)
};
-JSErrorReport*
-js::CopyErrorReport(JSContext* cx, JSErrorReport* report)
+size_t
+ExtraMallocSize(JSErrorReport* report)
+{
+ if (report->linebuf())
+ /*
+ * Mozilla bug 1352449. Count with null
+ * terminator and alignment. See CopyExtraData for
+ * the details about alignment.
+ */
+ return (report->linebufLength() + 1) * sizeof(char16_t) + 1;
+
+ return 0;
+}
+
+size_t
+ExtraMallocSize(JSErrorNotes::Note* note)
+{
+ return 0;
+}
+
+bool
+CopyExtraData(JSContext* cx, uint8_t** cursor, JSErrorReport* copy, JSErrorReport* report)
+{
+ if (report->linebuf()) {
+ /*
+ * Make sure cursor is properly aligned for char16_t for platforms
+ * which need it and it's at the end of the buffer on exit.
+ */
+ size_t alignment_backlog = 0;
+ if (size_t(*cursor) % 2)
+ (*cursor)++;
+ else
+ alignment_backlog = 1;
+
+ size_t linebufSize = (report->linebufLength() + 1) * sizeof(char16_t);
+ const char16_t* linebufCopy = (const char16_t*)(*cursor);
+ js_memcpy(*cursor, report->linebuf(), linebufSize);
+ *cursor += linebufSize + alignment_backlog;
+ 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 +295,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 +309,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;
+ }
+
+ 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 + filenameSize == (uint8_t*)copy + mallocSize);
+
+ 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 +393,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 +583,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 +660,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 +744,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);
/*
@@ -707,67 +782,6 @@ ErrorReport::~ErrorReport()
{
}
-void
-ErrorReport::ReportAddonExceptionToTelementry(JSContext* cx)
-{
- MOZ_ASSERT(exnObject);
- RootedObject unwrapped(cx, UncheckedUnwrap(exnObject));
- MOZ_ASSERT(unwrapped, "UncheckedUnwrap failed?");
-
- // There is not much we can report if the exception is not an ErrorObject, let's ignore those.
- if (!unwrapped->is<ErrorObject>())
- return;
-
- Rooted<ErrorObject*> errObj(cx, &unwrapped->as<ErrorObject>());
- RootedObject stack(cx, errObj->stack());
-
- // Let's ignore TOP level exceptions. For regular add-ons those will not be reported anyway,
- // for SDK based once it should not be a valid case either.
- // At this point the frame stack is unwound but the exception object stored the stack so let's
- // use that for getting the function name.
- if (!stack)
- return;
-
- JSCompartment* comp = stack->compartment();
- JSAddonId* addonId = comp->creationOptions().addonIdOrNull();
-
- // We only want to send the report if the scope that just have thrown belongs to an add-on.
- // Let's check the compartment of the youngest function on the stack, to determine that.
- if (!addonId)
- return;
-
- RootedString funnameString(cx);
- JS::SavedFrameResult result = GetSavedFrameFunctionDisplayName(cx, stack, &funnameString);
- // AccessDenied should never be the case here for add-ons but let's not risk it.
- JSAutoByteString bytes;
- const char* funname = nullptr;
- bool denied = result == JS::SavedFrameResult::AccessDenied;
- funname = denied ? "unknown"
- : funnameString ? AtomToPrintableString(cx,
- &funnameString->asAtom(),
- &bytes)
- : "anonymous";
-
- UniqueChars addonIdChars(JS_EncodeString(cx, addonId));
-
- const char* filename = nullptr;
- if (reportp && reportp->filename) {
- filename = strrchr(reportp->filename, '/');
- if (filename)
- filename++;
- }
- if (!filename) {
- filename = "FILE_NOT_FOUND";
- }
- char histogramKey[64];
- SprintfLiteral(histogramKey, "%s %s %s %u",
- addonIdChars.get(),
- funname,
- filename,
- (reportp ? reportp->lineno : 0) );
- cx->runtime()->addTelemetry(JS_TELEMETRY_ADDON_EXCEPTIONS, 1, histogramKey);
-}
-
bool
ErrorReport::init(JSContext* cx, HandleValue exn,
SniffingBehavior sniffingBehavior)
@@ -786,10 +800,6 @@ ErrorReport::init(JSContext* cx, HandleValue exn,
JSMSG_ERR_DURING_THROW);
return false;
}
-
- // Let's see if the exception is from add-on code, if so, it should be reported
- // to telementry.
- ReportAddonExceptionToTelementry(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 595a21410..bdb3c0a4d 100644
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -15,7 +15,6 @@
#include "jsgc.h"
#include "jsobj.h"
#include "jsprf.h"
-#include "jswatchpoint.h"
#include "jsweakmap.h"
#include "jswrapper.h"
@@ -113,7 +112,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 +268,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;
@@ -543,11 +542,6 @@ js::SetPreserveWrapperCallback(JSContext* cx, PreserveWrapperCallback callback)
cx->preserveWrapperCallback = callback;
}
-/*
- * The below code is for temporary telemetry use. It can be removed when
- * sufficient data has been harvested.
- */
-
namespace js {
// Defined in vm/GlobalObject.cpp.
extern size_t sSetProtoCalled;
@@ -584,7 +578,6 @@ void
js::TraceWeakMaps(WeakMapTracer* trc)
{
WeakMapBase::traceAllMappings(trc);
- WatchpointMap::traceAll(trc);
}
extern JS_FRIEND_API(bool)
@@ -643,12 +636,6 @@ js::StringToLinearStringSlow(JSContext* cx, JSString* str)
return str->ensureLinear(cx);
}
-JS_FRIEND_API(void)
-JS_SetAccumulateTelemetryCallback(JSContext* cx, JSAccumulateTelemetryDataCallback callback)
-{
- cx->setTelemetryCallback(cx, callback);
-}
-
JS_FRIEND_API(JSObject*)
JS_CloneObject(JSContext* cx, HandleObject obj, HandleObject protoArg)
{
diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h
index 722085549..491215456 100644
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -105,46 +105,6 @@ JS_TraceShapeCycleCollectorChildren(JS::CallbackTracer* trc, JS::GCCellPtr shape
extern JS_FRIEND_API(void)
JS_TraceObjectGroupCycleCollectorChildren(JS::CallbackTracer* trc, JS::GCCellPtr group);
-enum {
- JS_TELEMETRY_GC_REASON,
- JS_TELEMETRY_GC_IS_ZONE_GC,
- JS_TELEMETRY_GC_MS,
- JS_TELEMETRY_GC_BUDGET_MS,
- JS_TELEMETRY_GC_ANIMATION_MS,
- JS_TELEMETRY_GC_MAX_PAUSE_MS,
- JS_TELEMETRY_GC_MARK_MS,
- JS_TELEMETRY_GC_SWEEP_MS,
- JS_TELEMETRY_GC_COMPACT_MS,
- JS_TELEMETRY_GC_MARK_ROOTS_MS,
- JS_TELEMETRY_GC_MARK_GRAY_MS,
- JS_TELEMETRY_GC_SLICE_MS,
- JS_TELEMETRY_GC_SLOW_PHASE,
- JS_TELEMETRY_GC_MMU_50,
- JS_TELEMETRY_GC_RESET,
- JS_TELEMETRY_GC_RESET_REASON,
- JS_TELEMETRY_GC_INCREMENTAL_DISABLED,
- JS_TELEMETRY_GC_NON_INCREMENTAL,
- JS_TELEMETRY_GC_NON_INCREMENTAL_REASON,
- JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS,
- JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS,
- JS_TELEMETRY_GC_MINOR_REASON,
- JS_TELEMETRY_GC_MINOR_REASON_LONG,
- JS_TELEMETRY_GC_MINOR_US,
- JS_TELEMETRY_GC_NURSERY_BYTES,
- JS_TELEMETRY_GC_PRETENURE_COUNT,
- JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT,
- JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS,
- JS_TELEMETRY_ADDON_EXCEPTIONS,
- JS_TELEMETRY_AOT_USAGE,
- JS_TELEMETRY_END
-};
-
-typedef void
-(*JSAccumulateTelemetryDataCallback)(int id, uint32_t sample, const char* key);
-
-extern JS_FRIEND_API(void)
-JS_SetAccumulateTelemetryCallback(JSContext* cx, JSAccumulateTelemetryDataCallback callback);
-
extern JS_FRIEND_API(bool)
JS_GetIsSecureContext(JSCompartment* compartment);
@@ -1456,9 +1416,6 @@ struct MOZ_STACK_CLASS JS_FRIEND_API(ErrorReport)
bool populateUncaughtExceptionReportUTF8(JSContext* cx, ...);
bool populateUncaughtExceptionReportUTF8VA(JSContext* cx, va_list ap);
- // Reports exceptions from add-on scopes to telementry.
- void ReportAddonExceptionToTelementry(JSContext* cx);
-
// We may have a provided JSErrorReport, so need a way to represent that.
JSErrorReport* reportp;
@@ -2153,30 +2110,6 @@ JS_FRIEND_API(void*)
JS_GetDataViewData(JSObject* obj, const JS::AutoCheckCannotGC&);
namespace js {
-
-/**
- * Add a watchpoint -- in the Object.prototype.watch sense -- to |obj| for the
- * property |id|, using the callable object |callable| as the function to be
- * called for notifications.
- *
- * This is an internal function exposed -- temporarily -- only so that DOM
- * proxies can be watchable. Don't use it! We'll soon kill off the
- * Object.prototype.{,un}watch functions, at which point this will go too.
- */
-extern JS_FRIEND_API(bool)
-WatchGuts(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable);
-
-/**
- * Remove a watchpoint -- in the Object.prototype.watch sense -- from |obj| for
- * the property |id|.
- *
- * This is an internal function exposed -- temporarily -- only so that DOM
- * proxies can be watchable. Don't use it! We'll soon kill off the
- * Object.prototype.{,un}watch functions, at which point this will go too.
- */
-extern JS_FRIEND_API(bool)
-UnwatchGuts(JSContext* cx, JS::HandleObject obj, JS::HandleId id);
-
namespace jit {
enum class InlinableNative : uint16_t;
@@ -2297,6 +2230,7 @@ struct JSJitInfo {
Method,
StaticMethod,
InlinableNative,
+ IgnoresReturnValueNative,
// Must be last
OpTypeCount
};
@@ -2388,8 +2322,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 bcb0da80b..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;
@@ -288,7 +299,15 @@ CallerGetterImpl(JSContext* cx, const CallArgs& args)
return true;
}
+ if (JS_IsDeadWrapper(callerObj)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_DEAD_OBJECT);
+ return false;
+ }
+
JSFunction* callerFun = &callerObj->as<JSFunction>();
+ if (IsWrappedAsyncFunction(callerFun))
+ callerFun = GetUnwrappedAsyncFunction(callerFun);
MOZ_ASSERT(!callerFun->isBuiltin(), "non-builtin iterator returned a builtin?");
if (callerFun->strict()) {
@@ -314,54 +333,14 @@ CallerSetterImpl(JSContext* cx, const CallArgs& args)
{
MOZ_ASSERT(IsFunction(args.thisv()));
- // Beware! This function can be invoked on *any* function! It can't
- // assume it'll never be invoked on natives, strict mode functions, bound
- // functions, or anything else that ordinarily has immutable .caller
- // defined with [[ThrowTypeError]].
- RootedFunction fun(cx, &args.thisv().toObject().as<JSFunction>());
- if (!CallerRestrictions(cx, fun))
- return false;
-
- // Return |undefined| unless an error must be thrown.
- args.rval().setUndefined();
-
- // We can almost just return |undefined| here -- but if the caller function
- // was strict mode code, we still have to throw a TypeError. This requires
- // computing the caller, checking that no security boundaries are crossed,
- // and throwing a TypeError if the resulting caller is strict.
-
- NonBuiltinScriptFrameIter iter(cx);
- if (!AdvanceToActiveCallLinear(cx, iter, fun))
- return true;
-
- ++iter;
- while (!iter.done() && iter.isEvalFrame())
- ++iter;
-
- if (iter.done() || !iter.isFunctionFrame())
- return true;
-
- RootedObject caller(cx, iter.callee(cx));
- if (!cx->compartment()->wrap(cx, &caller)) {
- cx->clearPendingException();
- return true;
- }
-
- // If we don't have full access to the caller, or the caller is not strict,
- // return undefined. Otherwise throw a TypeError.
- JSObject* callerObj = CheckedUnwrap(caller);
- if (!callerObj)
- return true;
-
- JSFunction* callerFun = &callerObj->as<JSFunction>();
- MOZ_ASSERT(!callerFun->isBuiltin(), "non-builtin iterator returned a builtin?");
-
- if (callerFun->strict()) {
- JS_ReportErrorFlagsAndNumberASCII(cx, JSREPORT_ERROR, GetErrorMessage, nullptr,
- JSMSG_CALLER_IS_STRICT);
- return false;
+ // We just have to return |undefined|, but first we call CallerGetterImpl
+ // because we need the same strict-mode and security checks.
+
+ if (!CallerGetterImpl(cx, args)) {
+ return false;
}
+ args.rval().setUndefined();
return true;
}
@@ -400,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;
@@ -497,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())
@@ -690,7 +669,7 @@ js::fun_symbolHasInstance(JSContext* cx, unsigned argc, Value* vp)
}
/*
- * ES6 (4-25-16) 7.3.19 OrdinaryHasInstance
+ * ES6 7.3.19 OrdinaryHasInstance
*/
bool
JS::OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v, bool* bp)
@@ -707,7 +686,7 @@ JS::OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v, bool*
if (obj->is<JSFunction>() && obj->isBoundFunction()) {
/* Steps 2a-b. */
obj = obj->as<JSFunction>().getBoundFunctionTarget();
- return InstanceOfOperator(cx, obj, v, bp);
+ return InstanceofOperator(cx, obj, v, bp);
}
/* Step 3. */
@@ -716,12 +695,12 @@ JS::OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v, bool*
return true;
}
- /* Step 4. */
+ /* Step 4-5. */
RootedValue pval(cx);
if (!GetProperty(cx, obj, obj, cx->names().prototype, &pval))
return false;
- /* Step 5. */
+ /* Step 6. */
if (pval.isPrimitive()) {
/*
* Throw a runtime error if instanceof is called on a function that
@@ -732,7 +711,7 @@ JS::OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v, bool*
return false;
}
- /* Step 6. */
+ /* Step 7. */
RootedObject pobj(cx, &pval.toObject());
bool isDelegate;
if (!IsDelegate(cx, pobj, v, &isDelegate))
@@ -815,8 +794,10 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
RootedFunction functionProto(cx, &functionProto_->as<JSFunction>());
- const char* rawSource = "() {\n}";
+ const char* rawSource = "function () {\n}";
size_t sourceLen = strlen(rawSource);
+ size_t begin = 9;
+ MOZ_ASSERT(rawSource[begin] == '(');
mozilla::UniquePtr<char16_t[], JS::FreePolicy> source(InflateString(cx, rawSource, &sourceLen));
if (!source)
return nullptr;
@@ -838,13 +819,15 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
RootedScript script(cx, JSScript::Create(cx,
options,
sourceObject,
+ begin,
+ ss->length(),
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;
@@ -879,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;
@@ -917,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))
@@ -1019,80 +954,97 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
}
}
- if (fun->isAsync()) {
- if (!out.append("async "))
- return nullptr;
- }
+ bool funIsNonArrowLambda = fun->isLambda() && !fun->isArrow();
- bool funIsMethodOrNonArrowLambda = (fun->isLambda() && !fun->isArrow()) || fun->isMethod() ||
- fun->isGetter() || fun->isSetter();
- 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 and methods.
- if (haveSource && !prettyPrint && funIsMethodOrNonArrowLambda) {
+ // If we're not in pretty mode, put parentheses around lambda functions
+ // so that eval returns lambda, not function statement.
+ if (haveSource && !prettyPrint && funIsNonArrowLambda) {
if (!out.append("("))
return nullptr;
}
- if (!fun->isArrow()) {
- bool ok;
- if (fun->isStarGenerator() && !fun->isAsync())
- ok = out.append("function* ");
- else
- ok = out.append("function ");
- if (!ok)
- return nullptr;
- }
- if (fun->explicitName()) {
- if (!out.append(fun->explicitName()))
- return nullptr;
- }
if (haveSource && !script->scriptSource()->hasSourceData() &&
!JSScript::loadSource(cx, script->scriptSource(), &haveSource))
{
return nullptr;
}
+
+ auto AppendPrelude = [&out, &fun]() {
+ if (fun->isAsync()) {
+ if (!out.append("async "))
+ return false;
+ }
+
+ if (!fun->isArrow()) {
+ if (!out.append("function"))
+ return false;
+
+ if (fun->isStarGenerator()) {
+ if (!out.append('*'))
+ return false;
+ }
+ }
+
+ if (fun->explicitName()) {
+ if (!out.append(' '))
+ return false;
+ if (!out.append(fun->explicitName()))
+ return false;
+ }
+ return true;
+ };
+
if (haveSource) {
- Rooted<JSFlatString*> src(cx, script->sourceData(cx));
+ Rooted<JSFlatString*> src(cx, JSScript::sourceDataForToString(cx, script));
if (!src)
return nullptr;
if (!out.append(src))
return nullptr;
- if (!prettyPrint && funIsMethodOrNonArrowLambda) {
+ if (!prettyPrint && funIsNonArrowLambda) {
if (!out.append(")"))
return nullptr;
}
- } else if (fun->isInterpreted() && !fun->isSelfHostedBuiltin()) {
- if (!out.append("() {\n ") ||
+ } 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]") ||
!out.append("\n}"))
{
return nullptr;
}
} else {
- MOZ_ASSERT(!fun->isExprBody());
- bool derived = fun->infallibleIsDefaultClassConstructor(cx);
- if (derived && fun->isDerivedClassConstructor()) {
- if (!out.append("(...args) {\n ") ||
- !out.append("super(...args);\n}"))
- {
- return nullptr;
- }
- } else {
- if (!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();
}
@@ -1321,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);
@@ -1405,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
@@ -1448,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)
@@ -1669,7 +1618,18 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator
StringBuffer sb(cx);
- if (!sb.append('('))
+ if (isAsync) {
+ if (!sb.append("async "))
+ return false;
+ }
+ if (!sb.append("function"))
+ return false;
+ if (isStarGenerator && !isAsync) {
+ if (!sb.append('*'))
+ return false;
+ }
+
+ if (!sb.append(" anonymous("))
return false;
if (args.length() > 1) {
@@ -1690,12 +1650,15 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator
if (i < args.length() - 2) {
// Step 9.d.iii.
- if (!sb.append(", "))
+ if (!sb.append(","))
return false;
}
}
}
+ if (!sb.append('\n'))
+ return false;
+
// Remember the position of ")".
Maybe<uint32_t> parameterListEnd = Some(uint32_t(sb.length()));
MOZ_ASSERT(FunctionConstructorMedialSigils[0] == ')');
diff --git a/js/src/jsfun.h b/js/src/jsfun.h
index 7da831aa2..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 {
@@ -460,6 +452,19 @@ class JSFunction : public js::NativeObject
return nonLazyScript();
}
+ // If this is a scripted function, returns its canonical function (the
+ // original function allocated by the frontend). Note that lazy self-hosted
+ // builtins don't have a lazy script so in that case we also return nullptr.
+ JSFunction* maybeCanonicalFunction() const {
+ if (hasScript()) {
+ return nonLazyScript()->functionNonDelazifying();
+ }
+ if (isInterpretedLazy() && !isSelfHostedBuiltin()) {
+ return lazyScript()->functionNonDelazifying();
+ }
+ return nullptr;
+ }
+
// The state of a JSFunction whose script errored out during bytecode
// compilation. Such JSFunctions are only reachable via GC iteration and
// not from script.
@@ -473,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_);
@@ -580,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:
@@ -789,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/jsgc.cpp b/js/src/jsgc.cpp
index 3d4dae9bb..5a9d732b6 100644
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -207,7 +207,6 @@
#include "jsscript.h"
#include "jstypes.h"
#include "jsutil.h"
-#include "jswatchpoint.h"
#include "jsweakmap.h"
#ifdef XP_WIN
# include "jswin.h"
@@ -1524,19 +1523,11 @@ GCMarker::delayMarkingChildren(const void* thing)
}
inline void
-ArenaLists::prepareForIncrementalGC(JSRuntime* rt)
+ArenaLists::prepareForIncrementalGC()
{
+ purge();
for (auto i : AllAllocKinds()) {
- FreeSpan* span = freeLists[i];
- if (span != &placeholder) {
- if (!span->isEmpty()) {
- Arena* arena = span->getArena();
- arena->allocatedDuringIncremental = true;
- rt->gc.marker.delayMarkingArena(arena);
- } else {
- freeLists[i] = &placeholder;
- }
- }
+ arenaLists[i].moveCursorToEnd();
}
}
@@ -2251,7 +2242,7 @@ GCRuntime::updateTypeDescrObjects(MovingTracer* trc, Zone* zone)
{
zone->typeDescrObjects.sweep();
for (auto r = zone->typeDescrObjects.all(); !r.empty(); r.popFront())
- UpdateCellPointers(trc, r.front().get());
+ UpdateCellPointers(trc, r.front());
}
void
@@ -2310,22 +2301,27 @@ GCRuntime::updateCellPointers(MovingTracer* trc, Zone* zone, AllocKinds kinds, s
// 2) typed object type descriptor objects
// 3) all other objects
//
+// Also, there can be data races calling IsForwarded() on the new location of a
+// cell that is being updated in parallel on another thread. This can be avoided
+// by updating some kinds of cells in different phases. This is done for JSScripts
+// and LazyScripts, and JSScripts and Scopes.
+//
// Since we want to minimize the number of phases, we put everything else into
// the first phase and label it the 'misc' phase.
static const AllocKinds UpdatePhaseMisc {
AllocKind::SCRIPT,
- AllocKind::LAZY_SCRIPT,
AllocKind::BASE_SHAPE,
AllocKind::SHAPE,
AllocKind::ACCESSOR_SHAPE,
AllocKind::OBJECT_GROUP,
AllocKind::STRING,
- AllocKind::JITCODE,
- AllocKind::SCOPE
+ AllocKind::JITCODE
};
static const AllocKinds UpdatePhaseObjects {
+ AllocKind::LAZY_SCRIPT,
+ AllocKind::SCOPE,
AllocKind::FUNCTION,
AllocKind::FUNCTION_EXTENDED,
AllocKind::OBJECT0,
@@ -2395,11 +2391,6 @@ GCRuntime::updatePointersToRelocatedCells(Zone* zone, AutoLockForExclusiveAccess
Debugger::markIncomingCrossCompartmentEdges(&trc);
WeakMapBase::markAll(zone, &trc);
- for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
- c->trace(&trc);
- if (c->watchpointMap)
- c->watchpointMap->markAll(&trc);
- }
// Mark all gray roots, making sure we call the trace callback to get the
// current set.
@@ -2408,7 +2399,6 @@ GCRuntime::updatePointersToRelocatedCells(Zone* zone, AutoLockForExclusiveAccess
}
// Sweep everything to fix up weak pointers
- WatchpointMap::sweepAll(rt);
Debugger::sweepAll(rt->defaultFreeOp());
jit::JitRuntime::SweepJitcodeGlobalTable(rt);
rt->gc.sweepZoneAfterCompacting(zone);
@@ -3574,6 +3564,23 @@ RelazifyFunctions(Zone* zone, AllocKind kind)
}
}
+static bool
+ShouldCollectZone(Zone* zone, JS::gcreason::Reason reason)
+{
+ // Normally we collect all scheduled zones.
+ if (reason != JS::gcreason::COMPARTMENT_REVIVED)
+ return zone->isGCScheduled();
+
+ // If we are repeating a GC because we noticed dead compartments haven't
+ // been collected, then only collect zones containing those compartments.
+ for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
+ if (comp->scheduledForDestruction)
+ return true;
+ }
+
+ return false;
+}
+
bool
GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock)
{
@@ -3597,7 +3604,7 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAcces
#endif
/* Set up which zones will be collected. */
- if (zone->isGCScheduled()) {
+ if (ShouldCollectZone(zone, reason)) {
if (!zone->isAtomsZone()) {
any = true;
zone->setGCState(Zone::Mark);
@@ -3616,7 +3623,7 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAcces
for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) {
c->marked = false;
c->scheduledForDestruction = false;
- c->maybeAlive = false;
+ c->maybeAlive = c->hasBeenEntered() || !c->zone()->isGCScheduled();
if (shouldPreserveJITCode(c, currentTime, reason, canAllocateMoreCode))
c->zone()->setPreservingCode(true);
}
@@ -3636,6 +3643,7 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAcces
* on. If the value of keepAtoms() changes between GC slices, then we'll
* cancel the incremental GC. See IsIncrementalGCSafe.
*/
+
if (isFull && !rt->keepAtoms()) {
Zone* atomsZone = rt->atomsCompartment(lock)->zone();
if (atomsZone->isGCScheduled()) {
@@ -3650,15 +3658,12 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAcces
return false;
/*
- * At the end of each incremental slice, we call prepareForIncrementalGC,
- * which marks objects in all arenas that we're currently allocating
- * into. This can cause leaks if unreachable objects are in these
- * arenas. This purge call ensures that we only mark arenas that have had
- * allocations after the incremental GC started.
+ * Ensure that after the start of a collection we don't allocate into any
+ * existing arenas, as this can cause unreachable things to be marked.
*/
if (isIncremental) {
for (GCZonesIter zone(rt); !zone.done(); zone.next())
- zone->arenas.purge();
+ zone->arenas.prepareForIncrementalGC();
}
MemProfiler::MarkTenuredStart(rt);
@@ -3743,12 +3748,10 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAcces
gcstats::AutoPhase ap2(stats, gcstats::PHASE_MARK_ROOTS);
if (isIncremental) {
- gcstats::AutoPhase ap3(stats, gcstats::PHASE_BUFFER_GRAY_ROOTS);
bufferGrayRoots();
+ markCompartments();
}
-
- markCompartments();
-
+
return true;
}
@@ -3761,9 +3764,14 @@ GCRuntime::markCompartments()
* This code ensures that if a compartment is "dead", then it will be
* collected in this GC. A compartment is considered dead if its maybeAlive
* flag is false. The maybeAlive flag is set if:
- * (1) the compartment has incoming cross-compartment edges, or
- * (2) an object in the compartment was marked during root marking, either
- * as a black root or a gray root.
+ * (1) the compartment has been entered (set in beginMarkPhase() above)
+ * (2) the compartment is not being collected (set in beginMarkPhase()
+ * above)
+ * (3) an object in the compartment was marked during root marking, either
+ * as a black root or a gray root (set in RootMarking.cpp), or
+ * (4) the compartment has incoming cross-compartment edges from another
+ * compartment that has maybeAlive set (set by this method).
+ *
* If the maybeAlive is false, then we set the scheduledForDestruction flag.
* At the end of the GC, we look for compartments where
* scheduledForDestruction is true. These are compartments that were somehow
@@ -3781,26 +3789,37 @@ GCRuntime::markCompartments()
* allocation and read barriers during JS_TransplantObject and the like.
*/
- /* Set the maybeAlive flag based on cross-compartment edges. */
- for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
- for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
+ /* Propagate the maybeAlive flag via cross-compartment edges. */
+
+ Vector<JSCompartment*, 0, js::SystemAllocPolicy> workList;
+
+ for (CompartmentsIter comp(rt, SkipAtoms); !comp.done(); comp.next()) {
+ if (comp->maybeAlive) {
+ if (!workList.append(comp))
+ return;
+ }
+ }
+ while (!workList.empty()) {
+ JSCompartment* comp = workList.popCopy();
+ for (JSCompartment::WrapperEnum e(comp); !e.empty(); e.popFront()) {
if (e.front().key().is<JSString*>())
continue;
JSCompartment* dest = e.front().mutableKey().compartment();
- if (dest)
+ if (dest && !dest->maybeAlive) {
dest->maybeAlive = true;
+ if (!workList.append(dest))
+ return;
+ }
}
}
- /*
- * For black roots, code in gc/Marking.cpp will already have set maybeAlive
- * during MarkRuntime.
- */
-
- /* Propogate maybeAlive to scheduleForDestruction. */
- for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
- if (!c->maybeAlive && !rt->isAtomsCompartment(c))
- c->scheduledForDestruction = true;
+
+ /* Set scheduleForDestruction based on maybeAlive. */
+
+ for (GCCompartmentsIter comp(rt); !comp.done(); comp.next()) {
+ MOZ_ASSERT(!comp->scheduledForDestruction);
+ if (!comp->maybeAlive && !rt->isAtomsCompartment(comp))
+ comp->scheduledForDestruction = true;
}
}
@@ -3824,10 +3843,6 @@ GCRuntime::markWeakReferences(gcstats::Phase phase)
for (ZoneIterT zone(rt); !zone.done(); zone.next())
markedAny |= WeakMapBase::markZoneIteratively(zone, &marker);
}
- for (CompartmentsIterT<ZoneIterT> c(rt); !c.done(); c.next()) {
- if (c->watchpointMap)
- markedAny |= c->watchpointMap->markIteratively(&marker);
- }
markedAny |= Debugger::markAllIteratively(&marker);
markedAny |= jit::JitRuntime::MarkJitcodeGlobalTableIteratively(&marker);
@@ -4599,9 +4614,6 @@ GCRuntime::beginSweepingZoneGroup(AutoLockForExclusiveAccess& lock)
// Bug 1071218: the following two methods have not yet been
// refactored to work on a single zone-group at once.
- // Collect watch points associated with unreachable objects.
- WatchpointMap::sweepAll(rt);
-
// Detach unreachable debuggers and global objects from each other.
Debugger::sweepAll(&fop);
@@ -5301,7 +5313,7 @@ AutoGCSlice::~AutoGCSlice()
for (ZonesIter zone(runtime, WithAtoms); !zone.done(); zone.next()) {
if (zone->isGCMarking()) {
zone->setNeedsIncrementalBarrier(true, Zone::UpdateJit);
- zone->arenas.prepareForIncrementalGC(runtime);
+ zone->arenas.purge();
} else {
zone->setNeedsIncrementalBarrier(false, Zone::UpdateJit);
}
@@ -5482,7 +5494,7 @@ gc::AbortReason
gc::IsIncrementalGCUnsafe(JSRuntime* rt)
{
MOZ_ASSERT(!rt->mainThread.suppressGC);
-
+
if (rt->keepAtoms())
return gc::AbortReason::KeepAtomsSet;
@@ -5493,9 +5505,17 @@ gc::IsIncrementalGCUnsafe(JSRuntime* rt)
}
void
-GCRuntime::budgetIncrementalGC(SliceBudget& budget, AutoLockForExclusiveAccess& lock)
+GCRuntime::budgetIncrementalGC(JS::gcreason::Reason reason, SliceBudget& budget,
+ AutoLockForExclusiveAccess& lock)
{
AbortReason unsafeReason = IsIncrementalGCUnsafe(rt);
+ if (unsafeReason == AbortReason::None) {
+ if (reason == JS::gcreason::COMPARTMENT_REVIVED)
+ unsafeReason = gc::AbortReason::CompartmentRevived;
+ else if (mode != JSGC_MODE_INCREMENTAL)
+ unsafeReason = gc::AbortReason::ModeChange;
+ }
+
if (unsafeReason != AbortReason::None) {
resetIncrementalGC(unsafeReason, lock);
budget.makeUnlimited();
@@ -5503,12 +5523,7 @@ GCRuntime::budgetIncrementalGC(SliceBudget& budget, AutoLockForExclusiveAccess&
return;
}
- if (mode != JSGC_MODE_INCREMENTAL) {
- resetIncrementalGC(AbortReason::ModeChange, lock);
- budget.makeUnlimited();
- stats.nonincremental(AbortReason::ModeChange);
- return;
- }
+
if (isTooMuchMalloc()) {
budget.makeUnlimited();
@@ -5667,7 +5682,7 @@ GCRuntime::gcCycle(bool nonincrementalByAPI, SliceBudget& budget, JS::gcreason::
stats.nonincremental(gc::AbortReason::NonIncrementalRequested);
budget.makeUnlimited();
} else {
- budgetIncrementalGC(budget, session.lock);
+ budgetIncrementalGC(reason, budget, session.lock);
}
/* The GC was reset, so we need a do-over. */
@@ -5759,6 +5774,22 @@ GCRuntime::checkIfGCAllowedInCurrentState(JS::gcreason::Reason reason)
return true;
}
+bool
+GCRuntime::shouldRepeatForDeadZone(JS::gcreason::Reason reason)
+{
+ MOZ_ASSERT_IF(reason == JS::gcreason::COMPARTMENT_REVIVED, !isIncremental);
+
+ if (!isIncremental || isIncrementalGCInProgress())
+ return false;
+
+ for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
+ if (c->scheduledForDestruction)
+ return true;
+ }
+
+ return false;
+}
+
void
GCRuntime::collect(bool nonincrementalByAPI, SliceBudget budget, JS::gcreason::Reason reason)
{
@@ -5777,27 +5808,23 @@ GCRuntime::collect(bool nonincrementalByAPI, SliceBudget budget, JS::gcreason::R
do {
poked = false;
bool wasReset = gcCycle(nonincrementalByAPI, budget, reason);
-
- /* Need to re-schedule all zones for GC. */
- if (poked && cleanUpEverything)
+
+ bool repeatForDeadZone = false;
+ if (poked && cleanUpEverything) {
+ /* Need to re-schedule all zones for GC. */
JS::PrepareForFullGC(rt->contextFromMainThread());
- /*
- * This code makes an extra effort to collect compartments that we
- * thought were dead at the start of the GC. See the large comment in
- * beginMarkPhase.
- */
- bool repeatForDeadZone = false;
- if (!nonincrementalByAPI && !isIncrementalGCInProgress()) {
- for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
- if (c->scheduledForDestruction) {
- nonincrementalByAPI = true;
- repeatForDeadZone = true;
- reason = JS::gcreason::COMPARTMENT_REVIVED;
- c->zone()->scheduleGC();
- }
- }
+
+ } else if (shouldRepeatForDeadZone(reason) && !wasReset) {
+ /*
+ * This code makes an extra effort to collect compartments that we
+ * thought were dead at the start of the GC. See the large comment
+ * in beginMarkPhase.
+ */
+ repeatForDeadZone = true;
+ reason = JS::gcreason::COMPARTMENT_REVIVED;
}
+
/*
* If we reset an existing GC, we need to start a new one. Also, we
@@ -6149,12 +6176,6 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target)
for (auto group = source->zone()->cellIter<ObjectGroup>(); !group.done(); group.next()) {
group->setGeneration(target->zone()->types.generation);
group->compartment_ = target;
-
- // Remove any unboxed layouts from the list in the off thread
- // compartment. These do not need to be reinserted in the target
- // compartment's list, as the list is not required to be complete.
- if (UnboxedLayout* layout = group->maybeUnboxedLayoutDontCheckGeneration())
- layout->detachFromCompartment();
}
// Fixup zone pointers in source's zone to refer to target's zone.
diff --git a/js/src/jsgc.h b/js/src/jsgc.h
index d3cf31fe7..952fd6bae 100644
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -61,7 +61,8 @@ enum class State {
D(ModeChange) \
D(MallocBytesTrigger) \
D(GCBytesTrigger) \
- D(ZoneChange)
+ D(ZoneChange) \
+ D(CompartmentRevived)
enum class AbortReason {
#define MAKE_REASON(name) name,
GC_ABORT_REASONS(MAKE_REASON)
@@ -453,6 +454,12 @@ class ArenaList {
check();
return !*cursorp_;
}
+
+ void moveCursorToEnd() {
+ while (!isCursorAtEnd()) {
+ cursorp_ = &(*cursorp_)->next;
+ }
+ }
// This can return nullptr.
Arena* arenaAfterCursor() const {
@@ -739,7 +746,7 @@ class ArenaLists
freeLists[i] = &placeholder;
}
- inline void prepareForIncrementalGC(JSRuntime* rt);
+ inline void prepareForIncrementalGC();
/* Check if this arena is in use. */
bool arenaIsInUse(Arena* arena, AllocKind kind) const {
diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp
index 59893fa98..3e222ca6f 100644
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -157,8 +157,11 @@ SortComparatorIntegerIds(jsid a, jsid b, bool* lessOrEqualp)
}
static bool
-EnumerateNativeProperties(JSContext* cx, HandleNativeObject pobj, unsigned flags, Maybe<IdSet>& ht,
- AutoIdVector* props, Handle<UnboxedPlainObject*> unboxed = nullptr)
+EnumerateNativeProperties(JSContext* cx,
+ HandleNativeObject pobj,
+ unsigned flags,
+ Maybe<IdSet>& ht,
+ AutoIdVector* props)
{
bool enumerateSymbols;
if (flags & JSITER_SYMBOLSONLY) {
@@ -220,16 +223,6 @@ EnumerateNativeProperties(JSContext* cx, HandleNativeObject pobj, unsigned flags
return false;
}
- if (unboxed) {
- // If |unboxed| is set then |pobj| is the expando for an unboxed
- // plain object we are enumerating. Add the unboxed properties
- // themselves here since they are all property names that were
- // given to the object before any of the expando's properties.
- MOZ_ASSERT(pobj->is<UnboxedExpandoObject>());
- if (!EnumerateExtraProperties(cx, unboxed, flags, ht, props))
- return false;
- }
-
size_t initialLength = props->length();
/* Collect all unique property names from this object's shape. */
@@ -355,22 +348,12 @@ Snapshot(JSContext* cx, HandleObject pobj_, unsigned flags, AutoIdVector* props)
do {
if (pobj->getOpsEnumerate()) {
- if (pobj->is<UnboxedPlainObject>() && pobj->as<UnboxedPlainObject>().maybeExpando()) {
- // Special case unboxed objects with an expando object.
- RootedNativeObject expando(cx, pobj->as<UnboxedPlainObject>().maybeExpando());
- if (!EnumerateNativeProperties(cx, expando, flags, ht, props,
- pobj.as<UnboxedPlainObject>()))
- {
- return false;
- }
- } else {
- if (!EnumerateExtraProperties(cx, pobj, flags, ht, props))
- return false;
+ if (!EnumerateExtraProperties(cx, pobj, flags, ht, props))
+ return false;
- if (pobj->isNative()) {
- if (!EnumerateNativeProperties(cx, pobj.as<NativeObject>(), flags, ht, props))
- return false;
- }
+ if (pobj->isNative()) {
+ if (!EnumerateNativeProperties(cx, pobj.as<NativeObject>(), flags, ht, props))
+ return false;
}
} else if (pobj->isNative()) {
// Give the object a chance to resolve all lazy properties
@@ -671,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);
@@ -715,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);
@@ -785,11 +768,6 @@ CanCompareIterableObjectToCache(JSObject* obj)
{
if (obj->isNative())
return obj->as<NativeObject>().hasEmptyElements();
- if (obj->is<UnboxedPlainObject>()) {
- if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
- return expando->hasEmptyElements();
- return true;
- }
return false;
}
@@ -943,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;
@@ -1507,7 +1485,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;
@@ -1526,7 +1504,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))
@@ -1549,7 +1528,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))
@@ -1570,7 +1550,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;
@@ -1582,7 +1563,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))
@@ -1603,7 +1584,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/jsnativestack.cpp b/js/src/jsnativestack.cpp
index 166a5a4f7..4e96e01e8 100644
--- a/js/src/jsnativestack.cpp
+++ b/js/src/jsnativestack.cpp
@@ -26,11 +26,7 @@
# include <sys/syscall.h>
# include <sys/types.h>
# include <unistd.h>
-static pid_t
-gettid()
-{
- return syscall(__NR_gettid);
-}
+# define gettid() static_cast<pid_t>(syscall(SYS_gettid))
# endif
#else
@@ -71,33 +67,18 @@ js::GetNativeStackBaseImpl()
# endif
}
-#elif defined(SOLARIS)
+#elif defined(XP_SOLARIS)
#include <ucontext.h>
JS_STATIC_ASSERT(JS_STACK_GROWTH_DIRECTION < 0);
-void*
-js::GetNativeStackBaseImpl()
-{
- stack_t st;
- stack_getbounds(&st);
- return static_cast<char*>(st.ss_sp) + st.ss_size;
-}
-
-#elif defined(AIX)
-
-#include <ucontext.h>
-
-JS_STATIC_ASSERT(JS_STACK_GROWTH_DIRECTION < 0);
-
-void*
+void*
js::GetNativeStackBaseImpl()
{
- ucontext_t context;
- getcontext(&context);
- return static_cast<char*>(context.uc_stack.ss_sp) +
- context.uc_stack.ss_size;
+ stack_t st;
+ stack_getbounds(&st);
+ return static_cast<char*>(st.ss_sp) + st.ss_size;
}
#elif defined(XP_LINUX) && !defined(ANDROID) && defined(__GLIBC__)
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 b17c845bb..ef1291079 100644
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -33,7 +33,6 @@
#include "jsstr.h"
#include "jstypes.h"
#include "jsutil.h"
-#include "jswatchpoint.h"
#include "jswin.h"
#include "jswrapper.h"
@@ -43,6 +42,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"
@@ -470,7 +470,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>();
@@ -869,9 +869,6 @@ static inline JSObject*
CreateThisForFunctionWithGroup(JSContext* cx, HandleObjectGroup group,
NewObjectKind newKind)
{
- if (group->maybeUnboxedLayout() && newKind != SingletonObject)
- return UnboxedPlainObject::create(cx, group, newKind);
-
if (TypeNewScript* newScript = group->newScript()) {
if (newScript->analyzed()) {
// The definite properties analysis has been performed for this
@@ -886,7 +883,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);
@@ -954,7 +951,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));
@@ -1013,13 +1010,7 @@ js::CreateThisForFunction(JSContext* cx, HandleObject callee, HandleObject newTa
JSObject::nonNativeSetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
HandleValue receiver, ObjectOpResult& result)
{
- RootedValue value(cx, v);
- if (MOZ_UNLIKELY(obj->watched())) {
- WatchpointMap* wpmap = cx->compartment()->watchpointMap;
- if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, &value))
- return false;
- }
- return obj->getOpsSetProperty()(cx, obj, id, value, receiver, result);
+ return obj->getOpsSetProperty()(cx, obj, id, v, receiver, result);
}
/* static */ bool
@@ -1145,19 +1136,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;
}
@@ -1166,46 +1156,27 @@ static bool
GetScriptPlainObjectProperties(JSContext* cx, HandleObject obj,
MutableHandle<IdValueVector> properties)
{
- if (obj->is<PlainObject>()) {
- PlainObject* nobj = &obj->as<PlainObject>();
-
- if (!properties.appendN(IdValuePair(), nobj->slotSpan()))
- return false;
+ MOZ_ASSERT(obj->is<PlainObject>());
+ PlainObject* nobj = &obj->as<PlainObject>();
- for (Shape::Range<NoGC> r(nobj->lastProperty()); !r.empty(); r.popFront()) {
- Shape& shape = r.front();
- MOZ_ASSERT(shape.isDataDescriptor());
- uint32_t slot = shape.slot();
- properties[slot].get().id = shape.propid();
- properties[slot].get().value = nobj->getSlot(slot);
- }
-
- for (size_t i = 0; i < nobj->getDenseInitializedLength(); i++) {
- Value v = nobj->getDenseElement(i);
- if (!v.isMagic(JS_ELEMENTS_HOLE) && !properties.append(IdValuePair(INT_TO_JSID(i), v)))
- return false;
- }
+ if (!properties.appendN(IdValuePair(), nobj->slotSpan()))
+ return false;
- return true;
+ for (Shape::Range<NoGC> r(nobj->lastProperty()); !r.empty(); r.popFront()) {
+ Shape& shape = r.front();
+ MOZ_ASSERT(shape.isDataDescriptor());
+ uint32_t slot = shape.slot();
+ properties[slot].get().id = shape.propid();
+ properties[slot].get().value = nobj->getSlot(slot);
}
- if (obj->is<UnboxedPlainObject>()) {
- UnboxedPlainObject* nobj = &obj->as<UnboxedPlainObject>();
-
- const UnboxedLayout& layout = nobj->layout();
- if (!properties.appendN(IdValuePair(), layout.properties().length()))
+ for (size_t i = 0; i < nobj->getDenseInitializedLength(); i++) {
+ Value v = nobj->getDenseElement(i);
+ if (!v.isMagic(JS_ELEMENTS_HOLE) && !properties.append(IdValuePair(INT_TO_JSID(i), v)))
return false;
-
- for (size_t i = 0; i < layout.properties().length(); i++) {
- const UnboxedLayout::Property& property = layout.properties()[i];
- properties[i].get().id = NameToId(property.name);
- properties[i].get().value = nobj->getValue(property);
- }
-
- return true;
}
- MOZ_CRASH("Bad object kind");
+ return true;
}
static bool
@@ -1227,13 +1198,13 @@ js::DeepCloneObjectLiteral(JSContext* cx, HandleObject obj, NewObjectKind newKin
/* NB: Keep this in sync with XDRObjectLiteral. */
MOZ_ASSERT_IF(obj->isSingleton(),
cx->compartment()->behaviors().getSingletonsAsTemplates());
- MOZ_ASSERT(obj->is<PlainObject>() || obj->is<UnboxedPlainObject>() ||
- obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
+ MOZ_ASSERT(obj->is<PlainObject>() ||
+ 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.
@@ -1347,10 +1318,8 @@ js::XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj)
{
if (mode == XDR_ENCODE) {
MOZ_ASSERT(obj->is<PlainObject>() ||
- obj->is<UnboxedPlainObject>() ||
- 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))
@@ -1362,8 +1331,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)
@@ -1451,40 +1423,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;
}
@@ -1510,9 +1483,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");
/*
@@ -1594,10 +1567,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
@@ -1743,7 +1720,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;
}
@@ -1860,10 +1837,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()) {
@@ -1875,15 +1852,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;
@@ -2003,7 +1983,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;
}
@@ -2216,7 +2197,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 {
@@ -2333,16 +2315,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<UnboxedPlainObject>()) {
- if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
- MarkNonNativePropertyFound<NoGC>(propp);
- return true;
- }
- } 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);
@@ -2357,9 +2329,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;
@@ -2386,13 +2367,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;
@@ -2473,7 +2454,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));
@@ -2482,7 +2463,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));
@@ -2491,10 +2472,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);
@@ -2566,7 +2547,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;
}
@@ -2590,11 +2571,6 @@ js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto, JS::Object
break;
}
- // Convert unboxed objects to their native representations before changing
- // their prototype/group, as they depend on the group for their layout.
- if (!MaybeConvertUnboxedObjectToNative(cx, obj))
- return false;
-
Rooted<TaggedProto> taggedProto(cx, TaggedProto(proto));
if (!SetClassAndProto(cx, obj, obj->getClass(), taggedProto))
return false;
@@ -2618,9 +2594,6 @@ js::PreventExtensions(JSContext* cx, HandleObject obj, ObjectOpResult& result, I
if (!obj->nonProxyIsExtensible())
return result.succeed();
- if (!MaybeConvertUnboxedObjectToNative(cx, obj))
- return false;
-
// Force lazy properties to be resolved.
AutoIdVector props(cx);
if (!js::GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props))
@@ -2641,7 +2614,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());
@@ -2781,7 +2754,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;
@@ -2815,68 +2788,6 @@ js::GetPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
return true;
}
-bool
-js::WatchGuts(JSContext* cx, JS::HandleObject origObj, JS::HandleId id, JS::HandleObject callable)
-{
- RootedObject obj(cx, ToWindowIfWindowProxy(origObj));
- if (obj->isNative()) {
- // Use sparse indexes for watched objects, as dense elements can be
- // written to without checking the watchpoint map.
- if (!NativeObject::sparsifyDenseElements(cx, obj.as<NativeObject>()))
- return false;
-
- MarkTypePropertyNonData(cx, obj, id);
- }
-
- WatchpointMap* wpmap = cx->compartment()->watchpointMap;
- if (!wpmap) {
- wpmap = cx->runtime()->new_<WatchpointMap>();
- if (!wpmap || !wpmap->init()) {
- ReportOutOfMemory(cx);
- js_delete(wpmap);
- return false;
- }
- cx->compartment()->watchpointMap = wpmap;
- }
-
- return wpmap->watch(cx, obj, id, js::WatchHandler, callable);
-}
-
-bool
-js::UnwatchGuts(JSContext* cx, JS::HandleObject origObj, JS::HandleId id)
-{
- // Looking in the map for an unsupported object will never hit, so we don't
- // need to check for nativeness or watchable-ness here.
- RootedObject obj(cx, ToWindowIfWindowProxy(origObj));
- if (WatchpointMap* wpmap = cx->compartment()->watchpointMap)
- wpmap->unwatch(obj, id, nullptr, nullptr);
- return true;
-}
-
-bool
-js::WatchProperty(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable)
-{
- if (WatchOp op = obj->getOpsWatch())
- return op(cx, obj, id, callable);
-
- if (!obj->isNative() || obj->is<TypedArrayObject>()) {
- JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
- obj->getClass()->name);
- return false;
- }
-
- return WatchGuts(cx, obj, id, callable);
-}
-
-bool
-js::UnwatchProperty(JSContext* cx, HandleObject obj, HandleId id)
-{
- if (UnwatchOp op = obj->getOpsUnwatch())
- return op(cx, obj, id);
-
- return UnwatchGuts(cx, obj, id);
-}
-
const char*
js::GetObjectClassName(JSContext* cx, HandleObject obj)
{
@@ -2891,24 +2802,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);
@@ -3024,7 +2917,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;
}
@@ -3046,7 +2939,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;
}
@@ -3055,7 +2948,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;
}
@@ -3454,7 +3347,6 @@ JSObject::dump(FILE* fp) const
if (obj->isBoundFunction()) fprintf(fp, " bound_function");
if (obj->isQualifiedVarObj()) fprintf(fp, " varobj");
if (obj->isUnqualifiedVarObj()) fprintf(fp, " unqualified_varobj");
- if (obj->watched()) fprintf(fp, " watched");
if (obj->isIteratedSingleton()) fprintf(fp, " iterated_singleton");
if (obj->isNewGroupUnknown()) fprintf(fp, " new_type_unknown");
if (obj->hasUncacheableProto()) fprintf(fp, " has_uncacheable_proto");
@@ -3714,22 +3606,6 @@ JSObject::allocKindForTenure(const js::Nursery& nursery) const
if (IsProxy(this))
return as<ProxyObject>().allocKindForTenure();
- // Unboxed plain objects are sized according to the data they store.
- if (is<UnboxedPlainObject>()) {
- size_t nbytes = as<UnboxedPlainObject>().layoutDontCheckGeneration().size();
- return GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + nbytes);
- }
-
- // 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>()) {
@@ -3913,10 +3789,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..01845d7e6 100644
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -141,8 +141,6 @@ class JSObject : public js::gc::Cell
js::GetOwnPropertyOp getOpsGetOwnPropertyDescriptor()
const { return getClass()->getOpsGetOwnPropertyDescriptor(); }
js::DeletePropertyOp getOpsDeleteProperty() const { return getClass()->getOpsDeleteProperty(); }
- js::WatchOp getOpsWatch() const { return getClass()->getOpsWatch(); }
- js::UnwatchOp getOpsUnwatch() const { return getClass()->getOpsUnwatch(); }
js::GetElementsOp getOpsGetElements() const { return getClass()->getOpsGetElements(); }
JSNewEnumerateOp getOpsEnumerate() const { return getClass()->getOpsEnumerate(); }
JSFunToStringOp getOpsFunToString() const { return getClass()->getOpsFunToString(); }
@@ -200,8 +198,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,18 +212,13 @@ 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);
- }
-
// A "qualified" varobj is the object on which "qualified" variable
// declarations (i.e., those defined with "var") are kept.
//
@@ -247,8 +240,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 +255,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 +267,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 +281,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 +342,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 +414,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 +427,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 +513,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,
@@ -1029,21 +1025,6 @@ extern bool
DefineFunctions(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs,
DefineAsIntrinsic intrinsic);
-/*
- * Set a watchpoint: a synchronous callback when the given property of the
- * given object is set.
- *
- * Watchpoints are nonstandard and do not fit in well with the way ES6
- * specifies [[Set]]. They are also insufficient for implementing
- * Object.observe.
- */
-extern bool
-WatchProperty(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable);
-
-/* Clear a watchpoint. */
-extern bool
-UnwatchProperty(JSContext* cx, HandleObject obj, HandleId id);
-
/* ES6 draft rev 36 (2015 March 17) 7.1.1 ToPrimitive(vp[, preferredType]) */
extern bool
ToPrimitiveSlow(JSContext* cx, JSType hint, MutableHandleValue vp);
diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h
index 6be4d0d28..98e740142 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
@@ -482,12 +464,6 @@ JSObject::isBoundFunction() const
}
inline bool
-JSObject::watched() const
-{
- return hasAllFlags(js::BaseShape::WATCHED);
-}
-
-inline bool
JSObject::isDelegate() const
{
return hasAllFlags(js::BaseShape::DELEGATE);
@@ -574,48 +550,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 9f914943e..fc7438e3b 100644
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -235,6 +235,8 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
{
uint32_t begin = script->sourceStart();
uint32_t end = script->sourceEnd();
+ uint32_t toStringStart = script->toStringStart();
+ uint32_t toStringEnd = script->toStringEnd();
uint32_t lineno = script->lineno();
uint32_t column = script->column();
@@ -242,6 +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(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
@@ -255,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, 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
@@ -317,6 +326,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
IsStarGenerator,
IsAsync,
HasRest,
+ IsExprBody,
OwnSource,
ExplicitUseStrict,
SelfHosted,
@@ -434,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())
@@ -517,7 +529,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
sourceObject = &enclosingScript->sourceObject()->as<ScriptSourceObject>();
}
- script = JSScript::Create(cx, options, sourceObject, 0, 0);
+ script = JSScript::Create(cx, options, sourceObject, 0, 0, 0, 0);
if (!script)
return false;
@@ -587,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);
@@ -600,6 +614,10 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
return false;
if (!xdr->codeUint32(&script->sourceEnd_))
return false;
+ if (!xdr->codeUint32(&script->toStringStart_))
+ return false;
+ if (!xdr->codeUint32(&script->toStringEnd_))
+ return false;
if (!xdr->codeUint32(&lineno) ||
!xdr->codeUint32(&column) ||
@@ -930,6 +948,8 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript
{
uint32_t begin;
uint32_t end;
+ uint32_t toStringStart;
+ uint32_t toStringEnd;
uint32_t lineno;
uint32_t column;
uint64_t packedFields;
@@ -943,12 +963,16 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript
begin = lazy->begin();
end = lazy->end();
+ toStringStart = lazy->toStringStart();
+ toStringEnd = lazy->toStringEnd();
lineno = lazy->lineno();
column = lazy->column();
packedFields = lazy->packedFields();
}
if (!xdr->codeUint32(&begin) || !xdr->codeUint32(&end) ||
+ !xdr->codeUint32(&toStringStart) ||
+ !xdr->codeUint32(&toStringEnd) ||
!xdr->codeUint32(&lineno) || !xdr->codeUint32(&column) ||
!xdr->codeUint64(&packedFields))
{
@@ -957,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, lineno, column));
+ packedFields, begin, end, toStringStart, lineno, column));
if (!lazy)
return false;
+ lazy->setToStringEnd(toStringEnd);
fun->initLazyScript(lazy);
}
}
@@ -1003,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>();
@@ -1423,11 +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(script->scriptSource()->hasSourceData());
+ return script->scriptSource()->substring(cx, script->sourceStart(), script->sourceEnd());
+}
+
+/* static */ JSFlatString*
+JSScript::sourceDataForToString(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->toStringStart(), script->toStringEnd());
}
UncompressedSourceCache::AutoHoldEntry::AutoHoldEntry()
@@ -2428,9 +2469,16 @@ JSScript::initCompartment(ExclusiveContext* cx)
/* static */ JSScript*
JSScript::Create(ExclusiveContext* cx, const ReadOnlyCompileOptions& options,
- HandleObject sourceObject, uint32_t bufStart, uint32_t bufEnd)
+ HandleObject sourceObject, uint32_t bufStart, uint32_t bufEnd,
+ 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)
@@ -2450,6 +2498,8 @@ JSScript::Create(ExclusiveContext* cx, const ReadOnlyCompileOptions& options,
script->setSourceObject(sourceObject);
script->sourceStart_ = bufStart;
script->sourceEnd_ = bufEnd;
+ script->toStringStart_ = toStringStart;
+ script->toStringEnd_ = toStringEnd;
return script;
}
@@ -2644,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())
@@ -3245,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;
}
@@ -3304,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);
@@ -3382,7 +3435,8 @@ CreateEmptyScriptForClone(JSContext* cx, HandleScript src)
.setNoScriptRval(src->noScriptRval())
.setVersion(src->getVersion());
- return JSScript::Create(cx, options, sourceObject, src->sourceStart(), src->sourceEnd());
+ return JSScript::Create(cx, options, sourceObject, src->sourceStart(), src->sourceEnd(),
+ src->toStringStart(), src->toStringEnd());
}
JSScript*
@@ -3932,7 +3986,8 @@ JSScript::formalLivesInArgumentsObject(unsigned argSlot)
}
LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
- uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column)
+ uint32_t begin, uint32_t end,
+ uint32_t toStringStart, uint32_t lineno, uint32_t column)
: script_(nullptr),
function_(fun),
enclosingScope_(nullptr),
@@ -3941,10 +3996,13 @@ LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
packedFields_(packedFields),
begin_(begin),
end_(end),
+ toStringStart_(toStringStart),
+ toStringEnd_(end),
lineno_(lineno),
column_(column)
{
MOZ_ASSERT(begin <= end);
+ MOZ_ASSERT(toStringStart <= begin);
}
void
@@ -3990,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 lineno, uint32_t column)
+ uint32_t toStringStart, uint32_t lineno, uint32_t column)
{
union {
PackedView p;
@@ -4018,7 +4076,8 @@ LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun,
cx->compartment()->scheduleDelazificationForDebugger();
- return new (res) LazyScript(fun, table.forget(), packed, begin, end, lineno, column);
+ return new (res) LazyScript(fun, table.forget(), packed, begin, end,
+ toStringStart, lineno, column);
}
/* static */ LazyScript*
@@ -4026,7 +4085,8 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
const frontend::AtomVector& closedOverBindings,
Handle<GCVector<JSFunction*, 8>> innerFunctions,
JSVersion version,
- uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column)
+ uint32_t begin, uint32_t end,
+ uint32_t toStringStart, uint32_t lineno, uint32_t column)
{
union {
PackedView p;
@@ -4038,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);
@@ -4049,7 +4110,8 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
p.isDerivedClassConstructor = false;
p.needsHomeObject = false;
- LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column);
+ LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, toStringStart,
+ lineno, column);
if (!res)
return nullptr;
@@ -4070,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 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);
@@ -4079,7 +4141,8 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
// holding this lazy script.
HandleFunction dummyFun = fun;
- LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column);
+ LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, toStringStart,
+ lineno, column);
if (!res)
return nullptr;
@@ -4264,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 87da79901..85eb2938d 100644
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -575,10 +575,6 @@ class ScriptSource
introductionOffset_ = offset;
hasIntroductionOffset_ = true;
}
-
- uint32_t parameterListEnd() const {
- return parameterListEnd_;
- }
};
class ScriptSourceHolder
@@ -857,9 +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. */
+ // 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_
+ // | |
+ // 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 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
@@ -1015,6 +1038,7 @@ 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.
@@ -1030,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);
+ js::HandleObject sourceObject,
+ uint32_t sourceStart, uint32_t sourceEnd,
+ uint32_t toStringStart, uint32_t toStringEnd);
void initCompartment(js::ExclusiveContext* cx);
@@ -1178,6 +1203,14 @@ class JSScript : public js::gc::TenuredCell
return sourceEnd_;
}
+ uint32_t toStringStart() const {
+ return toStringStart_;
+ }
+
+ uint32_t toStringEnd() const {
+ return toStringEnd_;
+ }
+
bool noScriptRval() const {
return noScriptRval_;
}
@@ -1319,6 +1352,13 @@ class JSScript : public js::gc::TenuredCell
hasRest_ = true;
}
+ bool isExprBody() const {
+ return isExprBody_;
+ }
+ void setIsExprBody() {
+ isExprBody_ = true;
+ }
+
void setNeedsHomeObject() {
needsHomeObject_ = true;
}
@@ -1454,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) {
@@ -1481,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>())
@@ -1500,8 +1541,9 @@ class JSScript : public js::gc::TenuredCell
// directly, via lazy arguments or a rest parameter.
bool mayReadFrameArgsDirectly();
- JSFlatString* sourceData(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);
void setSourceObject(JSObject* object);
@@ -1515,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' */
@@ -1924,7 +1968,7 @@ class LazyScript : public gc::TenuredCell
#endif
private:
- static const uint32_t NumClosedOverBindingsBits = 21;
+ static const uint32_t NumClosedOverBindingsBits = 20;
static const uint32_t NumInnerFunctionsBits = 20;
struct PackedView {
@@ -1934,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;
@@ -1960,20 +2009,26 @@ class LazyScript : public gc::TenuredCell
};
// Source location for the script.
+ // See the comment in JSScript for the details.
uint32_t begin_;
uint32_t end_;
+ 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 lineno, uint32_t column);
+ uint32_t begin, uint32_t end, uint32_t toStringStart,
+ uint32_t lineno, uint32_t column);
// Create a LazyScript without initializing the closedOverBindings and the
// innerFunctions. To be GC-safe, the caller must initialize both vectors
// with valid atoms and functions.
static LazyScript* CreateRaw(ExclusiveContext* cx, HandleFunction fun,
uint64_t packedData, uint32_t begin, uint32_t end,
- uint32_t lineno, uint32_t column);
+ uint32_t toStringStart, uint32_t lineno, uint32_t column);
public:
static const uint32_t NumClosedOverBindingsLimit = 1 << NumClosedOverBindingsBits;
@@ -1985,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 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
@@ -2000,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 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_;
}
@@ -2087,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;
}
@@ -2173,6 +2235,12 @@ class LazyScript : public gc::TenuredCell
uint32_t end() const {
return end_;
}
+ uint32_t toStringStart() const {
+ return toStringStart_;
+ }
+ uint32_t toStringEnd() const {
+ return toStringEnd_;
+ }
uint32_t lineno() const {
return lineno_;
}
@@ -2180,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;
@@ -2199,7 +2273,8 @@ class LazyScript : public gc::TenuredCell
};
/* If this fails, add/remove padding within LazyScript. */
-JS_STATIC_ASSERT(sizeof(LazyScript) % js::gc::CellSize == 0);
+static_assert(sizeof(LazyScript) % js::gc::CellSize == 0,
+ "Size of LazyScript must be an integral multiple of js::gc::CellSize");
struct ScriptAndCounts
{
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 4151d012b..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,
@@ -1970,14 +1974,14 @@ js::str_trim(JSContext* cx, unsigned argc, Value* vp)
}
bool
-js::str_trimLeft(JSContext* cx, unsigned argc, Value* vp)
+js::str_trimStart(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return TrimString(cx, args, true, false);
}
bool
-js::str_trimRight(JSContext* cx, unsigned argc, Value* vp)
+js::str_trimEnd(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return TrimString(cx, args, false, true);
@@ -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)
{
@@ -2568,8 +2572,10 @@ static const JSFunctionSpec string_methods[] = {
JS_FN("startsWith", str_startsWith, 1,0),
JS_FN("endsWith", str_endsWith, 1,0),
JS_FN("trim", str_trim, 0,0),
- JS_FN("trimLeft", str_trimLeft, 0,0),
- JS_FN("trimRight", str_trimRight, 0,0),
+ JS_FN("trimLeft", str_trimStart, 0,0),
+ JS_FN("trimStart", str_trimStart, 0,0),
+ JS_FN("trimRight", str_trimEnd, 0,0),
+ JS_FN("trimEnd", str_trimEnd, 0,0),
JS_FN("toLocaleLowerCase", str_toLocaleLowerCase, 0,0),
JS_FN("toLocaleUpperCase", str_toLocaleUpperCase, 0,0),
JS_SELF_HOSTED_FN("localeCompare", "String_localeCompare", 1,0),
@@ -2881,8 +2887,10 @@ static const JSFunctionSpec string_static_methods[] = {
JS_SELF_HOSTED_FN("startsWith", "String_static_startsWith", 2,0),
JS_SELF_HOSTED_FN("endsWith", "String_static_endsWith", 2,0),
JS_SELF_HOSTED_FN("trim", "String_static_trim", 1,0),
- JS_SELF_HOSTED_FN("trimLeft", "String_static_trimLeft", 1,0),
- JS_SELF_HOSTED_FN("trimRight", "String_static_trimRight", 1,0),
+ JS_SELF_HOSTED_FN("trimLeft", "String_static_trimStart", 1,0),
+ JS_SELF_HOSTED_FN("trimStart", "String_static_trimStart", 1,0),
+ JS_SELF_HOSTED_FN("trimRight", "String_static_trimEnd", 1,0),
+ JS_SELF_HOSTED_FN("trimEnd", "String_static_trimEnd", 1,0),
JS_SELF_HOSTED_FN("toLocaleLowerCase","String_static_toLocaleLowerCase",1,0),
JS_SELF_HOSTED_FN("toLocaleUpperCase","String_static_toLocaleUpperCase",1,0),
JS_SELF_HOSTED_FN("normalize", "String_static_normalize", 1,0),
@@ -2897,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*
@@ -2906,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;
@@ -3070,8 +3081,11 @@ js::ValueToSource(JSContext* cx, HandleValue v)
return ToString<CanGC>(cx, v);
}
-
+#if JS_HAS_TOSOURCE
return ObjectToSource(cx, obj);
+#else
+ return ToString<CanGC>(cx, v);
+#endif
}
JSString*
diff --git a/js/src/jsstr.h b/js/src/jsstr.h
index 118118839..68175c826 100644
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -367,10 +367,10 @@ extern bool
str_trim(JSContext* cx, unsigned argc, Value* vp);
extern bool
-str_trimLeft(JSContext* cx, unsigned argc, Value* vp);
+str_trimStart(JSContext* cx, unsigned argc, Value* vp);
extern bool
-str_trimRight(JSContext* cx, unsigned argc, Value* vp);
+str_trimEnd(JSContext* cx, unsigned argc, Value* vp);
extern bool
str_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp);
@@ -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/jstypes.h b/js/src/jstypes.h
index 75774e5b8..04ffbe00d 100644
--- a/js/src/jstypes.h
+++ b/js/src/jstypes.h
@@ -147,13 +147,7 @@
# define JS_64BIT
# endif
#elif defined(__GNUC__)
-/* Additional GCC defines are when running on Solaris, AIX, and HPUX */
-# if defined(__x86_64__) || defined(__sparcv9) || \
- defined(__64BIT__) || defined(__LP64__)
-# define JS_64BIT
-# endif
-#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) /* Sun Studio C/C++ */
-# if defined(__x86_64) || defined(__sparcv9)
+# if defined(__x86_64__) || defined(__LP64__)
# define JS_64BIT
# endif
#elif defined(__xlc__) || defined(__xlC__) /* IBM XL C/C++ */
diff --git a/js/src/jsversion.h b/js/src/jsversion.h
index 8bdfe47b6..cf4c6e73a 100644
--- a/js/src/jsversion.h
+++ b/js/src/jsversion.h
@@ -12,7 +12,6 @@
*/
#define JS_HAS_STR_HTML_HELPERS 1 /* (no longer used) */
#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */
-#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */
#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */
#define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */
#define JS_HAS_UNEVAL 1 /* has uneval() top-level function */
diff --git a/js/src/jswatchpoint.cpp b/js/src/jswatchpoint.cpp
deleted file mode 100644
index 3cf43e219..000000000
--- a/js/src/jswatchpoint.cpp
+++ /dev/null
@@ -1,245 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "jswatchpoint.h"
-
-#include "jsatom.h"
-#include "jscompartment.h"
-#include "jsfriendapi.h"
-
-#include "gc/Marking.h"
-#include "vm/Shape.h"
-
-#include "jsgcinlines.h"
-
-using namespace js;
-using namespace js::gc;
-
-inline HashNumber
-WatchKeyHasher::hash(const Lookup& key)
-{
- return MovableCellHasher<PreBarrieredObject>::hash(key.object) ^ HashId(key.id);
-}
-
-namespace {
-
-class AutoEntryHolder {
- typedef WatchpointMap::Map Map;
- Generation gen;
- Map& map;
- Map::Ptr p;
- RootedObject obj;
- RootedId id;
-
- public:
- AutoEntryHolder(JSContext* cx, Map& map, Map::Ptr p)
- : gen(map.generation()), map(map), p(p), obj(cx, p->key().object), id(cx, p->key().id)
- {
- MOZ_ASSERT(!p->value().held);
- p->value().held = true;
- }
-
- ~AutoEntryHolder() {
- if (gen != map.generation())
- p = map.lookup(WatchKey(obj, id));
- if (p)
- p->value().held = false;
- }
-};
-
-} /* anonymous namespace */
-
-bool
-WatchpointMap::init()
-{
- return map.init();
-}
-
-bool
-WatchpointMap::watch(JSContext* cx, HandleObject obj, HandleId id,
- JSWatchPointHandler handler, HandleObject closure)
-{
- MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id) || JSID_IS_SYMBOL(id));
-
- if (!obj->setWatched(cx))
- return false;
-
- Watchpoint w(handler, closure, false);
- if (!map.put(WatchKey(obj, id), w)) {
- ReportOutOfMemory(cx);
- return false;
- }
- /*
- * For generational GC, we don't need to post-barrier writes to the
- * hashtable here because we mark all watchpoints as part of root marking in
- * markAll().
- */
- return true;
-}
-
-void
-WatchpointMap::unwatch(JSObject* obj, jsid id,
- JSWatchPointHandler* handlerp, JSObject** closurep)
-{
- if (Map::Ptr p = map.lookup(WatchKey(obj, id))) {
- if (handlerp)
- *handlerp = p->value().handler;
- if (closurep) {
- // Read barrier to prevent an incorrectly gray closure from escaping the
- // watchpoint. See the comment before UnmarkGrayChildren in gc/Marking.cpp
- JS::ExposeObjectToActiveJS(p->value().closure);
- *closurep = p->value().closure;
- }
- map.remove(p);
- }
-}
-
-void
-WatchpointMap::unwatchObject(JSObject* obj)
-{
- for (Map::Enum e(map); !e.empty(); e.popFront()) {
- Map::Entry& entry = e.front();
- if (entry.key().object == obj)
- e.removeFront();
- }
-}
-
-void
-WatchpointMap::clear()
-{
- map.clear();
-}
-
-bool
-WatchpointMap::triggerWatchpoint(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
-{
- Map::Ptr p = map.lookup(WatchKey(obj, id));
- if (!p || p->value().held)
- return true;
-
- AutoEntryHolder holder(cx, map, p);
-
- /* Copy the entry, since GC would invalidate p. */
- JSWatchPointHandler handler = p->value().handler;
- RootedObject closure(cx, p->value().closure);
-
- /* Determine the property's old value. */
- Value old;
- old.setUndefined();
- if (obj->isNative()) {
- NativeObject* nobj = &obj->as<NativeObject>();
- if (Shape* shape = nobj->lookup(cx, id)) {
- if (shape->hasSlot())
- old = nobj->getSlot(shape->slot());
- }
- }
-
- // Read barrier to prevent an incorrectly gray closure from escaping the
- // watchpoint. See the comment before UnmarkGrayChildren in gc/Marking.cpp
- JS::ExposeObjectToActiveJS(closure);
-
- /* Call the handler. */
- return handler(cx, obj, id, old, vp.address(), closure);
-}
-
-bool
-WatchpointMap::markIteratively(JSTracer* trc)
-{
- bool marked = false;
- for (Map::Enum e(map); !e.empty(); e.popFront()) {
- Map::Entry& entry = e.front();
- JSObject* priorKeyObj = entry.key().object;
- jsid priorKeyId(entry.key().id.get());
- bool objectIsLive =
- IsMarked(trc->runtime(), const_cast<PreBarrieredObject*>(&entry.key().object));
- if (objectIsLive || entry.value().held) {
- if (!objectIsLive) {
- TraceEdge(trc, const_cast<PreBarrieredObject*>(&entry.key().object),
- "held Watchpoint object");
- marked = true;
- }
-
- MOZ_ASSERT(JSID_IS_STRING(priorKeyId) ||
- JSID_IS_INT(priorKeyId) ||
- JSID_IS_SYMBOL(priorKeyId));
- TraceEdge(trc, const_cast<PreBarrieredId*>(&entry.key().id), "WatchKey::id");
-
- if (entry.value().closure && !IsMarked(trc->runtime(), &entry.value().closure)) {
- TraceEdge(trc, &entry.value().closure, "Watchpoint::closure");
- marked = true;
- }
-
- /* We will sweep this entry in sweepAll if !objectIsLive. */
- if (priorKeyObj != entry.key().object || priorKeyId != entry.key().id)
- e.rekeyFront(WatchKey(entry.key().object, entry.key().id));
- }
- }
- return marked;
-}
-
-void
-WatchpointMap::markAll(JSTracer* trc)
-{
- for (Map::Enum e(map); !e.empty(); e.popFront()) {
- Map::Entry& entry = e.front();
- WatchKey key = entry.key();
- WatchKey prior = key;
- MOZ_ASSERT(JSID_IS_STRING(prior.id) || JSID_IS_INT(prior.id) || JSID_IS_SYMBOL(prior.id));
-
- TraceEdge(trc, const_cast<PreBarrieredObject*>(&key.object),
- "held Watchpoint object");
- TraceEdge(trc, const_cast<PreBarrieredId*>(&key.id), "WatchKey::id");
- TraceEdge(trc, &entry.value().closure, "Watchpoint::closure");
-
- if (prior.object != key.object || prior.id != key.id)
- e.rekeyFront(key);
- }
-}
-
-void
-WatchpointMap::sweepAll(JSRuntime* rt)
-{
- for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
- if (WatchpointMap* wpmap = c->watchpointMap)
- wpmap->sweep();
- }
-}
-
-void
-WatchpointMap::sweep()
-{
- for (Map::Enum e(map); !e.empty(); e.popFront()) {
- Map::Entry& entry = e.front();
- JSObject* obj(entry.key().object);
- if (IsAboutToBeFinalizedUnbarriered(&obj)) {
- MOZ_ASSERT(!entry.value().held);
- e.removeFront();
- } else if (obj != entry.key().object) {
- e.rekeyFront(WatchKey(obj, entry.key().id));
- }
- }
-}
-
-void
-WatchpointMap::traceAll(WeakMapTracer* trc)
-{
- JSRuntime* rt = trc->context;
- for (CompartmentsIter comp(rt, SkipAtoms); !comp.done(); comp.next()) {
- if (WatchpointMap* wpmap = comp->watchpointMap)
- wpmap->trace(trc);
- }
-}
-
-void
-WatchpointMap::trace(WeakMapTracer* trc)
-{
- for (Map::Range r = map.all(); !r.empty(); r.popFront()) {
- Map::Entry& entry = r.front();
- trc->trace(nullptr,
- JS::GCCellPtr(entry.key().object.get()),
- JS::GCCellPtr(entry.value().closure.get()));
- }
-}
diff --git a/js/src/jswatchpoint.h b/js/src/jswatchpoint.h
deleted file mode 100644
index bba6c38ce..000000000
--- a/js/src/jswatchpoint.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef jswatchpoint_h
-#define jswatchpoint_h
-
-#include "jsalloc.h"
-
-#include "gc/Barrier.h"
-#include "js/HashTable.h"
-
-namespace js {
-
-struct WeakMapTracer;
-
-struct WatchKey {
- WatchKey() {}
- WatchKey(JSObject* obj, jsid id) : object(obj), id(id) {}
- WatchKey(const WatchKey& key) : object(key.object.get()), id(key.id.get()) {}
-
- // These are traced unconditionally during minor GC, so do not require
- // post-barriers.
- PreBarrieredObject object;
- PreBarrieredId id;
-
- bool operator!=(const WatchKey& other) const {
- return object != other.object || id != other.id;
- }
-};
-
-typedef bool
-(* JSWatchPointHandler)(JSContext* cx, JSObject* obj, jsid id, const JS::Value& old,
- JS::Value* newp, void* closure);
-
-struct Watchpoint {
- JSWatchPointHandler handler;
- PreBarrieredObject closure; /* This is always marked in minor GCs and so doesn't require a postbarrier. */
- bool held; /* true if currently running handler */
- Watchpoint(JSWatchPointHandler handler, JSObject* closure, bool held)
- : handler(handler), closure(closure), held(held) {}
-};
-
-struct WatchKeyHasher
-{
- typedef WatchKey Lookup;
- static inline js::HashNumber hash(const Lookup& key);
-
- static bool match(const WatchKey& k, const Lookup& l) {
- return MovableCellHasher<PreBarrieredObject>::match(k.object, l.object) &&
- DefaultHasher<PreBarrieredId>::match(k.id, l.id);
- }
-
- static void rekey(WatchKey& k, const WatchKey& newKey) {
- k.object.unsafeSet(newKey.object);
- k.id.unsafeSet(newKey.id);
- }
-};
-
-class WatchpointMap {
- public:
- typedef HashMap<WatchKey, Watchpoint, WatchKeyHasher, SystemAllocPolicy> Map;
-
- bool init();
- bool watch(JSContext* cx, HandleObject obj, HandleId id,
- JSWatchPointHandler handler, HandleObject closure);
- void unwatch(JSObject* obj, jsid id,
- JSWatchPointHandler* handlerp, JSObject** closurep);
- void unwatchObject(JSObject* obj);
- void clear();
-
- bool triggerWatchpoint(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp);
-
- bool markIteratively(JSTracer* trc);
- void markAll(JSTracer* trc);
- static void sweepAll(JSRuntime* rt);
- void sweep();
-
- static void traceAll(WeakMapTracer* trc);
- void trace(WeakMapTracer* trc);
-
- private:
- Map map;
-};
-
-} // namespace js
-
-#endif /* jswatchpoint_h */
diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h
index 3c73979f8..32336c68b 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);
@@ -270,6 +270,8 @@ class JS_FRIEND_API(OpaqueCrossCompartmentWrapper) : public CrossCompartmentWrap
virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper, ESClass* cls) const override;
virtual bool isArray(JSContext* cx, HandleObject obj,
JS::IsArrayAnswer* answer) const override;
+ virtual bool hasInstance(JSContext* cx, HandleObject wrapper,
+ MutableHandleValue v, bool* bp) const override;
virtual const char* className(JSContext* cx, HandleObject wrapper) const override;
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override;
@@ -313,13 +315,6 @@ class JS_FRIEND_API(SecurityWrapper) : public Base
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
- // Allow isCallable and isConstructor. They used to be class-level, and so could not be guarded
- // against.
-
- virtual bool watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
- JS::HandleObject callable) const override;
- virtual bool unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id) const override;
-
/*
* Allow our subclasses to select the superclass behavior they want without
* needing to specify an exact superclass.
diff --git a/js/src/moz.build b/js/src/moz.build
index a3283b5d6..d1e80b4ce 100644
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -291,7 +291,6 @@ UNIFIED_SOURCES += [
'jspropertytree.cpp',
'jsscript.cpp',
'jsstr.cpp',
- 'jswatchpoint.cpp',
'jsweakmap.cpp',
'perf/jsperf.cpp',
'proxy/BaseProxyHandler.cpp',
@@ -355,7 +354,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 +618,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/old-configure.in b/js/src/old-configure.in
index 8abea5956..6566ce05e 100644
--- a/js/src/old-configure.in
+++ b/js/src/old-configure.in
@@ -206,10 +206,6 @@ case "$target" in
# -Zc:sizedDealloc- disables C++14 global sized deallocation (see bug 1160146)
CXXFLAGS="$CXXFLAGS -Zc:sizedDealloc-"
-
- # Disable C++11 thread-safe statics due to crashes on XP (bug 1204752)
- # See https://connect.microsoft.com/VisualStudio/feedback/details/1789709/visual-c-2015-runtime-broken-on-windows-server-2003-c-11-magic-statics
- CXXFLAGS="$CXXFLAGS -Zc:threadSafeInit-"
;;
esac
AC_SUBST(MSVS_VERSION)
@@ -927,6 +923,14 @@ case "$target" in
fi
;;
+i*86-*-solaris*)
+ MOZ_FIX_LINK_PATHS="-L${DIST}/bin -R'\$\$ORIGIN':/usr/gcc/7/lib"
+ ;;
+
+x86_64-*-solaris*)
+ MOZ_FIX_LINK_PATHS="-L${DIST}/bin -R'\$\$ORIGIN':/usr/gcc/7/lib/amd64"
+ ;;
+
esac
dnl Only one oddball right now (QNX), but this gives us flexibility
diff --git a/js/src/proxy/BaseProxyHandler.cpp b/js/src/proxy/BaseProxyHandler.cpp
index 8d5f30a19..423aa8671 100644
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -419,20 +419,6 @@ BaseProxyHandler::setImmutablePrototype(JSContext* cx, HandleObject proxy, bool*
}
bool
-BaseProxyHandler::watch(JSContext* cx, HandleObject proxy, HandleId id, HandleObject callable) const
-{
- JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
- proxy->getClass()->name);
- return false;
-}
-
-bool
-BaseProxyHandler::unwatch(JSContext* cx, HandleObject proxy, HandleId id) const
-{
- return true;
-}
-
-bool
BaseProxyHandler::getElements(JSContext* cx, HandleObject proxy, uint32_t begin, uint32_t end,
ElementAdder* adder) const
{
diff --git a/js/src/proxy/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/OpaqueCrossCompartmentWrapper.cpp b/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp
index ff3f4145c..02bf237ff 100644
--- a/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp
+++ b/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp
@@ -175,6 +175,14 @@ OpaqueCrossCompartmentWrapper::isArray(JSContext* cx, HandleObject obj,
return true;
}
+bool OpaqueCrossCompartmentWrapper::hasInstance(JSContext* cx,
+ HandleObject wrapper,
+ MutableHandleValue v,
+ bool* bp) const {
+ *bp = false;
+ return true;
+}
+
const char*
OpaqueCrossCompartmentWrapper::className(JSContext* cx,
HandleObject proxy) const
diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp
index b43fd02d2..6b3a3d1e9 100644
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -505,20 +505,6 @@ Proxy::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp
JSObject * const TaggedProto::LazyProto = reinterpret_cast<JSObject*>(0x1);
/* static */ bool
-Proxy::watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleObject callable)
-{
- JS_CHECK_RECURSION(cx, return false);
- return proxy->as<ProxyObject>().handler()->watch(cx, proxy, id, callable);
-}
-
-/* static */ bool
-Proxy::unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id)
-{
- JS_CHECK_RECURSION(cx, return false);
- return proxy->as<ProxyObject>().handler()->unwatch(cx, proxy, id);
-}
-
-/* static */ bool
Proxy::getElements(JSContext* cx, HandleObject proxy, uint32_t begin, uint32_t end,
ElementAdder* adder)
{
@@ -699,18 +685,6 @@ js::proxy_Construct(JSContext* cx, unsigned argc, Value* vp)
}
bool
-js::proxy_Watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable)
-{
- return Proxy::watch(cx, obj, id, callable);
-}
-
-bool
-js::proxy_Unwatch(JSContext* cx, HandleObject obj, HandleId id)
-{
- return Proxy::unwatch(cx, obj, id);
-}
-
-bool
js::proxy_GetElements(JSContext* cx, HandleObject proxy, uint32_t begin, uint32_t end,
ElementAdder* adder)
{
@@ -750,7 +724,6 @@ const ObjectOps js::ProxyObjectOps = {
js::proxy_SetProperty,
js::proxy_GetOwnPropertyDescriptor,
js::proxy_DeleteProperty,
- js::proxy_Watch, js::proxy_Unwatch,
js::proxy_GetElements,
nullptr, /* enumerate */
js::proxy_FunToString
@@ -774,7 +747,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 +769,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/Proxy.h b/js/src/proxy/Proxy.h
index 89909a085..4a8ecf8ab 100644
--- a/js/src/proxy/Proxy.h
+++ b/js/src/proxy/Proxy.h
@@ -65,9 +65,6 @@ class Proxy
static bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g);
static bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp);
- static bool watch(JSContext* cx, HandleObject proxy, HandleId id, HandleObject callable);
- static bool unwatch(JSContext* cx, HandleObject proxy, HandleId id);
-
static bool getElements(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end,
ElementAdder* adder);
diff --git a/js/src/proxy/ScriptedProxyHandler.cpp b/js/src/proxy/ScriptedProxyHandler.cpp
index 776547337..0e25f470c 100644
--- a/js/src/proxy/ScriptedProxyHandler.cpp
+++ b/js/src/proxy/ScriptedProxyHandler.cpp
@@ -8,8 +8,6 @@
#include "jsapi.h"
-#include "vm/Interpreter.h" // For InstanceOfOperator
-
#include "jsobjinlines.h"
#include "vm/NativeObject-inl.h"
@@ -1230,7 +1228,7 @@ bool
ScriptedProxyHandler::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v,
bool* bp) const
{
- return InstanceOfOperator(cx, proxy, v, bp);
+ return InstanceofOperator(cx, proxy, v, bp);
}
bool
diff --git a/js/src/proxy/SecurityWrapper.cpp b/js/src/proxy/SecurityWrapper.cpp
index 710faf9b0..71d22fca6 100644
--- a/js/src/proxy/SecurityWrapper.cpp
+++ b/js/src/proxy/SecurityWrapper.cpp
@@ -130,24 +130,5 @@ SecurityWrapper<Base>::defineProperty(JSContext* cx, HandleObject wrapper, Handl
return Base::defineProperty(cx, wrapper, id, desc, result);
}
-template <class Base>
-bool
-SecurityWrapper<Base>::watch(JSContext* cx, HandleObject proxy,
- HandleId id, HandleObject callable) const
-{
- ReportUnwrapDenied(cx);
- return false;
-}
-
-template <class Base>
-bool
-SecurityWrapper<Base>::unwatch(JSContext* cx, HandleObject proxy,
- HandleId id) const
-{
- ReportUnwrapDenied(cx);
- return false;
-}
-
-
template class js::SecurityWrapper<Wrapper>;
template class js::SecurityWrapper<CrossCompartmentWrapper>;
diff --git a/js/src/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/OSObject.cpp b/js/src/shell/OSObject.cpp
index 846ec7b15..4fb3d4e77 100644
--- a/js/src/shell/OSObject.cpp
+++ b/js/src/shell/OSObject.cpp
@@ -184,6 +184,11 @@ FileAsTypedArray(JSContext* cx, JS::HandleString pathnameStr)
return nullptr;
JS_ReportErrorUTF8(cx, "can't seek start of %s", pathname.ptr());
} else {
+ if (len > INT32_MAX) {
+ JS_ReportErrorUTF8(cx, "file %s is too large for a Uint8Array",
+ pathname.ptr());
+ return nullptr;
+ }
obj = JS_NewUint8Array(cx, len);
if (!obj)
return nullptr;
diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
index 8d144417a..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,15 +7284,11 @@ SetContextOptions(JSContext* cx, const OptionParser& op)
.setWasm(enableWasm)
.setWasmAlwaysBaseline(enableWasmAlwaysBaseline)
.setNativeRegExp(enableNativeRegExp)
- .setUnboxedArrays(enableUnboxedArrays)
.setArrayProtoValues(enableArrayProtoValues);
if (op.getBoolOption("wasm-check-bce"))
jit::JitOptions.wasmAlwaysCheckBounds = true;
- if (op.getBoolOption("no-unboxed-objects"))
- jit::JitOptions.disableUnboxedObjects = true;
-
if (const char* str = op.getStringOption("cache-ir-stubs")) {
if (strcmp(str, "on") == 0)
jit::JitOptions.disableCacheIR = false;
@@ -7542,7 +7552,6 @@ SetWorkerContextOptions(JSContext* cx)
.setWasm(enableWasm)
.setWasmAlwaysBaseline(enableWasmAlwaysBaseline)
.setNativeRegExp(enableNativeRegExp)
- .setUnboxedArrays(enableUnboxedArrays)
.setArrayProtoValues(enableArrayProtoValues);
cx->setOffthreadIonCompilationEnabled(offthreadCompilation);
cx->profilingScripts = enableCodeCoverage || enableDisassemblyDumps;
@@ -7712,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..8bd0512c5 100644
--- a/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js
+++ b/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js
@@ -1,7 +1,7 @@
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
// Generated by make_intl_data.py. DO NOT EDIT.
-// tzdata version = 2018e
+// tzdata version = 2019c
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..c760fd85e 100644
--- a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js
+++ b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js
@@ -1,7 +1,7 @@
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
// Generated by make_intl_data.py. DO NOT EDIT.
-// tzdata version = 2018e
+// tzdata version = 2019c
const tzMapper = [
x => x,
diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js
index 66ef3075d..38db5e77d 100644
--- a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js
+++ b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js
@@ -1,7 +1,7 @@
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
// Generated by make_intl_data.py. DO NOT EDIT.
-// tzdata version = 2018e
+// tzdata version = 2019c
const tzMapper = [
x => x,
diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js b/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js
index 8d44204bc..64a25c241 100644
--- a/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js
+++ b/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js
@@ -1,7 +1,7 @@
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
// Generated by make_intl_data.py. DO NOT EDIT.
-// tzdata version = 2018e
+// tzdata version = 2019c
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_2017/Function/Object-toSource.js b/js/src/tests/ecma_2017/Function/Object-toSource.js
new file mode 100644
index 000000000..33b9e588e
--- /dev/null
+++ b/js/src/tests/ecma_2017/Function/Object-toSource.js
@@ -0,0 +1,370 @@
+var BUGNUMBER = 1317400;
+var summary = "Function string representation in Object.prototype.toSource";
+
+print(BUGNUMBER + ": " + summary);
+
+// Methods.
+
+assertEq(({ foo(){} }).toSource(),
+ "({foo(){}})");
+assertEq(({ *foo(){} }).toSource(),
+ "({*foo(){}})");
+assertEq(({ async foo(){} }).toSource(),
+ "({async foo(){}})");
+
+assertEq(({ 1(){} }).toSource(),
+ "({1(){}})");
+
+// Methods with more spacing.
+// Spacing is kept.
+
+assertEq(({ foo (){} }).toSource(),
+ "({foo (){}})");
+assertEq(({ foo () {} }).toSource(),
+ "({foo () {}})");
+
+// Methods with computed name.
+// Method syntax is composed.
+
+let name = "foo";
+assertEq(({ [name](){} }).toSource(),
+ "({foo(){}})");
+assertEq(({ *[name](){} }).toSource(),
+ "({*foo(){}})");
+assertEq(({ async [name](){} }).toSource(),
+ "({async foo(){}})");
+
+assertEq(({ [ Symbol.iterator ](){} }).toSource(),
+ "({[Symbol.iterator](){}})");
+
+// Accessors.
+
+assertEq(({ get foo(){} }).toSource(),
+ "({get foo(){}})");
+assertEq(({ set foo(v){} }).toSource(),
+ "({set foo(v){}})");
+
+// Accessors with computed name.
+// Method syntax is composed.
+
+assertEq(({ get [name](){} }).toSource(),
+ "({get foo(){}})");
+assertEq(({ set [name](v){} }).toSource(),
+ "({set foo(v){}})");
+
+assertEq(({ get [ Symbol.iterator ](){} }).toSource(),
+ "({get [Symbol.iterator](){}})");
+assertEq(({ set [ Symbol.iterator ](v){} }).toSource(),
+ "({set [Symbol.iterator](v){}})");
+
+// Getter and setter with same name.
+// Getter always comes before setter.
+
+assertEq(({ get foo(){}, set foo(v){} }).toSource(),
+ "({get foo(){}, set foo(v){}})");
+assertEq(({ set foo(v){}, get foo(){} }).toSource(),
+ "({get foo(){}, set foo(v){}})");
+
+// Normal properties.
+
+assertEq(({ foo: function(){} }).toSource(),
+ "({foo:(function(){})})");
+assertEq(({ foo: function bar(){} }).toSource(),
+ "({foo:(function bar(){})})");
+assertEq(({ foo: function*(){} }).toSource(),
+ "({foo:(function*(){})})");
+assertEq(({ foo: async function(){} }).toSource(),
+ "({foo:(async function(){})})");
+
+// Normal properties with computed name.
+
+assertEq(({ [ Symbol.iterator ]: function(){} }).toSource(),
+ "({[Symbol.iterator]:(function(){})})");
+
+// Dynamically defined properties with function expression.
+// Never become a method syntax.
+
+let obj = {};
+obj.foo = function() {};
+assertEq(obj.toSource(),
+ "({foo:(function() {})})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: function() {}});
+assertEq(obj.toSource(),
+ "({})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({foo:(function() {})})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: function bar() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({foo:(function bar() {})})");
+
+obj = {};
+Object.defineProperty(obj, Symbol.iterator, {value: function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({[Symbol.iterator]:(function() {})})");
+
+// Dynamically defined property with other object's method.
+// Method syntax is composed.
+
+let method = ({foo() {}}).foo;
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: method, enumerable: true});
+assertEq(obj.toSource(),
+ "({foo() {}})");
+
+obj = {};
+Object.defineProperty(obj, "bar", {value: method, enumerable: true});
+assertEq(obj.toSource(),
+ "({bar() {}})");
+
+method = ({*foo() {}}).foo;
+
+obj = {};
+Object.defineProperty(obj, "bar", {value: method, enumerable: true});
+assertEq(obj.toSource(),
+ "({*bar() {}})");
+
+method = ({async foo() {}}).foo;
+
+obj = {};
+Object.defineProperty(obj, "bar", {value: method, enumerable: true});
+assertEq(obj.toSource(),
+ "({async bar() {}})");
+
+// Dynamically defined accessors.
+// Accessor syntax is composed.
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+obj = {};
+Object.defineProperty(obj, Symbol.iterator, {get: function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({get [Symbol.iterator]() {}})");
+
+obj = {};
+Object.defineProperty(obj, Symbol.iterator, {set: function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({set [Symbol.iterator]() {}})");
+
+// Dynamically defined accessors with other object's accessors.
+// Accessor syntax is composed.
+
+let accessor = Object.getOwnPropertyDescriptor({ get foo() {} }, "foo").get;
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ get bar() {} }, "bar").get;
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ set foo(v) {} }, "foo").set;
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo(v) {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ set bar(v) {} }, "bar").set;
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo(v) {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ get foo() {} }, "foo").get;
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ get bar() {} }, "bar").get;
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ set foo(v) {} }, "foo").set;
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo(v) {}})");
+
+accessor = Object.getOwnPropertyDescriptor({ set bar(v) {} }, "bar").set;
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo(v) {}})");
+
+// Methods with proxy.
+// Treated as normal property.
+
+method = ({foo() {}}).foo;
+let handler = {
+ get(that, name) {
+ if (name == "toSource") {
+ return function() {
+ return that.toSource();
+ };
+ }
+ return that[name];
+ }
+};
+let proxy = new Proxy(method, handler);
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: proxy, enumerable: true});
+assertEq(obj.toSource(),
+ "({foo:foo() {}})");
+
+// Accessors with proxy.
+// Accessor syntax is composed.
+
+accessor = Object.getOwnPropertyDescriptor({ get foo() {} }, "foo").get;
+proxy = new Proxy(accessor, handler);
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: proxy, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: proxy, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+// Methods from other global.
+// Treated as normal property.
+
+let g = newGlobal();
+
+method = g.eval("({ foo() {} }).foo");
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: method, enumerable: true});
+assertEq(obj.toSource(),
+ "({foo:foo() {}})");
+
+// Accessors from other global.
+// Accessor syntax is composed.
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ get foo() {} }, 'foo').get");
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ get bar() {} }, 'bar').get");
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ set foo(v) {} }, 'foo').set");
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo(v) {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ set bar(v) {} }, 'bar').set");
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo(v) {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ get foo() {} }, 'foo').get");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ get bar() {} }, 'bar').get");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ set foo(v) {} }, 'foo').set");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo(v) {}})");
+
+accessor = g.eval("Object.getOwnPropertyDescriptor({ set bar(v) {} }, 'bar').set");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: accessor, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo(v) {}})");
+
+// **** Some weird cases ****
+
+// Accessors with generator or async.
+
+obj = {};
+Object.defineProperty(obj, "foo", {get: function*() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({get foo() {}})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {set: async function() {}, enumerable: true});
+assertEq(obj.toSource(),
+ "({set foo() {}})");
+
+// Modified toSource.
+
+obj = { foo() {} };
+obj.foo.toSource = () => "hello";
+assertEq(obj.toSource(),
+ "({hello})");
+
+obj = { foo() {} };
+obj.foo.toSource = () => "bar() {}";
+assertEq(obj.toSource(),
+ "({bar() {}})");
+
+// Modified toSource with different method name.
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: function bar() {}, enumerable: true});
+obj.foo.toSource = () => "hello";
+assertEq(obj.toSource(),
+ "({foo:hello})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: function* bar() {}, enumerable: true});
+obj.foo.toSource = () => "hello";
+assertEq(obj.toSource(),
+ "({foo:hello})");
+
+obj = {};
+Object.defineProperty(obj, "foo", {value: async function bar() {}, enumerable: true});
+obj.foo.toSource = () => "hello";
+assertEq(obj.toSource(),
+ "({foo:hello})");
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/ecma_2017/Function/browser.js b/js/src/tests/ecma_2017/Function/browser.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/js/src/tests/ecma_2017/Function/browser.js
diff --git a/js/src/tests/ecma_2017/Function/shell.js b/js/src/tests/ecma_2017/Function/shell.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/js/src/tests/ecma_2017/Function/shell.js
diff --git a/js/src/tests/ecma_5/Array/frozen-dense-array.js b/js/src/tests/ecma_5/Array/frozen-dense-array.js
index 9db63036f..cdb86daa3 100644
--- a/js/src/tests/ecma_5/Array/frozen-dense-array.js
+++ b/js/src/tests/ecma_5/Array/frozen-dense-array.js
@@ -38,24 +38,5 @@ assertEq(delete a[0], false);
assertArrayIsExpected();
-var watchpointCalled = false;
-// NOTE: Be careful with the position of this test, since this sparsifies the
-// elements and you might not test what you think you're testing otherwise.
-a.watch(2, function(prop, oldValue, newValue) {
- watchpointCalled = true;
- assertEq(prop, 2);
- assertEq(oldValue, 1);
- assertEq(newValue, "foo");
-});
-
-assertArrayIsExpected();
-
-a.length = 5;
-a[2] = "foo";
-assertEq(watchpointCalled, true);
-assertEq(delete a[0], false);
-
-assertArrayIsExpected();
-
if (typeof reportCompare === "function")
reportCompare(true, true);
diff --git a/js/src/tests/ecma_5/extensions/watch-array-length.js b/js/src/tests/ecma_5/extensions/watch-array-length.js
deleted file mode 100644
index e9b356efa..000000000
--- a/js/src/tests/ecma_5/extensions/watch-array-length.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/licenses/publicdomain/
- */
-
-var hitCount;
-function watcher(p,o,n) { hitCount++; return n; }
-
-var a = [1];
-a.watch('length', watcher);
-hitCount = 0;
-a.length = 0;
-reportCompare(1, hitCount, "lenient; configurable: watchpoint hit");
-
-var b = Object.defineProperty([1],'0',{configurable:false});
-b.watch('length', watcher);
-hitCount = 0;
-var result;
-try {
- b.length = 0;
- result = "no error";
-} catch (x) {
- result = x.toString();
-}
-reportCompare(1, hitCount, "lenient; non-configurable: watchpoint hit");
-reportCompare(1, b.length, "lenient; non-configurable: length unchanged");
-reportCompare("no error", result, "lenient; non-configurable: no error thrown");
-
-var c = Object.defineProperty([1],'0',{configurable:false});
-c.watch('length', watcher);
-hitCount = 0;
-var threwTypeError;
-try {
- (function(){'use strict'; c.length = 0;})();
- threwTypeError = false;
-} catch (x) {
- threwTypeError = x instanceof TypeError;
-}
-reportCompare(1, hitCount, "strict; non-configurable: watchpoint hit");
-reportCompare(1, c.length, "strict; non-configurable: length unchanged");
-reportCompare(true, threwTypeError, "strict; non-configurable: TypeError thrown");
diff --git a/js/src/tests/ecma_5/extensions/watch-inherited-property.js b/js/src/tests/ecma_5/extensions/watch-inherited-property.js
deleted file mode 100644
index 1a0ad566b..000000000
--- a/js/src/tests/ecma_5/extensions/watch-inherited-property.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/licenses/publicdomain/
- */
-
-/* Create a prototype object with a setter property. */
-var protoSetterCount;
-var proto = ({ set x(v) { protoSetterCount++; } });
-
-/* Put a watchpoint on that setter. */
-var protoWatchCount;
-proto.watch('x', function() { protoWatchCount++; });
-
-/* Make an object with the above as its prototype. */
-function C() { }
-C.prototype = proto;
-var o = new C();
-
-/*
- * Set a watchpoint on the property in the inheriting object. We have
- * defined this to mean "duplicate the property, setter and all, in the
- * inheriting object." I don't think debugging observation mechanisms
- * should mutate the program being run, but that's what we've got.
- */
-var oWatchCount;
-o.watch('x', function() { oWatchCount++; });
-
-/*
- * Assign to the property. This should trip the watchpoint on the inheriting object and
- * the setter.
- */
-protoSetterCount = protoWatchCount = oWatchCount = 0;
-o.x = 1;
-assertEq(protoWatchCount, 0);
-assertEq(oWatchCount, 1);
-assertEq(protoSetterCount, 1);
-
-reportCompare(true, true);
diff --git a/js/src/tests/ecma_5/extensions/watch-replaced-setter.js b/js/src/tests/ecma_5/extensions/watch-replaced-setter.js
deleted file mode 100644
index 05cf60aff..000000000
--- a/js/src/tests/ecma_5/extensions/watch-replaced-setter.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/licenses/publicdomain/
- */
-
-/* A stock watcher function. */
-var watcherCount;
-function watcher(id, oldval, newval) { watcherCount++; return newval; }
-
-/* Create an object with a JavaScript setter. */
-var setterCount;
-var o = { w:2, set x(v) { setterCount++; } };
-
-/*
- * Put the object in dictionary mode, so that JSObject::putProperty will
- * mutate its shapes instead of creating new ones.
- */
-delete o.w;
-
-/*
- * Place a watchpoint on the property. The watchpoint structure holds the
- * original JavaScript setter, and a pointer to the shape.
- */
-o.watch('x', watcher);
-
-/*
- * Replace the accessor property with a value property. The shape's setter
- * should become a non-JS setter, js_watch_set, and the watchpoint
- * structure's saved setter should be updated (in this case, cleared).
- */
-Object.defineProperty(o, 'x', { value:3,
- writable:true,
- enumerable:true,
- configurable:true });
-
-/*
- * Assign to the property. This should trigger js_watch_set, which should
- * call the handler, and then see that there is no JS-level setter to pass
- * control on to, and return.
- */
-watcherCount = setterCount = 0;
-o.x = 3;
-assertEq(watcherCount, 1);
-assertEq(setterCount, 0);
-
-reportCompare(true, true);
diff --git a/js/src/tests/ecma_5/extensions/watch-setter-become-setter.js b/js/src/tests/ecma_5/extensions/watch-setter-become-setter.js
deleted file mode 100644
index f5eff98b8..000000000
--- a/js/src/tests/ecma_5/extensions/watch-setter-become-setter.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/licenses/publicdomain/
- */
-
-/* Create an object with a JavaScript setter. */
-var firstSetterCount;
-var o = { w:2, set x(v) { firstSetterCount++; } };
-
-/*
- * Put the object in dictionary mode, so that JSObject::putProperty will
- * mutate its shapes instead of creating new ones.
- */
-delete o.w;
-
-/* A stock watcher function. */
-var watcherCount;
-function watcher(id, oldval, newval) { watcherCount++; return newval; }
-
-/*
- * Place a watchpoint on the property. The property's shape now has the
- * watchpoint setter, with the original setter saved in the watchpoint
- * structure.
- */
-o.watch('x', watcher);
-
-/*
- * Replace the setter with a new setter. The shape should get updated to
- * refer to the new setter, and then the watchpoint setter should be
- * re-established.
- */
-var secondSetterCount;
-Object.defineProperty(o, 'x', { set: function () { secondSetterCount++ } });
-
-/*
- * Assign to the property. This should trigger the watchpoint and the new setter.
- */
-watcherCount = firstSetterCount = secondSetterCount = 0;
-o.x = 3;
-assertEq(watcherCount, 1);
-assertEq(firstSetterCount, 0);
-assertEq(secondSetterCount, 1);
-
-reportCompare(true, true);
diff --git a/js/src/tests/ecma_5/extensions/watch-value-prop-becoming-setter.js b/js/src/tests/ecma_5/extensions/watch-value-prop-becoming-setter.js
deleted file mode 100644
index 03cdd788e..000000000
--- a/js/src/tests/ecma_5/extensions/watch-value-prop-becoming-setter.js
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/licenses/publicdomain/
- */
-
-/* A stock watcher function. */
-var watcherCount;
-function watcher(id, old, newval) {
- watcherCount++;
- return newval;
-}
-
-/* Create an object with a value property. */
-var o = { w:2, x:3 };
-
-/*
- * Place a watchpoint on the value property. The watchpoint structure holds
- * the original JavaScript setter, and a pointer to the shape.
- */
-o.watch('x', watcher);
-
-/*
- * Put the object in dictionary mode, so that JSObject::putProperty will
- * mutate its shapes instead of creating new ones.
- */
-delete o.w;
-
-/*
- * Replace the value property with a setter.
- */
-var setterCount;
-o.__defineSetter__('x', function() { setterCount++; });
-
-/*
- * Trigger the watchpoint. The watchpoint handler should run, and then the
- * setter should run.
- */
-watcherCount = setterCount = 0;
-o.x = 4;
-assertEq(watcherCount, 1);
-assertEq(setterCount, 1);
-
-reportCompare(true, true);
diff --git a/js/src/tests/ecma_5/extensions/watchpoint-deletes-JSPropertyOp-setter.js b/js/src/tests/ecma_5/extensions/watchpoint-deletes-JSPropertyOp-setter.js
deleted file mode 100644
index 85410bbd4..000000000
--- a/js/src/tests/ecma_5/extensions/watchpoint-deletes-JSPropertyOp-setter.js
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/licenses/publicdomain/
- */
-
-function make_watcher(name) {
- return function (id, oldv, newv) {
- print("watched " + name + "[0]");
- };
-}
-
-var o, p;
-function f(flag) {
- if (flag) {
- o = arguments;
- } else {
- p = arguments;
- o.watch(0, make_watcher('o'));
- p.watch(0, make_watcher('p'));
-
- /*
- * Previously, the watchpoint implementation actually substituted its magic setter
- * functions for the setters of shared shapes, and then 1) carefully ignored calls
- * to its magic setter from unrelated objects, and 2) avoided restoring the
- * original setter until all watchpoints on that shape had been removed.
- *
- * However, when the watchpoint code began using JSObject::changeProperty and
- * js_ChangeNativePropertyAttrs to change shapes' setters, the shape tree code
- * became conscious of the presence of watchpoints, and shared shapes between
- * objects only when their watchpoint nature coincided. Clearing the magic setter
- * from one object's shape would not affect other objects, because the
- * watchpointed and non-watchpointed shapes were distinct if they were shared.
- *
- * Thus, the first unwatch call must go ahead and fix p's shape, even though a
- * watchpoint exists on the same shape in o. o's watchpoint's presence shouldn't
- * cause 'unwatch' to leave p's magic setter in place.
- */
-
- /* DropWatchPointAndUnlock would see o's watchpoint, and not change p's property. */
- p.unwatch(0);
-
- /* DropWatchPointAndUnlock would fix o's property, but not p's; p's setter would be gone. */
- o.unwatch(0);
-
- /* This would fail to invoke the arguments object's setter. */
- p[0] = 4;
-
- /* And the formal parameter would not get updated. */
- assertEq(flag, 4);
- }
-}
-
-f(true);
-f(false);
-
-reportCompare(true, true);
diff --git a/js/src/tests/ecma_6/Array/unscopables.js b/js/src/tests/ecma_6/Array/unscopables.js
index 6685309a0..56b3aa520 100644
--- a/js/src/tests/ecma_6/Array/unscopables.js
+++ b/js/src/tests/ecma_6/Array/unscopables.js
@@ -26,6 +26,8 @@ assertDeepEq(keys, [
"fill",
"find",
"findIndex",
+ "flat",
+ "flatMap",
"includes",
"keys",
"values"
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/Generators/runtime.js b/js/src/tests/ecma_6/Generators/runtime.js
index c4d3bb6a6..7146eef9f 100644
--- a/js/src/tests/ecma_6/Generators/runtime.js
+++ b/js/src/tests/ecma_6/Generators/runtime.js
@@ -109,7 +109,7 @@ function TestGeneratorFunction() {
// Doesn't matter particularly what string gets serialized, as long
// as it contains "function*" and "yield 10".
assertEq(GeneratorFunction('yield 10').toString(),
- "function* anonymous() {\nyield 10\n}");
+ "function* anonymous(\n) {\nyield 10\n}");
}
TestGeneratorFunction();
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_5/Object/regress-362872-01.js b/js/src/tests/js1_5/Object/regress-362872-01.js
deleted file mode 100644
index 0808ee82b..000000000
--- a/js/src/tests/js1_5/Object/regress-362872-01.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 362872;
-var summary = 'script should not drop watchpoint that is in use';
-var actual = 'No Crash';
-var expect = 'No Crash';
-
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
- function exploit() {
- var rooter = {}, object = {}, filler1 = "", filler2 = "\u5555";
- for(var i = 0; i < 32/2-2; i++) { filler1 += "\u5050"; }
- object.watch("foo", function(){
- object.unwatch("foo");
- object.unwatch("foo");
- for(var i = 0; i < 8 * 1024; i++) {
- rooter[i] = filler1 + filler2;
- }
- });
- object.foo = "bar";
- }
- exploit();
-
-
- reportCompare(expect, actual, summary);
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_5/Object/regress-362872-02.js b/js/src/tests/js1_5/Object/regress-362872-02.js
deleted file mode 100644
index edee43a4a..000000000
--- a/js/src/tests/js1_5/Object/regress-362872-02.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/*
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/licenses/publicdomain/
- * Contributor: Blake Kaplan
- */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 362872;
-var summary = 'script should not drop watchpoint that is in use';
-var actual = 'No Crash';
-var expect = 'No Crash';
-
-printBugNumber(BUGNUMBER);
-printStatus (summary);
-
-this.watch('x', function f() {
- print("before");
- x = 3;
- print("after");
- });
-x = 3;
-
-reportCompare(expect, actual, summary);
diff --git a/js/src/tests/js1_5/Regress/regress-127243.js b/js/src/tests/js1_5/Regress/regress-127243.js
deleted file mode 100644
index 11779f803..000000000
--- a/js/src/tests/js1_5/Regress/regress-127243.js
+++ /dev/null
@@ -1,75 +0,0 @@
-// |reftest| skip-if(xulRuntime.OS=="WINNT"&&isDebugBuild) slow
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 127243;
-var summary = 'Do not crash on watch';
-var actual = 'No Crash';
-var expect = 'No Crash';
-
-printBugNumber(BUGNUMBER);
-printStatus (summary);
-
-if (typeof window != 'undefined' && typeof document != 'undefined')
-{
- // delay test driver end
- gDelayTestDriverEnd = true;
- window.addEventListener('load', handleLoad, false);
-}
-else
-{
- printStatus('This test must be run in the browser');
- reportCompare(expect, actual, summary);
-
-}
-
-var div;
-
-function handleLoad()
-{
- div = document.createElement('div');
- document.body.appendChild(div);
- div.setAttribute('id', 'id1');
- div.style.width = '50px';
- div.style.height = '100px';
- div.style.overflow = 'auto';
-
- for (var i = 0; i < 5; i++)
- {
- var p = document.createElement('p');
- var t = document.createTextNode('blah');
- p.appendChild(t);
- div.appendChild(p);
- }
-
- div.watch('scrollTop', wee);
-
- setTimeout('setScrollTop()', 1000);
-}
-
-function wee(id, oldval, newval)
-{
- var t = document.createTextNode('setting ' + id +
- ' value ' + div.scrollTop +
- ' oldval ' + oldval +
- ' newval ' + newval);
- var p = document.createElement('p');
- p.appendChild(t);
- document.body.appendChild(p);
-
- return newval;
-}
-
-function setScrollTop()
-{
- div.scrollTop = 42;
-
- reportCompare(expect, actual, summary);
-
- gDelayTestDriverEnd = false;
- jsTestDriverEnd();
-
-}
diff --git a/js/src/tests/js1_5/Regress/regress-213482.js b/js/src/tests/js1_5/Regress/regress-213482.js
deleted file mode 100644
index 26ed7e894..000000000
--- a/js/src/tests/js1_5/Regress/regress-213482.js
+++ /dev/null
@@ -1,29 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 213482;
-var summary = 'Do not crash watching property when watcher sets property';
-var actual = 'No Crash';
-var expect = 'No Crash';
-
-printBugNumber(BUGNUMBER);
-printStatus (summary);
-
-var testobj = {value: 'foo'};
-
-function watched (a, b, c) {
- testobj.value = (new Date()).getTime();
-}
-
-function setTest() {
- testobj.value = 'b';
-}
-
-testobj.watch("value", watched);
-
-setTest();
-
-reportCompare(expect, actual, summary);
diff --git a/js/src/tests/js1_5/Regress/regress-240577.js b/js/src/tests/js1_5/Regress/regress-240577.js
deleted file mode 100644
index ba8330aa0..000000000
--- a/js/src/tests/js1_5/Regress/regress-240577.js
+++ /dev/null
@@ -1,37 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/*
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/licenses/publicdomain/
- * Contributor: Bob Clary
- */
-
-//-----------------------------------------------------------------------------
-// originally reported by Jens Thiele <karme@unforgettable.com> in
-var BUGNUMBER = 240577;
-var summary = 'object.watch execution context';
-var actual = '';
-var expect = '';
-
-printBugNumber(BUGNUMBER);
-printStatus (summary);
-
-var createWatcher = function ( watchlabel )
-{
- var watcher = function (property, oldvalue, newvalue)
- {
- actual += watchlabel; return newvalue;
- };
- return watcher;
-};
-
-var watcher1 = createWatcher('watcher1');
-
-var object = {property: 'value'};
-
-object.watch('property', watcher1);
-
-object.property = 'newvalue';
-
-expect = 'watcher1';
-
-reportCompare(expect, actual, summary);
diff --git a/js/src/tests/js1_5/Regress/regress-355341.js b/js/src/tests/js1_5/Regress/regress-355341.js
deleted file mode 100644
index ab2a4b884..000000000
--- a/js/src/tests/js1_5/Regress/regress-355341.js
+++ /dev/null
@@ -1,29 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 355341;
-var summary = 'Do not crash with watch and setter';
-var actual = '';
-var expect = '';
-
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
- Object.defineProperty(this, "x", { set: Function, enumerable: true, configurable: true });
- this.watch('x', function () { }); x = 3;
-
- reportCompare(expect, actual, summary);
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_5/Regress/regress-355344.js b/js/src/tests/js1_5/Regress/regress-355344.js
deleted file mode 100644
index 00bd39147..000000000
--- a/js/src/tests/js1_5/Regress/regress-355344.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 355344;
-var summary = 'Exceptions thrown by watch point';
-var actual = '';
-var expect = '';
-
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
- var o = {};
-
- expect = 'setter: yikes';
-
- o.watch('x', function(){throw 'yikes'});
- try
- {
- o.x = 3;
- }
- catch(ex)
- {
- actual = "setter: " + ex;
- }
-
- try
- {
- eval("") ;
- }
- catch(e)
- {
- actual = "eval: " + e;
- }
-
- reportCompare(expect, actual, summary);
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_5/Regress/regress-361467.js b/js/src/tests/js1_5/Regress/regress-361467.js
deleted file mode 100644
index 371c0a8b5..000000000
--- a/js/src/tests/js1_5/Regress/regress-361467.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 361467;
-var summary = 'Do not crash with certain watchers';
-var actual = '';
-var expect = '';
-
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
- expect = actual = 'No Crash';
-
- var x;
- this.watch('x', print);
- x = 5;
-
- reportCompare(expect, actual, summary);
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_5/Regress/regress-361617.js b/js/src/tests/js1_5/Regress/regress-361617.js
deleted file mode 100644
index 5d20fd78f..000000000
--- a/js/src/tests/js1_5/Regress/regress-361617.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 361617;
-var summary = 'Do not crash with getter, watch and gc';
-var actual = 'No Crash';
-var expect = 'No Crash';
-
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
- (function() {
- Object.defineProperty(this, "x", { get: function(){}, enumerable: true, configurable: true });
- })();
- this.watch('x', print);
- Object.defineProperty(this, "x", { get: function(){}, enumerable: true, configurable: true });
- gc();
- this.unwatch('x');
- x;
-
- reportCompare(expect, actual, summary);
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_5/Regress/regress-385393-06.js b/js/src/tests/js1_5/Regress/regress-385393-06.js
deleted file mode 100644
index 327103f39..000000000
--- a/js/src/tests/js1_5/Regress/regress-385393-06.js
+++ /dev/null
@@ -1,28 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 385393;
-var summary = 'Regression test for bug 385393';
-var actual = 'No Crash';
-var expect = 'No Crash';
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
- reportCompare(expect, actual, summary);
-
- true.watch("x", function(){});
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_5/Regress/regress-506567.js b/js/src/tests/js1_5/Regress/regress-506567.js
deleted file mode 100644
index e78dfe1db..000000000
--- a/js/src/tests/js1_5/Regress/regress-506567.js
+++ /dev/null
@@ -1,45 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 506567;
-var summary = 'Do not crash with watched variables';
-var actual = '';
-var expect = '';
-
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
- if (typeof clearInterval == 'undefined')
- {
- clearInterval = (function () {});
- }
-
- var obj = new Object();
- obj.test = null;
- obj.watch("test", (function(prop, oldval, newval)
- {
- if(false)
- {
- var test = newval % oldval;
- var func = (function(){clearInterval(myInterval);});
- }
- }));
-
- obj.test = 'null';
- print(obj.test);
-
- reportCompare(expect, actual, summary);
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_5/Scope/regress-185485.js b/js/src/tests/js1_5/Scope/regress-185485.js
index 19d190eea..a75bf885a 100644
--- a/js/src/tests/js1_5/Scope/regress-185485.js
+++ b/js/src/tests/js1_5/Scope/regress-185485.js
@@ -94,7 +94,7 @@ with (x)
}
status = inSection(5);
actual = x.g.toString();
-expect = (function () {}).toString();
+expect = (function() {}).toString();
addThis();
diff --git a/js/src/tests/js1_5/extensions/regress-303277.js b/js/src/tests/js1_5/extensions/regress-303277.js
deleted file mode 100644
index 319d9a2ed..000000000
--- a/js/src/tests/js1_5/extensions/regress-303277.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 303277;
-var summary = 'Do not crash with crash with a watchpoint for __proto__ property ';
-var actual = 'No Crash';
-var expect = 'No Crash';
-
-printBugNumber(BUGNUMBER);
-printStatus (summary);
-
-var o = {};
-o.watch("__proto__", function(){return null;});
-o.__proto__ = null;
-
-reportCompare(expect, actual, summary);
diff --git a/js/src/tests/js1_5/extensions/regress-355339.js b/js/src/tests/js1_5/extensions/regress-355339.js
deleted file mode 100644
index 9b15bd742..000000000
--- a/js/src/tests/js1_5/extensions/regress-355339.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 355339;
-var summary = 'Do not assert: sprop->setter != js_watch_set';
-var actual = '';
-var expect = '';
-
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
- expect = actual = 'No Crash';
- o = {};
- o.watch("j", function(a,b,c) { print("*",a,b,c) });
- o.unwatch("j");
- o.watch("j", function(a,b,c) { print("*",a,b,c) });
-
- reportCompare(expect, actual, summary);
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_5/extensions/regress-361346.js b/js/src/tests/js1_5/extensions/regress-361346.js
deleted file mode 100644
index 297c3b1f2..000000000
--- a/js/src/tests/js1_5/extensions/regress-361346.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 361346;
-var summary = 'Crash with setter, watch, GC';
-var actual = '';
-var expect = '';
-
-printBugNumber(BUGNUMBER);
-printStatus (summary);
-
-expect = actual = 'No Crash';
-
-Object.defineProperty(this, "x", { set: new Function, enumerable: true, configurable: true });
-this.watch('x', function(){});
-gc();
-x = {};
-
-reportCompare(expect, actual, summary);
diff --git a/js/src/tests/js1_5/extensions/regress-361360.js b/js/src/tests/js1_5/extensions/regress-361360.js
deleted file mode 100644
index 98e6575d9..000000000
--- a/js/src/tests/js1_5/extensions/regress-361360.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 361360;
-var summary = 'Do not assert: !caller || caller->pc involving setter and watch';
-var actual = '';
-var expect = '';
-
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
- expect = actual = 'No Crash';
-
- this.__defineSetter__('x', eval);
- this.watch('x', function(){});
- x = 3;
-
- reportCompare(expect, actual, summary);
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_5/extensions/regress-361552.js b/js/src/tests/js1_5/extensions/regress-361552.js
deleted file mode 100644
index eed54e6dd..000000000
--- a/js/src/tests/js1_5/extensions/regress-361552.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 361552;
-var summary = 'Crash with setter, watch, Script';
-var actual = '';
-var expect = '';
-
-printBugNumber(BUGNUMBER);
-printStatus (summary);
-
-expect = actual = 'No Crash';
-
-if (typeof Script == 'undefined')
-{
- print('Test skipped. Script not defined.');
-}
-else
-{
- this.__defineSetter__('x', gc);
- this.watch('x', new Script(''));
- x = 3;
-}
-reportCompare(expect, actual, summary);
diff --git a/js/src/tests/js1_5/extensions/regress-361558.js b/js/src/tests/js1_5/extensions/regress-361558.js
deleted file mode 100644
index a9a3ae725..000000000
--- a/js/src/tests/js1_5/extensions/regress-361558.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 361558;
-var summary = 'Do not assert: sprop->setter != js_watch_set';
-var actual = '';
-var expect = '';
-
-printBugNumber(BUGNUMBER);
-printStatus (summary);
-
-expect = actual = 'No Crash';
-
-({}.__proto__.watch('x', print)); ({}.watch('x', print));
-
-reportCompare(expect, actual, summary);
diff --git a/js/src/tests/js1_5/extensions/regress-361571.js b/js/src/tests/js1_5/extensions/regress-361571.js
deleted file mode 100644
index bf89d794b..000000000
--- a/js/src/tests/js1_5/extensions/regress-361571.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 361571;
-var summary = 'Do not assert: fp->scopeChain == parent';
-var actual = 'No Crash';
-var expect = 'No Crash';
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
- try
- {
- o = {};
- o.__defineSetter__('y', eval);
- o.watch('y', function () { return "";});
- o.y = 1;
- }
- catch(ex)
- {
- printStatus('Note eval can no longer be called directly');
- expect = 'EvalError: function eval must be called directly, and not by way of a function of another name';
- actual = ex + '';
- }
- reportCompare(expect, actual, summary);
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_5/extensions/regress-361856.js b/js/src/tests/js1_5/extensions/regress-361856.js
deleted file mode 100644
index e7e2f675b..000000000
--- a/js/src/tests/js1_5/extensions/regress-361856.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 361856;
-var summary = 'Do not assert: overwriting @ js_AddScopeProperty';
-var actual = '';
-var expect = '';
-
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
- function testit() {
- var obj = {};
- obj.watch("foo", function(){});
- delete obj.foo;
- obj = null;
- gc();
- }
- testit();
-
- reportCompare(expect, actual, summary);
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_5/extensions/regress-361964.js b/js/src/tests/js1_5/extensions/regress-361964.js
deleted file mode 100644
index fcb8bba01..000000000
--- a/js/src/tests/js1_5/extensions/regress-361964.js
+++ /dev/null
@@ -1,54 +0,0 @@
-// |reftest| skip -- slow, alert not dismissed, now busted by harness
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 361964;
-var summary = 'Crash [@ MarkGCThingChildren] involving watch and setter';
-var actual = 'No Crash';
-var expect = 'No Crash';
-
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
- var doc;
- if (typeof document == 'undefined')
- {
- doc = {};
- }
- else
- {
- doc = document;
- }
-
- if (typeof alert == 'undefined')
- {
- alert = print;
- }
-
-// Crash:
- doc.watch("title", function(a,b,c,d) {
- return { toString : function() { alert(1); } };
- });
- doc.title = "xxx";
-
-// No crash:
- doc.watch("title", function() {
- return { toString : function() { alert(1); } };
- });
- doc.title = "xxx";
-
- reportCompare(expect, actual, summary);
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_5/extensions/regress-385134.js b/js/src/tests/js1_5/extensions/regress-385134.js
deleted file mode 100644
index 041f4d6e7..000000000
--- a/js/src/tests/js1_5/extensions/regress-385134.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 385134;
-var summary = 'Do not crash with setter, watch, uneval';
-var actual = 'No Crash';
-var expect = 'No Crash';
-
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
- if (typeof this.__defineSetter__ != 'undefined' &&
- typeof this.watch != 'undefined' &&
- typeof uneval != 'undefined')
- {
- try {
- this.__defineSetter__(0, function(){});
- } catch (exc) {
- // In the browser, this fails. Ignore the error.
- }
- this.watch(0, function(){});
- uneval(this);
- }
- reportCompare(expect, actual, summary);
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_5/extensions/regress-385393-09.js b/js/src/tests/js1_5/extensions/regress-385393-09.js
deleted file mode 100644
index 42834824a..000000000
--- a/js/src/tests/js1_5/extensions/regress-385393-09.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 385393;
-var summary = 'Regression test for bug 385393';
-var actual = 'No Crash';
-var expect = 'No Crash';
-
-printBugNumber(BUGNUMBER);
-printStatus (summary);
-
-eval("this.__defineSetter__('x', gc); this.watch('x', [].slice); x = 1;");
-
-reportCompare(expect, actual, summary);
diff --git a/js/src/tests/js1_5/extensions/regress-390597.js b/js/src/tests/js1_5/extensions/regress-390597.js
deleted file mode 100644
index 9f8596adc..000000000
--- a/js/src/tests/js1_5/extensions/regress-390597.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 390597;
-var summary = 'watch point + eval-as-setter allows access to dead JSStackFrame';
-var actual = 'No Crash';
-var expect = 'No Crash';
-
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
- function exploit() {
- try
- {
- var obj = this, args = null;
- obj.__defineSetter__("evil", eval);
- obj.watch("evil", function() { return "args = arguments;"; });
- obj.evil = null;
- eval("print(args[0]);");
- }
- catch(ex)
- {
- print('Caught ' + ex);
- }
- }
- exploit();
-
- reportCompare(expect, actual, summary);
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_5/extensions/regress-420612.js b/js/src/tests/js1_5/extensions/regress-420612.js
deleted file mode 100644
index a4491095e..000000000
--- a/js/src/tests/js1_5/extensions/regress-420612.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 420612;
-var summary = 'Do not assert: obj == pobj';
-var actual = 'No Crash';
-var expect = 'No Crash';
-
-printBugNumber(BUGNUMBER);
-printStatus (summary);
-
-var obj = Object.create([]);
-obj.unwatch("x");
-
-if (typeof reportCompare === "function")
- reportCompare(true, true);
-
-print("Tests complete");
diff --git a/js/src/tests/js1_5/extensions/regress-435345-01.js b/js/src/tests/js1_5/extensions/regress-435345-01.js
deleted file mode 100644
index 28beab473..000000000
--- a/js/src/tests/js1_5/extensions/regress-435345-01.js
+++ /dev/null
@@ -1,100 +0,0 @@
-// |reftest| fails
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 435345;
-var summary = 'Watch the length property of arrays';
-var actual = '';
-var expect = '';
-
-// see http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Object:watch
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
- var arr;
-
- try
- {
- expect = 'watcher: propname=length, oldval=0, newval=1; ';
- actual = '';
- arr = [];
- arr.watch('length', watcher);
- arr[0] = '0';
- }
- catch(ex)
- {
- actual = ex + '';
- }
- reportCompare(expect, actual, summary + ': 1');
-
- try
- {
- expect = 'watcher: propname=length, oldval=1, newval=2; ' +
- 'watcher: propname=length, oldval=2, newval=2; ';
- actual = '';
- arr.push(5);
- }
- catch(ex)
- {
- actual = ex + '';
- }
- reportCompare(expect, actual, summary + ': 2');
-
- try
- {
- expect = 'watcher: propname=length, oldval=2, newval=1; ';
- actual = '';
- arr.pop();
- }
- catch(ex)
- {
- actual = ex + '';
- }
- reportCompare(expect, actual, summary + ': 3');
-
- try
- {
- expect = 'watcher: propname=length, oldval=1, newval=2; ';
- actual = '';
- arr.length++;
- }
- catch(ex)
- {
- actual = ex + '';
- }
- reportCompare(expect, actual, summary + ': 4');
-
- try
- {
- expect = 'watcher: propname=length, oldval=2, newval=5; ';
- actual = '';
- arr.length = 5;
- }
- catch(ex)
- {
- actual = ex + '';
- }
- reportCompare(expect, actual, summary + ': 5');
-
- exitFunc ('test');
-}
-
-function watcher(propname, oldval, newval)
-{
- actual += 'watcher: propname=' + propname + ', oldval=' + oldval +
- ', newval=' + newval + '; ';
-
- return newval;
-}
-
diff --git a/js/src/tests/js1_5/extensions/regress-454040.js b/js/src/tests/js1_5/extensions/regress-454040.js
deleted file mode 100644
index 63b5e1488..000000000
--- a/js/src/tests/js1_5/extensions/regress-454040.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 454040;
-var summary = 'Do not crash @ js_ComputeFilename';
-var actual = '';
-var expect = '';
-
-printBugNumber(BUGNUMBER);
-printStatus (summary);
-
-try
-{
- this.__defineGetter__("x", Function);
- this.__defineSetter__("x", Function);
- this.watch("x", x.__proto__);
- x = 1;
-}
-catch(ex)
-{
-}
-reportCompare(expect, actual, summary);
diff --git a/js/src/tests/js1_5/extensions/regress-454142.js b/js/src/tests/js1_5/extensions/regress-454142.js
deleted file mode 100644
index 05f331278..000000000
--- a/js/src/tests/js1_5/extensions/regress-454142.js
+++ /dev/null
@@ -1,30 +0,0 @@
-// |reftest| skip-if(Android)
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 454142;
-var summary = 'Do not crash with gczeal, setter, watch';
-var actual = '';
-var expect = '';
-
-printBugNumber(BUGNUMBER);
-printStatus (summary);
-
-this.watch("x", function(){});
-delete x;
-if (typeof gczeal == 'function')
-{
- gczeal(2);
-}
-
-this.__defineSetter__("x", function(){});
-
-if (typeof gczeal == 'function')
-{
- gczeal(0);
-}
-
-reportCompare(expect, actual, summary);
diff --git a/js/src/tests/js1_5/extensions/regress-455413.js b/js/src/tests/js1_5/extensions/regress-455413.js
deleted file mode 100644
index d00ab135b..000000000
--- a/js/src/tests/js1_5/extensions/regress-455413.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 455413;
-var summary = 'Do not assert with JIT: (m != JSVAL_INT) || isInt32(*vp)';
-var actual = 'No Crash';
-var expect = 'No Crash';
-
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-printBugNumber(BUGNUMBER);
-printStatus (summary);
-
-
-this.watch('x', Math.pow); (function() { for(var j=0;j<4;++j){x=1;} })();
-
-
-reportCompare(expect, actual, summary);
diff --git a/js/src/tests/js1_5/extensions/regress-465145.js b/js/src/tests/js1_5/extensions/regress-465145.js
deleted file mode 100644
index 84f81703b..000000000
--- a/js/src/tests/js1_5/extensions/regress-465145.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 465145;
-var summary = 'Do not crash with watched defined setter';
-var actual = '';
-var expect = '';
-
-printBugNumber(BUGNUMBER);
-printStatus (summary);
-
-
-this.__defineSetter__("x", function(){});
-this.watch("x", function(){});
-y = this;
-for (var z = 0; z < 2; ++z) { x = y };
-this.__defineSetter__("x", function(){});
-for (var z = 0; z < 2; ++z) { x = y };
-
-
-reportCompare(expect, actual, summary);
diff --git a/js/src/tests/js1_5/extensions/regress-472787.js b/js/src/tests/js1_5/extensions/regress-472787.js
deleted file mode 100755
index d3196f05f..000000000
--- a/js/src/tests/js1_5/extensions/regress-472787.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// |reftest| skip-if(Android)
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 472787;
-var summary = 'Do not hang with gczeal, watch and concat';
-var actual = '';
-var expect = '';
-
-printBugNumber(BUGNUMBER);
-printStatus (summary);
-
-this.__defineSetter__("x", Math.sin);
-this.watch("x", '' .concat);
-
-if (typeof gczeal == 'function')
-{
- gczeal(2);
-}
-
-x = 1;
-
-if (typeof gczeal == 'function')
-{
- gczeal(0);
-}
-
-reportCompare(expect, actual, summary);
diff --git a/js/src/tests/js1_5/extensions/regress-488995.js b/js/src/tests/js1_5/extensions/regress-488995.js
deleted file mode 100644
index f7abbe439..000000000
--- a/js/src/tests/js1_5/extensions/regress-488995.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 488995;
-var summary = 'Do not crash with watch, __defineSetter__ on svg';
-var actual = '';
-var expect = '';
-
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
- if (typeof document == 'undefined')
- {
- print('Test skipped: requires browser.');
- }
- else
- {
- try
- {
- var o=document.createElementNS("http://www.w3.org/2000/svg", "svg");
- var p=o.y;
- p.watch('animVal', function(id, oldvar, newval) {} );
- p.type='xxx';
- p.__defineSetter__('animVal', function() {});
- p.animVal=0;
- }
- catch(ex)
- {
- }
- }
-
- reportCompare(expect, actual, summary);
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_6/Regress/regress-476655.js b/js/src/tests/js1_6/Regress/regress-476655.js
deleted file mode 100644
index 9077ba3a0..000000000
--- a/js/src/tests/js1_6/Regress/regress-476655.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 476655;
-var summary = 'TM: Do not assert: count <= (size_t) (fp->regs->sp - StackBase(fp) - depth)';
-var actual = '';
-var expect = '';
-
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
-
- try
- {
- eval(
- "(function (){ for (var y in this) {} })();" +
- "[''.watch(\"\", function(){}) for each (x in ['', '', eval, '', '']) if " +
- "(x)].map(Function)"
- );
- }
- catch(ex)
- {
- }
-
-
- reportCompare(expect, actual, summary);
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_6/extensions/regress-457521.js b/js/src/tests/js1_6/extensions/regress-457521.js
deleted file mode 100644
index 6f4fbda50..000000000
--- a/js/src/tests/js1_6/extensions/regress-457521.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 457521;
-var summary = 'Do not crash @ js_DecompileValueGenerator';
-var actual = '';
-var expect = '';
-
-printBugNumber(BUGNUMBER);
-printStatus (summary);
-
-try
-{
- this.__defineSetter__("x", [,,,].map);
- this.watch("x", (new Function("var y, eval")));
- x = true;
-}
-catch(ex)
-{
-}
-reportCompare(expect, actual, summary);
diff --git a/js/src/tests/js1_6/extensions/regress-479567.js b/js/src/tests/js1_6/extensions/regress-479567.js
deleted file mode 100644
index 6f3525130..000000000
--- a/js/src/tests/js1_6/extensions/regress-479567.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 479567;
-var summary = 'Do not assert: thing';
-var actual = '';
-var expect = '';
-
-printBugNumber(BUGNUMBER);
-printStatus (summary);
-
-
-function f()
-{
- (eval("(function(){for each (y in [false, false, false]);});"))();
-}
-
-try
-{
- this.__defineGetter__("x", gc);
- uneval(this.watch("y", this.toSource))
- f();
- throw x;
-}
-catch(ex)
-{
-}
-
-
-reportCompare(expect, actual, summary);
diff --git a/js/src/tests/js1_7/extensions/regress-354945-01.js b/js/src/tests/js1_7/extensions/regress-354945-01.js
index 76f1a3c82..1c57db0f7 100644
--- a/js/src/tests/js1_7/extensions/regress-354945-01.js
+++ b/js/src/tests/js1_7/extensions/regress-354945-01.js
@@ -6,7 +6,7 @@
//-----------------------------------------------------------------------------
var BUGNUMBER = 354945;
var summary = 'Do not crash with new Iterator';
-var expect = 'TypeError: trap __iterator__ for ({__iterator__:(function (){ })}) returned a primitive value';
+var expect = 'TypeError: trap __iterator__ for ({__iterator__:(function(){ })}) returned a primitive value';
var actual;
diff --git a/js/src/tests/js1_7/extensions/regress-354945-02.js b/js/src/tests/js1_7/extensions/regress-354945-02.js
index 261bf7de1..abef90f77 100644
--- a/js/src/tests/js1_7/extensions/regress-354945-02.js
+++ b/js/src/tests/js1_7/extensions/regress-354945-02.js
@@ -20,7 +20,7 @@ function test()
printBugNumber(BUGNUMBER);
printStatus (summary);
- expect = 'TypeError: trap __iterator__ for ({__iterator__:(function (){ })}) returned a primitive value';
+ expect = 'TypeError: trap __iterator__ for ({__iterator__:(function(){ })}) returned a primitive value';
var obj = {};
obj.__iterator__ = function(){ };
try
diff --git a/js/src/tests/js1_7/extensions/regress-453955.js b/js/src/tests/js1_7/extensions/regress-453955.js
deleted file mode 100644
index 454f712f3..000000000
--- a/js/src/tests/js1_7/extensions/regress-453955.js
+++ /dev/null
@@ -1,31 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 453955;
-var summary = 'Do not assert: sprop->setter != js_watch_set || pobj != obj';
-var actual = '';
-var expect = '';
-
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
- for (var z = 0; z < 2; ++z)
- {
- [].filter.watch("9", function(y) { yield y; });
- }
-
- reportCompare(expect, actual, summary);
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_7/extensions/regress-473282.js b/js/src/tests/js1_7/extensions/regress-473282.js
deleted file mode 100644
index c50ac9234..000000000
--- a/js/src/tests/js1_7/extensions/regress-473282.js
+++ /dev/null
@@ -1,20 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 473282;
-var summary = 'Do not assert: thing';
-var actual = '';
-var expect = '';
-
-printBugNumber(BUGNUMBER);
-printStatus (summary);
-
-this.watch("b", "".substring);
-this.__defineGetter__("a", gc);
-for each (b in [this, null, null]);
-a;
-
-reportCompare(expect, actual, summary);
diff --git a/js/src/tests/js1_7/geniter/regress-352197.js b/js/src/tests/js1_7/geniter/regress-352197.js
index 7982e12ee..495717af7 100644
--- a/js/src/tests/js1_7/geniter/regress-352197.js
+++ b/js/src/tests/js1_7/geniter/regress-352197.js
@@ -20,7 +20,7 @@ function test()
printBugNumber(BUGNUMBER);
printStatus (summary);
- expect = /TypeError: anonymous generator function returns a value/;
+ expect = /TypeError: can't use 'yield' in a function that can return a value/;
try
{
var gen = eval('(function() { { return 5; } yield 3; })');
diff --git a/js/src/tests/js1_7/regress/regress-385133-01.js b/js/src/tests/js1_7/regress/regress-385133-01.js
deleted file mode 100644
index 03b09f535..000000000
--- a/js/src/tests/js1_7/regress/regress-385133-01.js
+++ /dev/null
@@ -1,37 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 385133;
-var summary = 'Do not crash due to recursion with watch, setter, delete, generator';
-var actual = 'No Crash';
-var expect = 'No Crash';
-
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
- try
- {
- Object.defineProperty(this, "x", { set: {}.watch, enumerable: true, configurable: true });
- this.watch('x', 'foo'.split);
- delete x;
- function g(){ x = 1; yield; }
- for (i in g()) { }
- }
- catch(ex)
- {
- }
- reportCompare(expect, actual, summary);
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_8/extensions/regress-394709.js b/js/src/tests/js1_8/extensions/regress-394709.js
deleted file mode 100644
index d04d8832f..000000000
--- a/js/src/tests/js1_8/extensions/regress-394709.js
+++ /dev/null
@@ -1,51 +0,0 @@
-// |reftest| skip-if(!xulRuntime.shell)
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 394709;
-var summary = 'Do not leak with object.watch and closure';
-var actual = 'No Leak';
-var expect = 'No Leak';
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
- assertEq(finalizeCount(), 0, "invalid initial state");
-
- runtest();
- gc();
- assertEq(finalizeCount(), 1, "leaked");
-
- runtest();
- gc();
- assertEq(finalizeCount(), 2, "leaked");
-
- runtest();
- gc();
- assertEq(finalizeCount(), 3, "leaked");
-
-
- function runtest () {
- var obj = { b: makeFinalizeObserver() };
- obj.watch('b', watcher);
-
- function watcher(id, old, value) {
- ++obj.n;
- return value;
- }
- }
-
- reportCompare(expect, actual, summary);
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_8/extensions/regress-481989.js b/js/src/tests/js1_8/extensions/regress-481989.js
deleted file mode 100644
index 36efb7567..000000000
--- a/js/src/tests/js1_8/extensions/regress-481989.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 481989;
-var summary = 'TM: Do not assert: SPROP_HAS_STUB_SETTER(sprop)';
-var actual = '';
-var expect = '';
-
-printBugNumber(BUGNUMBER);
-printStatus (summary);
-
-
-y = this.watch("x", function(){}); for each (let y in ['', '']) x = y;
-
-
-reportCompare(expect, actual, summary);
diff --git a/js/src/tests/js1_8/genexps/regress-683738.js b/js/src/tests/js1_8/genexps/regress-683738.js
index de563645d..b0309a90e 100644
--- a/js/src/tests/js1_8/genexps/regress-683738.js
+++ b/js/src/tests/js1_8/genexps/regress-683738.js
@@ -20,7 +20,7 @@ function test()
printBugNumber(BUGNUMBER);
printStatus (summary);
- expect = "generator function foo returns a value";
+ expect = "can't use 'yield' in a function that can return a value";
try
{
actual = 'No Error';
@@ -32,7 +32,7 @@ function test()
}
reportCompare(expect, actual, summary + ": 1");
- expect = "generator function foo returns a value";
+ expect = "generator function can't return a value";
try
{
actual = 'No Error';
@@ -44,7 +44,7 @@ function test()
}
reportCompare(expect, actual, summary + ": 2");
- expect = "generator function foo returns a value";
+ expect = "can't use 'yield' in a function that can return a value";
try
{
actual = 'No Error';
@@ -56,7 +56,7 @@ function test()
}
reportCompare(expect, actual, summary + ": 3");
- expect = "generator function foo returns a value";
+ expect = "generator function can't return a value";
try
{
actual = 'No Error';
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_1/extensions/regress-452498-193.js b/js/src/tests/js1_8_1/extensions/regress-452498-193.js
deleted file mode 100644
index 1397bf4bb..000000000
--- a/js/src/tests/js1_8_1/extensions/regress-452498-193.js
+++ /dev/null
@@ -1,34 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 452498;
-var summary = 'TM: upvar2 regression tests';
-var actual = '';
-var expect = '';
-
-//------- Comment #193 From Gary Kwong
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
-// Assertion failure: afunbox->parent, at ../jsparse.cpp:1912
-
- this.x = undefined;
- this.watch("x", Function);
- NaN = uneval({ get \u3056 (){ return undefined } });
- x+=NaN;
-
- reportCompare(expect, actual, summary);
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_8_1/extensions/regress-452498-196.js b/js/src/tests/js1_8_1/extensions/regress-452498-196.js
index 69d5a3586..5b9191199 100644
--- a/js/src/tests/js1_8_1/extensions/regress-452498-196.js
+++ b/js/src/tests/js1_8_1/extensions/regress-452498-196.js
@@ -21,15 +21,6 @@ function test()
printBugNumber(BUGNUMBER);
printStatus (summary);
-// Assertion failure: localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST, at ../jsfun.cpp:916
-
- this.x = undefined;
- this.watch("x", Function);
- NaN = uneval({ get \u3056 (){ return undefined } });
- x+=NaN;
-
- reportCompare(expect, actual, summary + ': 1');
-
// Assertion failure: lexdep->isLet(), at ../jsparse.cpp:1900
(function (){
diff --git a/js/src/tests/js1_8_1/extensions/regress-520572.js b/js/src/tests/js1_8_1/extensions/regress-520572.js
deleted file mode 100644
index 97f00029a..000000000
--- a/js/src/tests/js1_8_1/extensions/regress-520572.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
-/*
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/licenses/publicdomain/
- * Contributor: Blake Kaplan
- */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 520572;
-var summary = 'watch should innerize the object being watched';
-var actual = 0;
-var expect = 2;
-
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
- enterFunc ('test');
- printBugNumber(BUGNUMBER);
- printStatus (summary);
-
- if ("evalcx" in this) {
- // shell
- let s = evalcx("lazy");
- s.n = 0;
- evalcx('this.watch("x", function(){ n++; }); this.x = 4; x = 6', s);
- actual = s.n;
- reportCompare(expect, actual, summary);
- } else {
- // browser
- this.watch('x', function(){ actual++; });
- this.x = 4;
- x = 6;
- reportCompare(expect, actual, summary);
- }
-
- exitFunc ('test');
-}
diff --git a/js/src/tests/js1_8_1/regress/regress-452498-160.js b/js/src/tests/js1_8_1/regress/regress-452498-160.js
index 6498a0b5a..305b41795 100644
--- a/js/src/tests/js1_8_1/regress/regress-452498-160.js
+++ b/js/src/tests/js1_8_1/regress/regress-452498-160.js
@@ -21,11 +21,6 @@ function test()
printBugNumber(BUGNUMBER);
printStatus (summary);
-// crash [@ js_Interpret]
- (eval("(function(){ this.watch(\"x\", function () { new function ()y } ); const y = undefined });"))();
- x = NaN;
- reportCompare(expect, actual, summary + ': 2');
-
// Assertion failure: JOF_OPTYPE(op) == JOF_ATOM, at ../jsemit.cpp:5916
({ set z(v){}, set y(v)--x, set w(v)--w });
reportCompare(expect, actual, summary + ': 3');
diff --git a/js/src/tests/js1_8_5/extensions/regress-604781-1.js b/js/src/tests/js1_8_5/extensions/regress-604781-1.js
deleted file mode 100644
index a7c43f95d..000000000
--- a/js/src/tests/js1_8_5/extensions/regress-604781-1.js
+++ /dev/null
@@ -1,24 +0,0 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
-var watcherCount, setterCount;
-function watcher(id, oldval, newval) { watcherCount++; return newval; }
-function setter(newval) { setterCount++; }
-
-var p = { set x(v) { setter(v); } };
-p.watch('x', watcher);
-
-watcherCount = setterCount = 0;
-p.x = 2;
-assertEq(setterCount, 1);
-assertEq(watcherCount, 1);
-
-var o = Object.defineProperty({}, 'x', { set:setter, enumerable:true, configurable:true });
-o.watch('x', watcher);
-
-watcherCount = setterCount = 0;
-o.x = 2;
-assertEq(setterCount, 1);
-assertEq(watcherCount, 1);
-
-reportCompare(0, 0, 'ok');
diff --git a/js/src/tests/js1_8_5/extensions/regress-604781-2.js b/js/src/tests/js1_8_5/extensions/regress-604781-2.js
deleted file mode 100644
index 7aba4a274..000000000
--- a/js/src/tests/js1_8_5/extensions/regress-604781-2.js
+++ /dev/null
@@ -1,13 +0,0 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
-var log;
-function watcher(id, old, newval) { log += 'watcher'; return newval; }
-var o = { set x(v) { log += 'setter'; } };
-o.watch('x', watcher);
-Object.defineProperty(o, 'x', {value: 3, writable: true});
-log = '';
-o.x = 3;
-assertEq(log, 'watcher');
-
-reportCompare(0, 0, 'ok');
diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-1.js b/js/src/tests/js1_8_5/extensions/regress-627984-1.js
deleted file mode 100644
index a3726630a..000000000
--- a/js/src/tests/js1_8_5/extensions/regress-627984-1.js
+++ /dev/null
@@ -1,16 +0,0 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
-// See bug 627984, comment 17, item 1.
-var obj;
-var methods = [];
-for (var i = 0; i < 2; i++) {
- obj = {m: function () { return this.x; }};
- obj.watch("m", function (id, oldval, newval) { methods[i] = oldval; });
- obj.m = 0;
-}
-assertEq(typeof methods[0], "function");
-assertEq(typeof methods[1], "function");
-assertEq(methods[0] !== methods[1], true);
-
-reportCompare(0, 0, 'ok');
diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-2.js b/js/src/tests/js1_8_5/extensions/regress-627984-2.js
deleted file mode 100644
index c4f1b508c..000000000
--- a/js/src/tests/js1_8_5/extensions/regress-627984-2.js
+++ /dev/null
@@ -1,15 +0,0 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
-// See bug 627984, comment 17, item 2.
-var obj = {};
-var x;
-obj.watch("m", function (id, oldval, newval) {
- x = this.m;
- return newval;
- });
-delete obj.m;
-obj.m = function () { return this.method; };
-obj.m = 2;
-
-reportCompare(0, 0, 'ok');
diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-3.js b/js/src/tests/js1_8_5/extensions/regress-627984-3.js
deleted file mode 100644
index cbe4e10fa..000000000
--- a/js/src/tests/js1_8_5/extensions/regress-627984-3.js
+++ /dev/null
@@ -1,14 +0,0 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
-// Don't write string value to method slot.
-// See bug 627984, comment 17, item 2.
-var obj = {};
-obj.watch("m", function (id, oldval, newval) {
- return 'ok';
- });
-delete obj.m;
-obj.m = function () { return this.x; };
-assertEq(obj.m, 'ok');
-
-reportCompare(0, 0, 'ok');
diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-4.js b/js/src/tests/js1_8_5/extensions/regress-627984-4.js
deleted file mode 100644
index bbc017ffb..000000000
--- a/js/src/tests/js1_8_5/extensions/regress-627984-4.js
+++ /dev/null
@@ -1,15 +0,0 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
-// See bug 627984, comment 17, item 3.
-var obj = {};
-obj.watch("m", function (id, oldval, newval) {
- delete obj.m;
- obj.m = function () {};
- return newval;
- });
-delete obj.m;
-obj.m = 1;
-assertEq(obj.m, 1);
-
-reportCompare(0, 0, 'ok');
diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-5.js b/js/src/tests/js1_8_5/extensions/regress-627984-5.js
deleted file mode 100644
index 704d8421c..000000000
--- a/js/src/tests/js1_8_5/extensions/regress-627984-5.js
+++ /dev/null
@@ -1,13 +0,0 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
-// Bug 627984 comment 11.
-var o = ({});
-o.p = function() {};
-o.watch('p', function() { });
-o.q = function() {}
-delete o.p;
-o.p = function() {};
-assertEq(o.p, void 0);
-
-reportCompare(0, 0, 'ok');
diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-6.js b/js/src/tests/js1_8_5/extensions/regress-627984-6.js
deleted file mode 100644
index cb1a0fca9..000000000
--- a/js/src/tests/js1_8_5/extensions/regress-627984-6.js
+++ /dev/null
@@ -1,15 +0,0 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
-// Bug 627984 description.
-var o = Array;
-o.p = function() {};
-o.watch('p', function() { });
-for(var x in o) {
- o[x];
-}
-delete o.p;
-o.p = function() {};
-assertEq(o.p, void 0);
-
-reportCompare(0, 0, 'ok');
diff --git a/js/src/tests/js1_8_5/extensions/regress-627984-7.js b/js/src/tests/js1_8_5/extensions/regress-627984-7.js
deleted file mode 100644
index b13a0e912..000000000
--- a/js/src/tests/js1_8_5/extensions/regress-627984-7.js
+++ /dev/null
@@ -1,9 +0,0 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
-// See bug 627984 comment 20.
-var obj = {m: function () {}};
-obj.watch("m", function () { throw 'FAIL'; });
-var f = obj.m; // don't call the watchpoint
-
-reportCompare(0, 0, 'ok');
diff --git a/js/src/tests/js1_8_5/extensions/regress-631723.js b/js/src/tests/js1_8_5/extensions/regress-631723.js
deleted file mode 100644
index f7c755603..000000000
--- a/js/src/tests/js1_8_5/extensions/regress-631723.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
-var o = {a:1, b:2};
-o.watch("p", function() { return 13; });
-delete o.p;
-o.p = 0;
-assertEq(o.p, 13);
-
-reportCompare(0, 0, 'ok');
diff --git a/js/src/tests/js1_8_5/extensions/regress-636697.js b/js/src/tests/js1_8_5/extensions/regress-636697.js
deleted file mode 100644
index 6b3b1de37..000000000
--- a/js/src/tests/js1_8_5/extensions/regress-636697.js
+++ /dev/null
@@ -1,11 +0,0 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
-var a = {set p(x) {}};
-a.watch('p', function () {});
-var b = Object.create(a);
-b.watch('p', function () {});
-delete b.p;
-b.p = 0;
-
-reportCompare(0, 0, 'ok');
diff --git a/js/src/tests/js1_8_5/extensions/regress-637985.js b/js/src/tests/js1_8_5/extensions/regress-637985.js
deleted file mode 100644
index 305bfc820..000000000
--- a/js/src/tests/js1_8_5/extensions/regress-637985.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
-var obj = {};
-obj.watch(-1, function(){});
-obj.unwatch("-1"); // don't assert
-
-reportCompare(0, 0, 'ok'); \ No newline at end of file
diff --git a/js/src/tests/js1_8_5/extensions/regress-691746.js b/js/src/tests/js1_8_5/extensions/regress-691746.js
deleted file mode 100644
index 26f732d07..000000000
--- a/js/src/tests/js1_8_5/extensions/regress-691746.js
+++ /dev/null
@@ -1,11 +0,0 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
-var obj = {};
-try {
- obj.watch(QName(), function () {});
-} catch (exc) {
-}
-gc();
-
-reportCompare(0, 0, 'ok');
diff --git a/js/src/tests/js1_8_5/extensions/watch-undefined-setter.js b/js/src/tests/js1_8_5/extensions/watch-undefined-setter.js
deleted file mode 100644
index 92608de0e..000000000
--- a/js/src/tests/js1_8_5/extensions/watch-undefined-setter.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/*
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/licenses/publicdomain/
- * Contributor:
- * Gary Kwong
- */
-
-var gTestfile = 'watch-undefined-setter.js';
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 560277;
-var summary =
- 'Crash [@ JSObject::getParent] or [@ js_WrapWatchedSetter] or ' +
- '[@ js_GetClassPrototype]';
-
-this.watch("x", function() { });
-Object.defineProperty(this, "x", { set: undefined, configurable: true });
-
-reportCompare(true, true);
diff --git a/js/src/tests/js1_8_5/reflect-parse/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/js1_8_5/regress/regress-533876.js b/js/src/tests/js1_8_5/regress/regress-533876.js
deleted file mode 100644
index e44bc8a4f..000000000
--- a/js/src/tests/js1_8_5/regress/regress-533876.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/licenses/publicdomain/
- * Contributors: Gary Kwong and Jason Orendorff
- */
-
-var savedEval = eval;
-var x = [0];
-eval();
-
-x.__proto__ = this; // x has non-dictionary scope
-try {
- DIE;
-} catch(e) {
-}
-
-delete eval; // force dictionary scope for global
-gc();
-eval = savedEval;
-var f = eval("(function () { return /x/; })");
-x.watch('x', f); // clone property from global to x, including SPROP_IN_DICTIONARY flag
-
-reportCompare("ok", "ok", "bug 533876");
diff --git a/js/src/tests/js1_8_5/regress/regress-548276.js b/js/src/tests/js1_8_5/regress/regress-548276.js
deleted file mode 100644
index 5e306eba1..000000000
--- a/js/src/tests/js1_8_5/regress/regress-548276.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// |reftest| skip
-/*
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/licenses/publicdomain/
- * Contributors: Gary Kwong and Jason Orendorff
- */
-var obj = {};
-obj.__defineSetter__("x", function() {});
-obj.watch("x", function() {});
-obj.__defineSetter__("x", /a/);
diff --git a/js/src/tests/js1_8_5/regress/regress-584355.js b/js/src/tests/js1_8_5/regress/regress-584355.js
index 4ddfe65d3..7d1b81a2e 100644
--- a/js/src/tests/js1_8_5/regress/regress-584355.js
+++ b/js/src/tests/js1_8_5/regress/regress-584355.js
@@ -1,5 +1,5 @@
var actual;
-var expect = "function f() { ff (); }";
+var expect = "function f () { ff (); }";
function fun() {
(new Function ("function ff () { actual = '' + ff. caller; } function f () { ff (); } f ();")) ();
}
diff --git a/js/src/tests/js1_8_5/regress/regress-584648.js b/js/src/tests/js1_8_5/regress/regress-584648.js
deleted file mode 100644
index a1635ea51..000000000
--- a/js/src/tests/js1_8_5/regress/regress-584648.js
+++ /dev/null
@@ -1,16 +0,0 @@
-// |reftest| skip
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-// Contributors: Gary Kwong <gary@rumblingedge.com>
-// Jason Orendorff <jorendorff@mozilla.com>
-
-// on a non-global object
-var x = {};
-x.watch("p", function () { evalcx(''); });
-x.p = 0;
-
-// on the global
-watch("e", (function () { evalcx(''); }));
-e = function () {};
-
-reportCompare(0, 0, "ok");
diff --git a/js/src/tests/js1_8_5/regress/regress-635195.js b/js/src/tests/js1_8_5/regress/regress-635195.js
deleted file mode 100644
index 89980b05a..000000000
--- a/js/src/tests/js1_8_5/regress/regress-635195.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
-var obj = {set x(v) {}};
-obj.watch("x", function() { delete obj.x; });
-obj.x = "hi"; // don't assert
-
-reportCompare(0, 0, 'ok');
diff --git a/js/src/tests/js1_8_5/regress/regress-636394.js b/js/src/tests/js1_8_5/regress/regress-636394.js
deleted file mode 100644
index d1a249786..000000000
--- a/js/src/tests/js1_8_5/regress/regress-636394.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
-var a = {p0: function () {}};
-var b = /f/;
-b.__proto__ = a;
-b.watch("p0", function () {});
-b.p0;
-
-reportCompare(0, 0, "ok");
diff --git a/js/src/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/tests/user.js b/js/src/tests/user.js
index 732bbbd1a..e75593ab1 100755
--- a/js/src/tests/user.js
+++ b/js/src/tests/user.js
@@ -22,7 +22,6 @@ user_pref("javascript.options.strict", false);
user_pref("javascript.options.werror", false);
user_pref("toolkit.startup.max_resumed_crashes", -1);
user_pref("security.turn_off_all_security_so_that_viruses_can_take_over_this_computer", true);
-user_pref("toolkit.telemetry.enabled", false);
user_pref("browser.safebrowsing.phishing.enabled", false);
user_pref("browser.safebrowsing.malware.enabled", false);
user_pref("browser.safebrowsing.forbiddenURIs.enabled", false);
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/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h
index 6614f5220..e9c9bc0e0 100644
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -457,8 +457,8 @@ ClampDoubleToUint8(const double x);
struct uint8_clamped {
uint8_t val;
- uint8_clamped() { }
- uint8_clamped(const uint8_clamped& other) : val(other.val) { }
+ uint8_clamped() = default;
+ uint8_clamped(const uint8_clamped& other) = default;
// invoke our assignment helpers for constructor conversion
explicit uint8_clamped(uint8_t x) { *this = x; }
@@ -469,10 +469,7 @@ struct uint8_clamped {
explicit uint8_clamped(int32_t x) { *this = x; }
explicit uint8_clamped(double x) { *this = x; }
- uint8_clamped& operator=(const uint8_clamped& x) {
- val = x.val;
- return *this;
- }
+ uint8_clamped& operator=(const uint8_clamped& x) = default;
uint8_clamped& operator=(uint8_t x) {
val = x;
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/Caches.h b/js/src/vm/Caches.h
index 91a78bdc8..b11dd9dcb 100644
--- a/js/src/vm/Caches.h
+++ b/js/src/vm/Caches.h
@@ -7,6 +7,8 @@
#ifndef vm_Caches_h
#define vm_Caches_h
+#include <new>
+
#include "jsatom.h"
#include "jsbytecode.h"
#include "jsobj.h"
@@ -191,14 +193,20 @@ class NewObjectCache
char templateObject[MAX_OBJ_SIZE];
};
- Entry entries[41]; // TODO: reconsider size
+ using EntryArray = Entry[41]; // TODO: reconsider size;
+ EntryArray entries;
public:
- typedef int EntryIndex;
+ using EntryIndex = int;
+
+ NewObjectCache()
+ : entries{} // zeroes out the array
+ {}
- NewObjectCache() { mozilla::PodZero(this); }
- void purge() { mozilla::PodZero(this); }
+ void purge() {
+ new (&entries) EntryArray{}; // zeroes out the array
+ }
/* Remove any cached items keyed on moved objects. */
void clearNurseryObjects(JSRuntime* rt);
diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h
index e971dc844..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,20 +116,26 @@
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") \
macro(fix, fix, "fix") \
macro(flags, flags, "flags") \
+ macro(flat, flat, "flat") \
+ macro(flatMap, flatMap, "flatMap") \
macro(float32, float32, "float32") \
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") \
@@ -144,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") \
@@ -156,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") \
@@ -215,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") \
@@ -236,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") \
@@ -291,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") \
@@ -307,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") \
@@ -327,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") \
@@ -341,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 9b20c2b9c..c95bb0597 100644
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -408,7 +408,6 @@ const ObjectOps ModuleEnvironmentObject::objectOps_ = {
ModuleEnvironmentObject::setProperty,
ModuleEnvironmentObject::getOwnPropertyDescriptor,
ModuleEnvironmentObject::deleteProperty,
- nullptr, nullptr, /* watch/unwatch */
nullptr, /* getElements */
ModuleEnvironmentObject::enumerate,
nullptr
@@ -790,7 +789,6 @@ static const ObjectOps WithEnvironmentObjectOps = {
with_SetProperty,
with_GetOwnPropertyDescriptor,
with_DeleteProperty,
- nullptr, nullptr, /* watch/unwatch */
nullptr, /* getElements */
nullptr, /* enumerate (native enumeration of target doesn't work) */
nullptr,
@@ -816,7 +814,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 +955,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);
@@ -1159,7 +1157,6 @@ static const ObjectOps RuntimeLexicalErrorObjectObjectOps = {
lexicalError_SetProperty,
lexicalError_GetOwnPropertyDescriptor,
lexicalError_DeleteProperty,
- nullptr, nullptr, /* watch/unwatch */
nullptr, /* getElements */
nullptr, /* enumerate (native enumeration of target doesn't work) */
nullptr, /* this */
@@ -1425,7 +1422,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 +2231,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 +2958,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 +3466,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 +3535,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 +3572,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 5f476c4ff..acfa8f74b 100644
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -22,7 +22,6 @@
#include "vm/EnvironmentObject-inl.h"
#include "vm/Stack-inl.h"
#include "vm/String-inl.h"
-#include "vm/UnboxedObject-inl.h"
namespace js {
@@ -337,14 +336,10 @@ InitGlobalLexicalOperation(JSContext* cx, LexicalEnvironmentObject* lexicalEnvAr
inline bool
InitPropertyOperation(JSContext* cx, JSOp op, HandleObject obj, HandleId id, HandleValue rhs)
{
- if (obj->is<PlainObject>() || obj->is<JSFunction>()) {
- unsigned propAttrs = GetInitDataPropAttrs(op);
- return NativeDefineProperty(cx, obj.as<NativeObject>(), id, rhs, nullptr, nullptr,
- propAttrs);
- }
-
- MOZ_ASSERT(obj->as<UnboxedPlainObject>().layout().lookup(id));
- return PutProperty(cx, obj, id, rhs, false);
+ MOZ_ASSERT(obj->is<PlainObject>() || obj->is<JSFunction>());
+ unsigned propAttrs = GetInitDataPropAttrs(op);
+ return NativeDefineProperty(cx, obj.as<NativeObject>(), id, rhs,
+ nullptr, nullptr, propAttrs);
}
inline bool
@@ -598,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);
@@ -835,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 b747e4d7a..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. */
@@ -718,14 +740,14 @@ js::Execute(JSContext* cx, HandleScript script, JSObject& envChainArg, Value* rv
}
/*
- * ES6 (4-25-16) 12.10.4 InstanceofOperator
+ * ES6 12.9.4 InstanceofOperator
*/
extern bool
-js::InstanceOfOperator(JSContext* cx, HandleObject obj, HandleValue v, bool* bp)
+JS::InstanceofOperator(JSContext* cx, HandleObject obj, HandleValue v, bool* bp)
{
/* Step 1. is handled by caller. */
- /* Step 2. */
+ /* Step 2-3. */
RootedValue hasInstance(cx);
RootedId id(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().hasInstance));
if (!GetProperty(cx, obj, obj, id, &hasInstance))
@@ -735,7 +757,7 @@ js::InstanceOfOperator(JSContext* cx, HandleObject obj, HandleValue v, bool* bp)
if (!IsCallable(hasInstance))
return ReportIsNotFunction(cx, hasInstance);
- /* Step 3. */
+ /* Step 4. */
RootedValue rval(cx);
if (!Call(cx, hasInstance, obj, v, &rval))
return false;
@@ -743,13 +765,13 @@ js::InstanceOfOperator(JSContext* cx, HandleObject obj, HandleValue v, bool* bp)
return true;
}
- /* Step 4. */
+ /* Step 5. */
if (!obj->isCallable()) {
RootedValue val(cx, ObjectValue(*obj));
return ReportIsNotFunction(cx, val);
}
- /* Step 5. */
+ /* Step 6. */
return OrdinaryHasInstance(cx, obj, v, bp);
}
@@ -760,7 +782,7 @@ js::HasInstance(JSContext* cx, HandleObject obj, HandleValue v, bool* bp)
RootedValue local(cx, v);
if (JSHasInstanceOp hasInstance = clasp->getHasInstance())
return hasInstance(cx, obj, &local, bp);
- return js::InstanceOfOperator(cx, obj, local, bp);
+ return JS::InstanceofOperator(cx, obj, local, bp);
}
static inline bool
@@ -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);
@@ -4111,7 +4135,7 @@ CASE(JSOP_INITHOMEOBJECT)
/* Load the home object */
ReservedRooted<JSObject*> obj(&rootObject0);
obj = &REGS.sp[int(-2 - skipOver)].toObject();
- MOZ_ASSERT(obj->is<PlainObject>() || obj->is<UnboxedPlainObject>() || obj->is<JSFunction>());
+ MOZ_ASSERT(obj->is<PlainObject>() || obj->is<JSFunction>());
func->setExtendedSlot(FunctionExtended::METHOD_HOMEOBJECT_SLOT, ObjectValue(*obj));
}
@@ -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);
@@ -4927,18 +4951,13 @@ js::NewObjectOperation(JSContext* cx, HandleScript script, jsbytecode* pc,
return nullptr;
if (group->maybePreliminaryObjects()) {
group->maybePreliminaryObjects()->maybeAnalyze(cx, group);
- if (group->maybeUnboxedLayout())
- group->maybeUnboxedLayout()->setAllocationSite(script, pc);
}
if (group->shouldPreTenure() || group->maybePreliminaryObjects())
newKind = TenuredObject;
-
- if (group->maybeUnboxedLayout())
- return UnboxedPlainObject::create(cx, group, newKind);
}
- RootedObject obj(cx);
+ RootedPlainObject obj(cx);
if (*pc == JSOP_NEWOBJECT) {
RootedPlainObject baseObject(cx, &script->getObject(pc)->as<PlainObject>());
@@ -4975,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;
@@ -5006,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);
@@ -5019,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;
@@ -5034,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/Interpreter.h b/js/src/vm/Interpreter.h
index 330dbef5f..9fefd75cc 100644
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -323,9 +323,6 @@ extern JSType
TypeOfValue(const Value& v);
extern bool
-InstanceOfOperator(JSContext* cx, HandleObject obj, HandleValue v, bool* bp);
-
-extern bool
HasInstance(JSContext* cx, HandleObject obj, HandleValue v, bool* bp);
// Unwind environment chain and iterator to match the scope corresponding to
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..030d92c12 100644
--- a/js/src/vm/NativeObject-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -158,11 +158,11 @@ NativeObject::extendDenseElements(ExclusiveContext* cx,
MOZ_ASSERT(!denseElementsAreFrozen());
/*
- * Don't grow elements for non-extensible objects or watched objects. Dense
- * elements can be added/written with no extensible or watchpoint checks as
- * long as there is capacity for them.
+ * Don't grow elements for non-extensible objects. Dense elements can be
+ * added/written with no extensible checks as long as there is capacity
+ * for them.
*/
- if (!nonProxyIsExtensible() || watched()) {
+ if (!nonProxyIsExtensible()) {
MOZ_ASSERT(getDenseCapacity() == 0);
return DenseElementResult::Incomplete;
}
@@ -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..d801fad06 100644
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -9,8 +9,6 @@
#include "mozilla/ArrayUtils.h"
#include "mozilla/Casting.h"
-#include "jswatchpoint.h"
-
#include "gc/Marking.h"
#include "js/Value.h"
#include "vm/Debugger.h"
@@ -390,33 +388,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)
{
@@ -629,7 +600,7 @@ NativeObject::maybeDensifySparseElements(js::ExclusiveContext* cx, HandleNativeO
return DenseElementResult::Incomplete;
/* Watch for conditions under which an object's elements cannot be dense. */
- if (!obj->nonProxyIsExtensible() || obj->watched())
+ if (!obj->nonProxyIsExtensible())
return DenseElementResult::Incomplete;
/*
@@ -699,10 +670,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 +689,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 +994,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 +1043,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 +1115,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();
}
@@ -2438,17 +2408,9 @@ SetExistingProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleVa
}
bool
-js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue value,
+js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue v,
HandleValue receiver, QualifiedBool qualified, ObjectOpResult& result)
{
- // Fire watchpoints, if any.
- RootedValue v(cx, value);
- if (MOZ_UNLIKELY(obj->watched())) {
- WatchpointMap* wpmap = cx->compartment()->watchpointMap;
- if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, &v))
- return false;
- }
-
// Step numbers below reference ES6 rev 27 9.1.9, the [[Set]] internal
// method for ordinary objects. We substitute our own names for these names
// used in the spec: O -> pobj, P -> id, ownDesc -> shape.
@@ -2556,7 +2518,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 d2c06eabc..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
@@ -876,7 +878,7 @@ class NativeObject : public ShapedObject
MOZ_ASSERT(end <= getDenseInitializedLength());
MOZ_ASSERT(!denseElementsAreCopyOnWrite());
for (size_t i = start; i < end; i++)
- elements_[i].HeapSlot::~HeapSlot();
+ elements_[i].destroy();
}
/*
@@ -885,7 +887,7 @@ class NativeObject : public ShapedObject
*/
void prepareSlotRangeForOverwrite(size_t start, size_t end) {
for (size_t i = start; i < end; i++)
- getSlotAddressUnchecked(i)->HeapSlot::~HeapSlot();
+ getSlotAddressUnchecked(i)->destroy();
}
public:
@@ -1085,7 +1087,8 @@ class NativeObject : public ShapedObject
for (uint32_t i = 0; i < count; ++i)
elements_[dstStart + i].set(this, HeapSlot::Element, dstStart + i, src[i]);
} else {
- memcpy(&elements_[dstStart], src, count * sizeof(HeapSlot));
+ memcpy(reinterpret_cast<Value*>(&elements_[dstStart]), src,
+ count * sizeof(Value));
elementsRangeWriteBarrierPost(dstStart, count);
}
}
@@ -1094,7 +1097,7 @@ class NativeObject : public ShapedObject
MOZ_ASSERT(dstStart + count <= getDenseCapacity());
MOZ_ASSERT(!denseElementsAreCopyOnWrite());
MOZ_ASSERT(!denseElementsAreFrozen());
- memcpy(&elements_[dstStart], src, count * sizeof(HeapSlot));
+ memcpy(reinterpret_cast<Value*>(&elements_[dstStart]), src, count * sizeof(Value));
elementsRangeWriteBarrierPost(dstStart, count);
}
@@ -1142,10 +1145,14 @@ class NativeObject : public ShapedObject
MOZ_ASSERT(!denseElementsAreCopyOnWrite());
MOZ_ASSERT(!denseElementsAreFrozen());
- memmove(elements_ + dstStart, elements_ + srcStart, count * sizeof(Value));
+ memmove(elements_ + dstStart, elements_ + srcStart, count * sizeof(HeapSlot));
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();
}
@@ -1467,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 d6a8fcaa4..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);
}
/////////////////////////////////////////////////////////////////////
@@ -495,13 +494,7 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp,
if (associated->is<JSFunction>()) {
// Canonicalize new functions to use the original one associated with its script.
- JSFunction* fun = &associated->as<JSFunction>();
- if (fun->hasScript())
- associated = fun->nonLazyScript()->functionNonDelazifying();
- else if (fun->isInterpretedLazy() && !fun->isSelfHostedBuiltin())
- associated = fun->lazyScript()->functionNonDelazifying();
- else
- associated = nullptr;
+ associated = associated->as<JSFunction>().maybeCanonicalFunction();
// If we have previously cleared the 'new' script information for this
// function, don't try to construct another one.
@@ -518,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
@@ -536,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;
}
@@ -780,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)
@@ -844,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);
}
@@ -903,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;
@@ -1054,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;
@@ -1317,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.
@@ -1509,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 97df908c3..e95e8a208 100644
--- a/js/src/vm/ReceiverGuard.cpp
+++ b/js/src/vm/ReceiverGuard.cpp
@@ -7,7 +7,6 @@
#include "vm/ReceiverGuard.h"
#include "builtin/TypedObject.h"
-#include "vm/UnboxedObject.h"
#include "jsobjinlines.h"
using namespace js;
@@ -16,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();
@@ -33,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;
@@ -46,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/ReceiverGuard.h b/js/src/vm/ReceiverGuard.h
index 459cc0012..c14f0d83b 100644
--- a/js/src/vm/ReceiverGuard.h
+++ b/js/src/vm/ReceiverGuard.h
@@ -28,11 +28,6 @@ namespace js {
// TypedObject: The structure of a typed object is determined by its group.
// All typed objects with the same group have the same class, prototype, and
// own properties.
-//
-// UnboxedPlainObject: The structure of an unboxed plain object is determined
-// by its group and its expando object's shape, if there is one. All unboxed
-// plain objects with the same group and expando shape have the same
-// properties except those stored in the expando's dense elements.
class HeapReceiverGuard;
class RootedReceiverGuard;
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 174e23594..284a4f3d7 100644
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -34,7 +34,6 @@
#include "jsnativestack.h"
#include "jsobj.h"
#include "jsscript.h"
-#include "jswatchpoint.h"
#include "jswin.h"
#include "jswrapper.h"
@@ -147,7 +146,6 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
updateChildRuntimeCount(parentRuntime),
#endif
interrupt_(false),
- telemetryCallback(nullptr),
handlingSegFault(false),
handlingJitInterrupt_(false),
interruptCallbackDisabled(false),
@@ -452,19 +450,6 @@ JSRuntime::destroyRuntime()
}
void
-JSRuntime::addTelemetry(int id, uint32_t sample, const char* key)
-{
- if (telemetryCallback)
- (*telemetryCallback)(id, sample, key);
-}
-
-void
-JSRuntime::setTelemetryCallback(JSRuntime* rt, JSAccumulateTelemetryDataCallback callback)
-{
- rt->telemetryCallback = callback;
-}
-
-void
JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes* rtSizes)
{
// Several tables in the runtime enumerated below can be used off thread.
@@ -603,7 +588,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/Runtime.h b/js/src/vm/Runtime.h
index 735adadf2..e60371e38 100644
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -11,11 +11,11 @@
#include "mozilla/Attributes.h"
#include "mozilla/LinkedList.h"
#include "mozilla/MemoryReporting.h"
-#include "mozilla/PodOperations.h"
#include "mozilla/Scoped.h"
#include "mozilla/ThreadLocal.h"
#include "mozilla/Vector.h"
+#include <algorithm>
#include <setjmp.h>
#include "jsatom.h"
@@ -577,17 +577,7 @@ struct JSRuntime : public JS::shadow::Runtime,
#endif
mozilla::Atomic<uint32_t, mozilla::Relaxed> interrupt_;
-
- /* Call this to accumulate telemetry data. */
- JSAccumulateTelemetryDataCallback telemetryCallback;
public:
- // Accumulates data for Firefox telemetry. |id| is the ID of a JS_TELEMETRY_*
- // histogram. |key| provides an additional key to identify the histogram.
- // |sample| is the data to add to the histogram.
- void addTelemetry(int id, uint32_t sample, const char* key = nullptr);
-
- void setTelemetryCallback(JSRuntime* rt, JSAccumulateTelemetryDataCallback callback);
-
enum InterruptMode {
RequestInterruptUrgent,
RequestInterruptCanWait
@@ -1504,20 +1494,21 @@ PerThreadData::exclusiveThreadsPresent()
static MOZ_ALWAYS_INLINE void
MakeRangeGCSafe(Value* vec, size_t len)
{
- mozilla::PodZero(vec, len);
+ // Don't PodZero here because JS::Value is non-trivial.
+ for (size_t i = 0; i < len; i++)
+ vec[i].setDouble(+0.0);
}
static MOZ_ALWAYS_INLINE void
MakeRangeGCSafe(Value* beg, Value* end)
{
- mozilla::PodZero(beg, end - beg);
+ MakeRangeGCSafe(beg, end - beg);
}
static MOZ_ALWAYS_INLINE void
MakeRangeGCSafe(jsid* beg, jsid* end)
{
- for (jsid* id = beg; id != end; ++id)
- *id = INT_TO_JSID(0);
+ std::fill(beg, end, INT_TO_JSID(0));
}
static MOZ_ALWAYS_INLINE void
@@ -1529,13 +1520,13 @@ MakeRangeGCSafe(jsid* vec, size_t len)
static MOZ_ALWAYS_INLINE void
MakeRangeGCSafe(Shape** beg, Shape** end)
{
- mozilla::PodZero(beg, end - beg);
+ std::fill(beg, end, nullptr);
}
static MOZ_ALWAYS_INLINE void
MakeRangeGCSafe(Shape** vec, size_t len)
{
- mozilla::PodZero(vec, len);
+ MakeRangeGCSafe(vec, vec + len);
}
static MOZ_ALWAYS_INLINE void
diff --git a/js/src/vm/Scope.cpp b/js/src/vm/Scope.cpp
index 112b34586..0f80d7b69 100644
--- a/js/src/vm/Scope.cpp
+++ b/js/src/vm/Scope.cpp
@@ -191,12 +191,12 @@ template <typename ConcreteScope>
static UniquePtr<typename ConcreteScope::Data>
NewEmptyScopeData(ExclusiveContext* cx, uint32_t length = 0)
{
- uint8_t* bytes = cx->zone()->pod_calloc<uint8_t>(ConcreteScope::sizeOfData(length));
+ uint8_t* bytes = cx->zone()->pod_malloc<uint8_t>(ConcreteScope::sizeOfData(length));
if (!bytes)
ReportOutOfMemory(cx);
auto data = reinterpret_cast<typename ConcreteScope::Data*>(bytes);
if (data)
- new (data) typename ConcreteScope::Data();
+ new (data) typename ConcreteScope::Data(length);
return UniquePtr<typename ConcreteScope::Data>(data);
}
@@ -273,7 +273,7 @@ Scope::XDRSizedBindingNames(XDRState<mode>* xdr, Handle<ConcreteScope*> scope,
}
for (uint32_t i = 0; i < length; i++) {
- if (!XDRBindingName(xdr, &data->names[i])) {
+ if (!XDRBindingName(xdr, &data->trailingNames[i])) {
if (mode == XDR_DECODE) {
DeleteScopeData(data.get());
data.set(nullptr);
@@ -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)
{
@@ -1250,7 +1258,7 @@ BindingIter::init(LexicalScope::Data& data, uint32_t firstFrameSlot, uint8_t fla
init(0, 0, 0, 0, 0, 0,
CanHaveEnvironmentSlots | flags,
firstFrameSlot, JSSLOT_FREE(&LexicalEnvironmentObject::class_),
- data.names, data.length);
+ data.trailingNames.start(), data.length);
} else {
// imports - [0, 0)
// positional formals - [0, 0)
@@ -1262,7 +1270,7 @@ BindingIter::init(LexicalScope::Data& data, uint32_t firstFrameSlot, uint8_t fla
init(0, 0, 0, 0, 0, data.constStart,
CanHaveFrameSlots | CanHaveEnvironmentSlots | flags,
firstFrameSlot, JSSLOT_FREE(&LexicalEnvironmentObject::class_),
- data.names, data.length);
+ data.trailingNames.start(), data.length);
}
}
@@ -1283,7 +1291,7 @@ BindingIter::init(FunctionScope::Data& data, uint8_t flags)
init(0, data.nonPositionalFormalStart, data.varStart, data.varStart, data.length, data.length,
flags,
0, JSSLOT_FREE(&CallObject::class_),
- data.names, data.length);
+ data.trailingNames.start(), data.length);
}
void
@@ -1299,7 +1307,7 @@ BindingIter::init(VarScope::Data& data, uint32_t firstFrameSlot)
init(0, 0, 0, 0, data.length, data.length,
CanHaveFrameSlots | CanHaveEnvironmentSlots,
firstFrameSlot, JSSLOT_FREE(&VarEnvironmentObject::class_),
- data.names, data.length);
+ data.trailingNames.start(), data.length);
}
void
@@ -1315,7 +1323,7 @@ BindingIter::init(GlobalScope::Data& data)
init(0, 0, 0, data.varStart, data.letStart, data.constStart,
CannotHaveSlots,
UINT32_MAX, UINT32_MAX,
- data.names, data.length);
+ data.trailingNames.start(), data.length);
}
void
@@ -1343,7 +1351,7 @@ BindingIter::init(EvalScope::Data& data, bool strict)
// consts - [data.length, data.length)
init(0, 0, 0, data.varStart, data.length, data.length,
flags, firstFrameSlot, firstEnvironmentSlot,
- data.names, data.length);
+ data.trailingNames.start(), data.length);
}
void
@@ -1359,7 +1367,7 @@ BindingIter::init(ModuleScope::Data& data)
init(data.varStart, data.varStart, data.varStart, data.varStart, data.letStart, data.constStart,
CanHaveFrameSlots | CanHaveEnvironmentSlots,
0, JSSLOT_FREE(&ModuleEnvironmentObject::class_),
- data.names, data.length);
+ data.trailingNames.start(), data.length);
}
PositionalFormalParameterIter::PositionalFormalParameterIter(JSScript* script)
diff --git a/js/src/vm/Scope.h b/js/src/vm/Scope.h
index 5304d6713..4a4ae8090 100644
--- a/js/src/vm/Scope.h
+++ b/js/src/vm/Scope.h
@@ -12,6 +12,7 @@
#include "jsobj.h"
#include "jsopcode.h"
+#include "jsutil.h"
#include "gc/Heap.h"
#include "gc/Policy.h"
@@ -111,6 +112,47 @@ class BindingName
void trace(JSTracer* trc);
};
+/**
+ * The various {Global,Module,...}Scope::Data classes consist of always-present
+ * bits, then a trailing array of BindingNames. The various Data classes all
+ * end in a TrailingNamesArray that contains sized/aligned space for *one*
+ * BindingName. Data instances that contain N BindingNames, are then allocated
+ * in sizeof(Data) + (space for (N - 1) BindingNames). Because this class's
+ * |data_| field is properly sized/aligned, the N-BindingName array can start
+ * at |data_|.
+ *
+ * This is concededly a very low-level representation, but we want to only
+ * allocate once for data+bindings both, and this does so approximately as
+ * elegantly as C++ allows.
+ */
+class TrailingNamesArray
+{
+ private:
+ alignas(BindingName) unsigned char data_[sizeof(BindingName)];
+
+ private:
+ // Some versions of GCC treat it as a -Wstrict-aliasing violation (ergo a
+ // -Werror compile error) to reinterpret_cast<> |data_| to |T*|, even
+ // through |void*|. Placing the latter cast in these separate functions
+ // breaks the chain such that affected GCC versions no longer warn/error.
+ void* ptr() {
+ return data_;
+ }
+
+ public:
+ // Explicitly ensure no one accidentally allocates scope data without
+ // poisoning its trailing names.
+ TrailingNamesArray() = delete;
+
+ explicit TrailingNamesArray(size_t nameCount) {
+ if (nameCount)
+ JS_POISON(&data_, 0xCC, sizeof(BindingName) * nameCount);
+ }
+ BindingName* start() { return reinterpret_cast<BindingName*>(ptr()); }
+
+ BindingName& operator[](size_t i) { return start()[i]; }
+};
+
class BindingLocation
{
public:
@@ -337,16 +379,19 @@ class LexicalScope : public Scope
//
// lets - [0, constStart)
// consts - [constStart, length)
- uint32_t constStart;
- uint32_t length;
+ uint32_t constStart = 0;
+ uint32_t length = 0;
// Frame slots [0, nextFrameSlot) are live when this is the innermost
// scope.
- uint32_t nextFrameSlot;
+ uint32_t nextFrameSlot = 0;
// The array of tagged JSAtom* names, allocated beyond the end of the
// struct.
- BindingName names[1];
+ TrailingNamesArray trailingNames;
+
+ explicit Data(size_t nameCount) : trailingNames(nameCount) {}
+ Data() = delete;
void trace(JSTracer* trc);
};
@@ -401,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;
@@ -433,11 +479,11 @@ class FunctionScope : public Scope
// The canonical function of the scope, as during a scope walk we
// often query properties of the JSFunction (e.g., is the function an
// arrow).
- GCPtrFunction canonicalFunction;
+ GCPtrFunction canonicalFunction = {};
// If parameter expressions are present, parameters act like lexical
// bindings.
- bool hasParameterExprs;
+ bool hasParameterExprs = false;
// Bindings are sorted by kind in both frames and environments.
//
@@ -452,17 +498,20 @@ class FunctionScope : public Scope
// positional formals - [0, nonPositionalFormalStart)
// other formals - [nonPositionalParamStart, varStart)
// vars - [varStart, length)
- uint16_t nonPositionalFormalStart;
- uint16_t varStart;
- uint32_t length;
+ uint16_t nonPositionalFormalStart = 0;
+ uint16_t varStart = 0;
+ uint32_t length = 0;
// Frame slots [0, nextFrameSlot) are live when this is the innermost
// scope.
- uint32_t nextFrameSlot;
+ uint32_t nextFrameSlot = 0;
// The array of tagged JSAtom* names, allocated beyond the end of the
// struct.
- BindingName names[1];
+ TrailingNamesArray trailingNames;
+
+ explicit Data(size_t nameCount) : trailingNames(nameCount) {}
+ Data() = delete;
void trace(JSTracer* trc);
};
@@ -514,6 +563,8 @@ class FunctionScope : public Scope
return data().nonPositionalFormalStart;
}
+ static bool isSpecialName(ExclusiveContext* cx, JSAtom* name);
+
static Shape* getEmptyEnvironmentShape(ExclusiveContext* cx, bool hasParameterExprs);
};
@@ -548,15 +599,18 @@ class VarScope : public Scope
struct Data
{
// All bindings are vars.
- uint32_t length;
+ uint32_t length = 0;
// Frame slots [firstFrameSlot(), nextFrameSlot) are live when this is
// the innermost scope.
- uint32_t nextFrameSlot;
+ uint32_t nextFrameSlot = 0;
// The array of tagged JSAtom* names, allocated beyond the end of the
// struct.
- BindingName names[1];
+ TrailingNamesArray trailingNames;
+
+ explicit Data(size_t nameCount) : trailingNames(nameCount) {}
+ Data() = delete;
void trace(JSTracer* trc);
};
@@ -638,14 +692,17 @@ class GlobalScope : public Scope
// vars - [varStart, letStart)
// lets - [letStart, constStart)
// consts - [constStart, length)
- uint32_t varStart;
- uint32_t letStart;
- uint32_t constStart;
- uint32_t length;
+ uint32_t varStart = 0;
+ uint32_t letStart = 0;
+ uint32_t constStart = 0;
+ uint32_t length = 0;
// The array of tagged JSAtom* names, allocated beyond the end of the
// struct.
- BindingName names[1];
+ TrailingNamesArray trailingNames;
+
+ explicit Data(size_t nameCount) : trailingNames(nameCount) {}
+ Data() = delete;
void trace(JSTracer* trc);
};
@@ -736,16 +793,19 @@ class EvalScope : public Scope
//
// top-level funcs - [0, varStart)
// vars - [varStart, length)
- uint32_t varStart;
- uint32_t length;
+ uint32_t varStart = 0;
+ uint32_t length = 0;
// Frame slots [0, nextFrameSlot) are live when this is the innermost
// scope.
- uint32_t nextFrameSlot;
+ uint32_t nextFrameSlot = 0;
// The array of tagged JSAtom* names, allocated beyond the end of the
// struct.
- BindingName names[1];
+ TrailingNamesArray trailingNames;
+
+ explicit Data(size_t nameCount) : trailingNames(nameCount) {}
+ Data() = delete;
void trace(JSTracer* trc);
};
@@ -827,7 +887,7 @@ class ModuleScope : public Scope
struct Data
{
// The module of the scope.
- GCPtr<ModuleObject*> module;
+ GCPtr<ModuleObject*> module = {};
// Bindings are sorted by kind.
//
@@ -835,18 +895,21 @@ class ModuleScope : public Scope
// vars - [varStart, letStart)
// lets - [letStart, constStart)
// consts - [constStart, length)
- uint32_t varStart;
- uint32_t letStart;
- uint32_t constStart;
- uint32_t length;
+ uint32_t varStart = 0;
+ uint32_t letStart = 0;
+ uint32_t constStart = 0;
+ uint32_t length = 0;
// Frame slots [0, nextFrameSlot) are live when this is the innermost
// scope.
- uint32_t nextFrameSlot;
+ uint32_t nextFrameSlot = 0;
// The array of tagged JSAtom* names, allocated beyond the end of the
// struct.
- BindingName names[1];
+ TrailingNamesArray trailingNames;
+
+ explicit Data(size_t nameCount) : trailingNames(nameCount) {}
+ Data() = delete;
void trace(JSTracer* trc);
};
diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
index 3e7baccad..82d2cde64 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);
@@ -1873,23 +1873,6 @@ intrinsic_RuntimeDefaultLocale(JSContext* cx, unsigned argc, Value* vp)
}
static bool
-intrinsic_AddContentTelemetry(JSContext* cx, unsigned argc, Value* vp)
-{
- CallArgs args = CallArgsFromVp(argc, vp);
- MOZ_ASSERT(args.length() == 2);
-
- int id = args[0].toInt32();
- MOZ_ASSERT(id < JS_TELEMETRY_END);
- MOZ_ASSERT(id >= 0);
-
- if (!cx->compartment()->isProbablySystemOrAddonCode())
- cx->runtime()->addTelemetry(id, args[1].toInt32());
-
- args.rval().setUndefined();
- return true;
-}
-
-static bool
intrinsic_ConstructFunction(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
@@ -2140,7 +2123,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),
@@ -2186,8 +2169,10 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_INLINABLE_FN("std_String_charAt", str_charAt, 1,0, StringCharAt),
JS_FN("std_String_endsWith", str_endsWith, 1,0),
JS_FN("std_String_trim", str_trim, 0,0),
- JS_FN("std_String_trimLeft", str_trimLeft, 0,0),
- JS_FN("std_String_trimRight", str_trimRight, 0,0),
+ JS_FN("std_String_trimLeft", str_trimStart, 0,0),
+ JS_FN("std_String_trimStart", str_trimStart, 0,0),
+ JS_FN("std_String_trimRight", str_trimEnd, 0,0),
+ JS_FN("std_String_trimEnd", str_trimEnd, 0,0),
JS_FN("std_String_toLocaleLowerCase", str_toLocaleLowerCase, 0,0),
JS_FN("std_String_toLocaleUpperCase", str_toLocaleUpperCase, 0,0),
JS_FN("std_String_normalize", str_normalize, 0,0),
@@ -2242,7 +2227,6 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("DecompileArg", intrinsic_DecompileArg, 2,0),
JS_FN("_FinishBoundFunctionInit", intrinsic_FinishBoundFunctionInit, 3,0),
JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0),
- JS_FN("AddContentTelemetry", intrinsic_AddContentTelemetry, 2,0),
JS_INLINABLE_FN("_IsConstructing", intrinsic_IsConstructing, 0,0,
IntrinsicIsConstructing),
@@ -2984,7 +2968,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..85bc044a5 100644
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -387,7 +387,7 @@ class BaseShape : public gc::TenuredCell
INDEXED = 0x20,
/* (0x40 is unused) */
HAD_ELEMENTS_ACCESS = 0x80,
- WATCHED = 0x100,
+ /* (0x100 is unused) */
ITERATED_SINGLETON = 0x200,
NEW_GROUP_UNKNOWN = 0x400,
UNCACHEABLE_PROTO = 0x800,
@@ -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/Stopwatch.cpp b/js/src/vm/Stopwatch.cpp
index 28632c2a1..684846f00 100644
--- a/js/src/vm/Stopwatch.cpp
+++ b/js/src/vm/Stopwatch.cpp
@@ -20,6 +20,7 @@
#include "gc/Zone.h"
#include "vm/Runtime.h"
+
namespace js {
bool
@@ -136,6 +137,9 @@ PerformanceMonitoring::start()
bool
PerformanceMonitoring::commit()
{
+ // Maximal initialization size, in elements for the vector of groups.
+ static const size_t MAX_GROUPS_INIT_CAPACITY = 1024;
+
#if !defined(MOZ_HAVE_RDTSC)
// The AutoStopwatch is only executed if `MOZ_HAVE_RDTSC`.
return false;
@@ -152,13 +156,24 @@ PerformanceMonitoring::commit()
return true;
}
- PerformanceGroupVector recentGroups;
- recentGroups_.swap(recentGroups);
+ // The move operation is generally constant time, unless
+ // `recentGroups_.length()` is very small, in which case
+ // it's fast just because it's small.
+ PerformanceGroupVector recentGroups(Move(recentGroups_));
+ recentGroups_ = PerformanceGroupVector(); // Reconstruct after `Move`.
bool success = true;
if (stopwatchCommitCallback)
success = stopwatchCommitCallback(iteration_, recentGroups, stopwatchCommitClosure);
+ // Heuristic: we expect to have roughly the same number of groups as in
+ // the previous iteration.
+ const size_t capacity = recentGroups.capacity() < MAX_GROUPS_INIT_CAPACITY ?
+ recentGroups.capacity() :
+ MAX_GROUPS_INIT_CAPACITY;
+ success = recentGroups_.reserve(capacity)
+ && success;
+
// Reset immediately, to make sure that we're not hit by the end
// of a nested event loop (which would cause `commit` to be called
// twice in succession).
@@ -227,7 +242,7 @@ AutoStopwatch::AutoStopwatch(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IM
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
JSCompartment* compartment = cx_->compartment();
- if (compartment->scheduledForDestruction)
+ if (MOZ_UNLIKELY(compartment->scheduledForDestruction))
return;
JSRuntime* runtime = cx_->runtime();
@@ -266,11 +281,11 @@ AutoStopwatch::~AutoStopwatch()
}
JSCompartment* compartment = cx_->compartment();
- if (compartment->scheduledForDestruction)
+ if (MOZ_UNLIKELY(compartment->scheduledForDestruction))
return;
JSRuntime* runtime = cx_->runtime();
- if (iteration_ != runtime->performanceMonitoring.iteration()) {
+ if (MOZ_UNLIKELY(iteration_ != runtime->performanceMonitoring.iteration())) {
// We have entered a nested event loop at some point.
// Any information we may have is obsolete.
return;
@@ -319,11 +334,6 @@ AutoStopwatch::exit()
const uint64_t cyclesEnd = getCycles(runtime);
cyclesDelta = cyclesEnd - cyclesStart_; // Always >= 0 by definition of `getCycles`.
}
-#if WINVER >= 0x600
- updateTelemetry(cpuStart_, cpuEnd);
-#elif defined(__linux__)
- updateTelemetry(cpuStart_, cpuEnd);
-#endif // WINVER >= 0x600 || _linux__
}
uint64_t CPOWTimeDelta = 0;
@@ -335,17 +345,6 @@ AutoStopwatch::exit()
return addToGroups(cyclesDelta, CPOWTimeDelta);
}
-void
-AutoStopwatch::updateTelemetry(const cpuid_t& cpuStart_, const cpuid_t& cpuEnd)
-{
- JSRuntime* runtime = cx_->runtime();
-
- if (isSameCPU(cpuStart_, cpuEnd))
- runtime->performanceMonitoring.testCpuRescheduling.stayed += 1;
- else
- runtime->performanceMonitoring.testCpuRescheduling.moved += 1;
-}
-
PerformanceGroup*
AutoStopwatch::acquireGroup(PerformanceGroup* group)
{
@@ -638,13 +637,6 @@ GetStopwatchIsMonitoringCPOW(JSContext* cx)
}
JS_PUBLIC_API(void)
-GetPerfMonitoringTestCpuRescheduling(JSContext* cx, uint64_t* stayed, uint64_t* moved)
-{
- *stayed = cx->performanceMonitoring.testCpuRescheduling.stayed;
- *moved = cx->performanceMonitoring.testCpuRescheduling.moved;
-}
-
-JS_PUBLIC_API(void)
AddCPOWPerformanceDelta(JSContext* cx, uint64_t delta)
{
cx->performanceMonitoring.totalCPOWTime += delta;
diff --git a/js/src/vm/Stopwatch.h b/js/src/vm/Stopwatch.h
index 38a3eb801..d7f299594 100644
--- a/js/src/vm/Stopwatch.h
+++ b/js/src/vm/Stopwatch.h
@@ -217,33 +217,6 @@ struct PerformanceMonitoring {
*/
uint64_t monotonicReadTimestampCounter();
- /**
- * Data extracted by the AutoStopwatch to determine how often
- * we reschedule the process to a different CPU during the
- * execution of JS.
- *
- * Warning: These values are incremented *only* on platforms
- * that offer a syscall/libcall to check on which CPU a
- * process is currently executed.
- */
- struct TestCpuRescheduling
- {
- // Incremented once we have finished executing code
- // in a group, if the CPU on which we started
- // execution is the same as the CPU on which
- // we finished.
- uint64_t stayed;
- // Incremented once we have finished executing code
- // in a group, if the CPU on which we started
- // execution is different from the CPU on which
- // we finished.
- uint64_t moved;
- TestCpuRescheduling()
- : stayed(0),
- moved(0)
- { }
- };
- TestCpuRescheduling testCpuRescheduling;
private:
PerformanceMonitoring(const PerformanceMonitoring&) = delete;
PerformanceMonitoring& operator=(const PerformanceMonitoring&) = delete;
@@ -375,9 +348,6 @@ class AutoStopwatch final {
// Add recent changes to a single group. Mark the group as changed recently.
bool addToGroup(JSRuntime* runtime, uint64_t cyclesDelta, uint64_t CPOWTimeDelta, PerformanceGroup* group);
- // Update telemetry statistics.
- void updateTelemetry(const cpuid_t& a, const cpuid_t& b);
-
// Perform a subtraction for a quantity that should be monotonic
// but is not guaranteed to be so.
//
diff --git a/js/src/vm/String.h b/js/src/vm/String.h
index 1a0c58575..514e2c205 100644
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -8,7 +8,6 @@
#define vm_String_h
#include "mozilla/MemoryReporting.h"
-#include "mozilla/PodOperations.h"
#include "mozilla/Range.h"
#include "jsapi.h"
@@ -1087,19 +1086,17 @@ class StaticStrings
static const size_t SMALL_CHAR_LIMIT = 128U;
static const size_t NUM_SMALL_CHARS = 64U;
- JSAtom* length2StaticTable[NUM_SMALL_CHARS * NUM_SMALL_CHARS];
+ JSAtom* length2StaticTable[NUM_SMALL_CHARS * NUM_SMALL_CHARS] = {}; // zeroes
public:
/* We keep these public for the JITs. */
static const size_t UNIT_STATIC_LIMIT = 256U;
- JSAtom* unitStaticTable[UNIT_STATIC_LIMIT];
+ JSAtom* unitStaticTable[UNIT_STATIC_LIMIT] = {}; // zeroes
static const size_t INT_STATIC_LIMIT = 256U;
- JSAtom* intStaticTable[INT_STATIC_LIMIT];
+ JSAtom* intStaticTable[INT_STATIC_LIMIT] = {}; // zeroes
- StaticStrings() {
- mozilla::PodZero(this);
- }
+ StaticStrings() = default;
bool init(JSContext* cx);
void trace(JSTracer* trc);
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/Time.cpp b/js/src/vm/Time.cpp
index 69e2cc41d..a9a5b7f0f 100644
--- a/js/src/vm/Time.cpp
+++ b/js/src/vm/Time.cpp
@@ -11,9 +11,10 @@
#include "mozilla/DebugOnly.h"
#include "mozilla/MathAlgorithms.h"
-#ifdef SOLARIS
+#ifdef XP_SOLARIS
#define _REENTRANT 1
#endif
+
#include <string.h>
#include <time.h>
@@ -33,7 +34,7 @@
#ifdef XP_UNIX
-#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
+#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
extern int gettimeofday(struct timeval* tv);
#endif
@@ -49,11 +50,11 @@ PRMJ_Now()
{
struct timeval tv;
-#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
+#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
gettimeofday(&tv);
#else
gettimeofday(&tv, 0);
-#endif /* _SVID_GETTOD */
+#endif /* _SVID_GETTOD */
return int64_t(tv.tv_sec) * PRMJ_USEC_PER_SEC + int64_t(tv.tv_usec);
}
diff --git a/js/src/vm/TypeInference-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 3d09c7464..2b1fa0e3b 100644
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -12,6 +12,8 @@
#include "mozilla/SizePrintfMacros.h"
#include "mozilla/Sprintf.h"
+#include <new>
+
#include "jsapi.h"
#include "jscntxt.h"
#include "jsgc.h"
@@ -33,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"
@@ -295,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)) {
@@ -859,10 +857,8 @@ TypeSet::IsTypeAboutToBeFinalized(TypeSet::Type* v)
}
bool
-TypeSet::clone(LifoAlloc* alloc, TemporaryTypeSet* result) const
+TypeSet::cloneIntoUninitialized(LifoAlloc* alloc, TemporaryTypeSet* result) const
{
- MOZ_ASSERT(result->empty());
-
unsigned objectCount = baseObjectCount();
unsigned capacity = (objectCount >= 2) ? TypeHashSet::Capacity(objectCount) : 0;
@@ -874,15 +870,15 @@ TypeSet::clone(LifoAlloc* alloc, TemporaryTypeSet* result) const
PodCopy(newSet, objectSet, capacity);
}
- new(result) TemporaryTypeSet(flags, capacity ? newSet : objectSet);
+ new (result) TemporaryTypeSet(flags, capacity ? newSet : objectSet);
return true;
}
TemporaryTypeSet*
TypeSet::clone(LifoAlloc* alloc) const
{
- TemporaryTypeSet* res = alloc->new_<TemporaryTypeSet>();
- if (!res || !clone(alloc, res))
+ TemporaryTypeSet* res = alloc->pod_malloc<TemporaryTypeSet>();
+ if (!res || !cloneIntoUninitialized(alloc, res))
return nullptr;
return res;
}
@@ -1150,10 +1146,9 @@ TypeScript::FreezeTypeSets(CompilerConstraintList* constraints, JSScript* script
TemporaryTypeSet* types = alloc->newArrayUninitialized<TemporaryTypeSet>(count);
if (!types)
return false;
- PodZero(types, count);
for (size_t i = 0; i < count; i++) {
- if (!existing[i].clone(alloc, &types[i]))
+ if (!existing[i].cloneIntoUninitialized(alloc, &types[i]))
return false;
}
@@ -1324,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;
}
@@ -1343,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());
@@ -1945,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
@@ -1996,17 +1968,6 @@ TypeSet::ObjectKey::watchStateChangeForTypedArrayData(CompilerConstraintList* co
ConstraintDataFreezeObjectForTypedArrayData(tarray)));
}
-void
-TypeSet::ObjectKey::watchStateChangeForUnboxedConvertedToNative(CompilerConstraintList* constraints)
-{
- HeapTypeSetKey objectProperty = property(JSID_EMPTY);
- LifoAlloc* alloc = constraints->alloc();
-
- typedef CompilerConstraintInstance<ConstraintDataFreezeObjectForUnboxedConvertedToNative> T;
- constraints->add(alloc->new_<T>(alloc, objectProperty,
- ConstraintDataFreezeObjectForUnboxedConvertedToNative()));
-}
-
static void
ObjectStateChange(ExclusiveContext* cxArg, ObjectGroup* group, bool markingUnknown)
{
@@ -2517,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()
@@ -2711,14 +2670,6 @@ ObjectGroup::updateNewPropertyTypes(ExclusiveContext* cx, JSObject* objArg, jsid
if (shape)
UpdatePropertyType(cx, types, obj, shape, false);
}
-
- if (obj->watched()) {
- /*
- * Mark the property as non-data, to inhibit optimizations on it
- * and avoid bypassing the watchpoint handler.
- */
- types->setNonDataProperty(cx);
- }
}
void
@@ -2817,15 +2768,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
@@ -2897,12 +2839,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
@@ -2935,23 +2871,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
@@ -2961,7 +2880,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()) {
@@ -2980,10 +2899,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
@@ -2994,7 +2910,7 @@ ObjectGroup::maybeClearNewScriptOnOOM()
if (!isMarked())
return;
- TypeNewScript* newScript = anyNewScript();
+ TypeNewScript* newScript = this->newScript();
if (!newScript)
return;
@@ -3009,7 +2925,7 @@ ObjectGroup::maybeClearNewScriptOnOOM()
void
ObjectGroup::clearNewScript(ExclusiveContext* cx, ObjectGroup* replacement /* = nullptr*/)
{
- TypeNewScript* newScript = anyNewScript();
+ TypeNewScript* newScript = this->newScript();
if (!newScript)
return;
@@ -3021,7 +2937,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();
}
@@ -3159,29 +3076,39 @@ class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint
};
bool
-js::AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx, ObjectGroup* group, HandleId id)
+js::AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx,
+ DPAConstraintInfo& constraintInfo,
+ ObjectGroup* group,
+ HandleId id,
+ bool* added)
{
/*
* Ensure that if the properties named here could have a getter, setter or
* a permanent property in any transitive prototype, the definite
* properties get cleared from the group.
*/
+
+ *added = false;
+
RootedObject proto(cx, group->proto().toObjectOrNull());
while (proto) {
- ObjectGroup* protoGroup = proto->getGroup(cx);
+ ObjectGroup* protoGroup = JSObject::getGroup(cx, proto);
if (!protoGroup) {
- cx->recoverFromOutOfMemory();
return false;
}
if (protoGroup->unknownProperties())
- return false;
+ return true;
HeapTypeSet* protoTypes = protoGroup->getProperty(cx, proto, id);
- if (!protoTypes || protoTypes->nonDataProperty() || protoTypes->nonWritableProperty())
+ if (!protoTypes)
return false;
- if (!protoTypes->addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(group)))
+ if (protoTypes->nonDataProperty() || protoTypes->nonWritableProperty())
+ return true;
+ if (!constraintInfo.addProtoConstraint(proto, id))
return false;
proto = proto->staticPrototype();
}
+
+ *added = true;
return true;
}
@@ -3405,7 +3332,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
@@ -3423,7 +3350,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) {
@@ -3463,22 +3390,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;
}
}
@@ -3578,17 +3489,11 @@ PreliminaryObjectArrayWithTemplate::maybeAnalyze(ExclusiveContext* cx, ObjectGro
}
}
- TryConvertToUnboxedLayout(cx, enter, shape(), group, preliminaryObjects);
- 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());
}
/////////////////////////////////////////////////////////////////////
@@ -3602,7 +3507,10 @@ 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.
+ MOZ_ASSERT(fun->maybeCanonicalFunction() == fun);
if (group->unknownProperties())
return true;
@@ -3706,6 +3614,43 @@ struct DestroyTypeNewScript
} // namespace
+bool DPAConstraintInfo::finishConstraints(JSContext* cx, ObjectGroup* group) {
+ for (const ProtoConstraint& constraint : protoConstraints_) {
+ ObjectGroup* protoGroup = constraint.proto->group();
+
+ // Note: we rely on the group's type information being unchanged since
+ // AddClearDefiniteGetterSetterForPrototypeChain.
+
+ bool unknownProperties = protoGroup->unknownProperties();
+ MOZ_RELEASE_ASSERT(!unknownProperties);
+
+ HeapTypeSet* protoTypes =
+ protoGroup->getProperty(cx, constraint.proto, constraint.id);
+ MOZ_RELEASE_ASSERT(protoTypes);
+
+ MOZ_ASSERT(!protoTypes->nonDataProperty());
+ MOZ_ASSERT(!protoTypes->nonWritableProperty());
+
+ if (!protoTypes->addConstraint(
+ cx,
+ cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(
+ group))) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+ }
+
+ for (const InliningConstraint& constraint : inliningConstraints_) {
+ if (!AddClearDefiniteFunctionUsesInScript(cx, group, constraint.caller,
+ constraint.callee)) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+ }
+
+ return true;
+}
+
bool
TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate, bool force)
{
@@ -3809,9 +3754,17 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate,
return false;
Vector<Initializer> initializerVector(cx);
+
+ DPAConstraintInfo constraintInfo(cx);
RootedPlainObject templateRoot(cx, templateObject());
- if (!jit::AnalyzeNewScriptDefiniteProperties(cx, function(), group, templateRoot, &initializerVector))
+ RootedFunction fun(cx, function());
+ if (!jit::AnalyzeNewScriptDefiniteProperties(cx,
+ constraintInfo,
+ fun,
+ group,
+ templateRoot,
+ &initializerVector))
return false;
if (!group->newScript())
@@ -3858,38 +3811,21 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate,
PodCopy(initializerList, initializerVector.begin(), initializerVector.length());
}
- // Try to use an unboxed representation for the group.
- if (!TryConvertToUnboxedLayout(cx, enter, templateObject()->lastProperty(), group, preliminaryObjects))
- return false;
-
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
// is needed.
+
+ if (!constraintInfo.finishConstraints(cx, group)) {
+ return false;
+ }
+ if (!group->newScript()) {
+ return true;
+ }
+
group->addDefiniteProperties(cx, templateObject()->lastProperty());
destroyNewScript.group = nullptr;
@@ -3911,6 +3847,16 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate,
if (!initialGroup)
return false;
+ // Add the constraints. Use the initialGroup as group referenced by the
+ // constraints because that's the group that will have the TypeNewScript
+ // associated with it. See the detachNewScript and setNewScript calls below.
+ if (!constraintInfo.finishConstraints(cx, initialGroup)) {
+ return false;
+ }
+ if (!group->newScript()) {
+ return true;
+ }
+
initialGroup->addDefiniteProperties(cx, templateObject()->lastProperty());
group->addDefiniteProperties(cx, prefixShape);
@@ -3959,8 +3905,15 @@ TypeNewScript::rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* g
oomUnsafe.crash("rollbackPartiallyInitializedObjects");
}
- if (!iter.isConstructing() || !iter.matchCallee(cx, function))
+ if (!iter.isConstructing()) {
continue;
+ }
+
+ MOZ_ASSERT(iter.calleeTemplate()->maybeCanonicalFunction());
+
+ if (iter.calleeTemplate()->maybeCanonicalFunction() != function) {
+ continue;
+ }
// Derived class constructors initialize their this-binding later and
// we shouldn't run the definite properties analysis on them.
@@ -3974,12 +3927,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>());
@@ -4173,12 +4120,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;
@@ -4262,21 +4203,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 9ba1c3cc8..fd021fc96 100644
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -262,7 +262,6 @@ class TypeSet
bool hasStableClassAndProto(CompilerConstraintList* constraints);
void watchStateChangeForInlinedCall(CompilerConstraintList* constraints);
void watchStateChangeForTypedArrayData(CompilerConstraintList* constraints);
- void watchStateChangeForUnboxedConvertedToNative(CompilerConstraintList* constraints);
HeapTypeSetKey property(jsid id);
void ensureTrackedProperty(JSContext* cx, jsid id);
@@ -498,7 +497,10 @@ class TypeSet
// Clone a type set into an arbitrary allocator.
TemporaryTypeSet* clone(LifoAlloc* alloc) const;
- bool clone(LifoAlloc* alloc, TemporaryTypeSet* result) const;
+
+ // |*result| is not even partly initialized when this function is called:
+ // this function placement-new's its contents into existence.
+ bool cloneIntoUninitialized(LifoAlloc* alloc, TemporaryTypeSet* result) const;
// Create a new TemporaryTypeSet where undefined and/or null has been filtered out.
TemporaryTypeSet* filter(LifoAlloc* alloc, bool filterUndefined, bool filterNull) const;
@@ -787,8 +789,65 @@ class TemporaryTypeSet : public TypeSet
TypedArraySharedness* sharedness);
};
+// Stack class to record information about constraints that need to be added
+// after finishing the Definite Properties Analysis. When the analysis succeeds,
+// the |finishConstraints| method must be called to add the constraints to the
+// TypeSets.
+//
+// There are two constraint types managed here:
+//
+// 1. Proto constraints for HeapTypeSets, to guard against things like getters
+// and setters on the proto chain.
+//
+// 2. Inlining constraints for StackTypeSets, to invalidate when additional
+// functions could be called at call sites where we inlined a function.
+//
+// This class uses bare GC-thing pointers because GC is suppressed when the
+// analysis runs.
+class MOZ_RAII DPAConstraintInfo {
+ struct ProtoConstraint {
+ JSObject* proto;
+ jsid id;
+ ProtoConstraint(JSObject* proto, jsid id) : proto(proto), id(id) {}
+ };
+ struct InliningConstraint {
+ JSScript* caller;
+ JSScript* callee;
+ InliningConstraint(JSScript* caller, JSScript* callee)
+ : caller(caller), callee(callee) {}
+ };
+
+ JS::AutoCheckCannotGC nogc_;
+ Vector<ProtoConstraint, 8> protoConstraints_;
+ Vector<InliningConstraint, 4> inliningConstraints_;
+
+public:
+ explicit DPAConstraintInfo(JSContext* cx)
+ : nogc_(cx)
+ , protoConstraints_(cx)
+ , inliningConstraints_(cx)
+ {
+ }
+
+ DPAConstraintInfo(const DPAConstraintInfo&) = delete;
+ void operator=(const DPAConstraintInfo&) = delete;
+
+ MOZ_MUST_USE bool addProtoConstraint(JSObject* proto, jsid id) {
+ return protoConstraints_.emplaceBack(proto, id);
+ }
+ MOZ_MUST_USE bool addInliningConstraint(JSScript* caller, JSScript* callee) {
+ return inliningConstraints_.emplaceBack(caller, callee);
+ }
+
+ MOZ_MUST_USE bool finishConstraints(JSContext* cx, ObjectGroup* group);
+};
+
bool
-AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx, ObjectGroup* group, HandleId id);
+AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx,
+ DPAConstraintInfo& constraintInfo,
+ ObjectGroup* group,
+ HandleId id,
+ bool* added);
bool
AddClearDefiniteFunctionUsesInScript(JSContext* cx, ObjectGroup* group,
@@ -807,15 +866,13 @@ class PreliminaryObjectArray
private:
// All objects with the type which have been allocated. The pointers in
// this array are weak.
- JSObject* objects[COUNT];
+ JSObject* objects[COUNT] = {}; // zeroes
public:
- PreliminaryObjectArray() {
- mozilla::PodZero(this);
- }
+ 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);
@@ -906,11 +963,11 @@ class TypeNewScript
private:
// Scripted function which this information was computed for.
- HeapPtr<JSFunction*> function_;
+ HeapPtr<JSFunction*> function_ = {};
// Any preliminary objects with the type. The analyses are not performed
// until this array is cleared.
- PreliminaryObjectArray* preliminaryObjects;
+ PreliminaryObjectArray* preliminaryObjects = nullptr;
// After the new script properties analyses have been performed, a template
// object to use for newly constructed objects. The shape of this object
@@ -918,7 +975,7 @@ class TypeNewScript
// allocation kind to use. This is null if the new objects have an unboxed
// layout, in which case the UnboxedLayout provides the initial structure
// of the object.
- HeapPtr<PlainObject*> templateObject_;
+ HeapPtr<PlainObject*> templateObject_ = {};
// Order in which definite properties become initialized. We need this in
// case the definite properties are invalidated (such as by adding a setter
@@ -928,21 +985,21 @@ class TypeNewScript
// shape. Property assignments in inner frames are preceded by a series of
// SETPROP_FRAME entries specifying the stack down to the frame containing
// the write.
- Initializer* initializerList;
+ Initializer* initializerList = nullptr;
// If there are additional properties found by the acquired properties
// analysis which were not found by the definite properties analysis, this
// shape contains all such additional properties (plus the definite
// properties). When an object of this group acquires this shape, it is
// fully initialized and its group can be changed to initializedGroup.
- HeapPtr<Shape*> initializedShape_;
+ HeapPtr<Shape*> initializedShape_ = {};
// Group with definite properties set for all properties found by
// both the definite and acquired properties analyses.
- HeapPtr<ObjectGroup*> initializedGroup_;
+ HeapPtr<ObjectGroup*> initializedGroup_ = {};
public:
- TypeNewScript() { mozilla::PodZero(this); }
+ TypeNewScript() = default;
~TypeNewScript() {
js_delete(preliminaryObjects);
js_free(initializerList);
diff --git a/js/src/vm/TypedArrayCommon.h b/js/src/vm/TypedArrayCommon.h
index d29c93a65..f59419b28 100644
--- a/js/src/vm/TypedArrayCommon.h
+++ b/js/src/vm/TypedArrayCommon.h
@@ -11,7 +11,8 @@
#include "mozilla/Assertions.h"
#include "mozilla/FloatingPoint.h"
-#include "mozilla/PodOperations.h"
+
+#include <algorithm>
#include "jsarray.h"
#include "jscntxt.h"
@@ -245,12 +246,24 @@ class UnsharedOps
template<typename T>
static void podCopy(SharedMem<T*> dest, SharedMem<T*> src, size_t nelem) {
- mozilla::PodCopy(dest.unwrapUnshared(), src.unwrapUnshared(), nelem);
+ // std::copy_n better matches the argument values/types of this
+ // function, but as noted below it allows the input/output ranges to
+ // overlap. std::copy does not, so use it so the compiler has extra
+ // ability to optimize.
+ const auto* first = src.unwrapUnshared();
+ const auto* last = first + nelem;
+ auto* result = dest.unwrapUnshared();
+ std::copy(first, last, result);
}
template<typename T>
- static void podMove(SharedMem<T*> dest, SharedMem<T*> src, size_t nelem) {
- mozilla::PodMove(dest.unwrapUnshared(), src.unwrapUnshared(), nelem);
+ static void podMove(SharedMem<T*> dest, SharedMem<T*> src, size_t n) {
+ // std::copy_n copies from |src| to |dest| starting from |src|, so
+ // input/output ranges *may* permissibly overlap, as this function
+ // allows.
+ const auto* start = src.unwrapUnshared();
+ auto* result = dest.unwrapUnshared();
+ std::copy_n(start, n, result);
}
static SharedMem<void*> extract(TypedArrayObject* obj) {
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 3018ace67..000000000
--- a/js/src/vm/UnboxedObject.cpp
+++ /dev/null
@@ -1,2152 +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 bool
-UnboxedTypeIncludes(JSValueType supertype, JSValueType subtype)
-{
- if (supertype == JSVAL_TYPE_DOUBLE && subtype == JSVAL_TYPE_INT32)
- return true;
- if (supertype == JSVAL_TYPE_OBJECT && subtype == JSVAL_TYPE_NULL)
- return true;
- return false;
-}
-
-static bool
-CombineUnboxedTypes(const Value& value, JSValueType* existing)
-{
- JSValueType type = value.isDouble() ? JSVAL_TYPE_DOUBLE : value.extractNonDoubleType();
-
- if (*existing == JSVAL_TYPE_MAGIC || *existing == type || UnboxedTypeIncludes(type, *existing)) {
- *existing = type;
- return true;
- }
- if (UnboxedTypeIncludes(*existing, type))
- return true;
- return false;
-}
-
-// Return whether the property names and types in layout are a subset of the
-// specified vector.
-static bool
-PropertiesAreSuperset(const UnboxedLayout::PropertyVector& properties, UnboxedLayout* layout)
-{
- for (size_t i = 0; i < layout->properties().length(); i++) {
- const UnboxedLayout::Property& layoutProperty = layout->properties()[i];
- bool found = false;
- for (size_t j = 0; j < properties.length(); j++) {
- if (layoutProperty.name == properties[j].name) {
- found = (layoutProperty.type == properties[j].type);
- break;
- }
- }
- if (!found)
- return false;
- }
- return true;
-}
-
-static bool
-CombinePlainObjectProperties(PlainObject* obj, Shape* templateShape,
- UnboxedLayout::PropertyVector& properties)
-{
- // All preliminary objects must have been created with enough space to
- // fill in their unboxed data inline. This is ensured either by using
- // the largest allocation kind (which limits the maximum size of an
- // unboxed object), or by using an allocation kind that covers all
- // properties in the template, as the space used by unboxed properties
- // is less than or equal to that used by boxed properties.
- MOZ_ASSERT(gc::GetGCKindSlots(obj->asTenured().getAllocKind()) >=
- Min(NativeObject::MAX_FIXED_SLOTS, templateShape->slotSpan()));
-
- if (obj->lastProperty() != templateShape || obj->hasDynamicElements()) {
- // Only use an unboxed representation if all created objects match
- // the template shape exactly.
- return false;
- }
-
- for (size_t i = 0; i < templateShape->slotSpan(); i++) {
- Value val = obj->getSlot(i);
-
- JSValueType& existing = properties[i].type;
- if (!CombineUnboxedTypes(val, &existing))
- return false;
- }
-
- return true;
-}
-
-static bool
-CombineArrayObjectElements(ExclusiveContext* cx, ArrayObject* obj, JSValueType* elementType)
-{
- if (obj->inDictionaryMode() ||
- obj->lastProperty()->propid() != AtomToId(cx->names().length) ||
- !obj->lastProperty()->previous()->isEmptyShape())
- {
- // Only use an unboxed representation if the object has no properties.
- return false;
- }
-
- for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) {
- Value val = obj->getDenseElement(i);
-
- // For now, unboxed arrays cannot have holes.
- if (val.isMagic(JS_ELEMENTS_HOLE))
- return false;
-
- if (!CombineUnboxedTypes(val, elementType))
- return false;
- }
-
- return true;
-}
-
-static size_t
-ComputePlainObjectLayout(ExclusiveContext* cx, Shape* templateShape,
- UnboxedLayout::PropertyVector& properties)
-{
- // Fill in the names for all the object's properties.
- for (Shape::Range<NoGC> r(templateShape); !r.empty(); r.popFront()) {
- size_t slot = r.front().slot();
- MOZ_ASSERT(!properties[slot].name);
- properties[slot].name = JSID_TO_ATOM(r.front().propid())->asPropertyName();
- }
-
- // Fill in all the unboxed object's property offsets.
- uint32_t offset = 0;
-
- // Search for an existing unboxed layout which is a subset of this one.
- // If there are multiple such layouts, use the largest one. If we're able
- // to find such a layout, use the same property offsets for the shared
- // properties, which will allow us to generate better code if the objects
- // have a subtype/supertype relation and are accessed at common sites.
- UnboxedLayout* bestExisting = nullptr;
- for (UnboxedLayout* existing : cx->compartment()->unboxedLayouts) {
- if (PropertiesAreSuperset(properties, existing)) {
- if (!bestExisting ||
- existing->properties().length() > bestExisting->properties().length())
- {
- bestExisting = existing;
- }
- }
- }
- if (bestExisting) {
- for (size_t i = 0; i < bestExisting->properties().length(); i++) {
- const UnboxedLayout::Property& existingProperty = bestExisting->properties()[i];
- for (size_t j = 0; j < templateShape->slotSpan(); j++) {
- if (existingProperty.name == properties[j].name) {
- MOZ_ASSERT(existingProperty.type == properties[j].type);
- properties[j].offset = existingProperty.offset;
- }
- }
- }
- offset = bestExisting->size();
- }
-
- // Order remaining properties from the largest down for the best space
- // utilization.
- static const size_t typeSizes[] = { 8, 4, 1 };
-
- for (size_t i = 0; i < ArrayLength(typeSizes); i++) {
- size_t size = typeSizes[i];
- for (size_t j = 0; j < templateShape->slotSpan(); j++) {
- if (properties[j].offset != UINT32_MAX)
- continue;
- JSValueType type = properties[j].type;
- if (UnboxedTypeSize(type) == size) {
- offset = JS_ROUNDUP(offset, size);
- properties[j].offset = offset;
- offset += size;
- }
- }
- }
-
- // The final offset is the amount of data needed by the object.
- return offset;
-}
-
-static bool
-SetLayoutTraceList(ExclusiveContext* cx, UnboxedLayout* layout)
-{
- // Figure out the offsets of any objects or string properties.
- Vector<int32_t, 8, SystemAllocPolicy> objectOffsets, stringOffsets;
- for (size_t i = 0; i < layout->properties().length(); i++) {
- const UnboxedLayout::Property& property = layout->properties()[i];
- MOZ_ASSERT(property.offset != UINT32_MAX);
- if (property.type == JSVAL_TYPE_OBJECT) {
- if (!objectOffsets.append(property.offset))
- return false;
- } else if (property.type == JSVAL_TYPE_STRING) {
- if (!stringOffsets.append(property.offset))
- return false;
- }
- }
-
- // Construct the layout's trace list.
- if (!objectOffsets.empty() || !stringOffsets.empty()) {
- Vector<int32_t, 8, SystemAllocPolicy> entries;
- if (!entries.appendAll(stringOffsets) ||
- !entries.append(-1) ||
- !entries.appendAll(objectOffsets) ||
- !entries.append(-1) ||
- !entries.append(-1))
- {
- return false;
- }
- int32_t* traceList = cx->zone()->pod_malloc<int32_t>(entries.length());
- if (!traceList)
- return false;
- PodCopy(traceList, entries.begin(), entries.length());
- layout->setTraceList(traceList);
- }
-
- return true;
-}
-
-static inline Value
-NextValue(Handle<GCVector<Value>> values, size_t* valueCursor)
-{
- return values[(*valueCursor)++];
-}
-
-static bool
-GetValuesFromPreliminaryArrayObject(ArrayObject* obj, MutableHandle<GCVector<Value>> values)
-{
- if (!values.append(Int32Value(obj->length())))
- return false;
- if (!values.append(Int32Value(obj->getDenseInitializedLength())))
- return false;
- for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) {
- if (!values.append(obj->getDenseElement(i)))
- return false;
- }
- return true;
-}
-
-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)));
-}
-
-static bool
-GetValuesFromPreliminaryPlainObject(PlainObject* obj, MutableHandle<GCVector<Value>> values)
-{
- for (size_t i = 0; i < obj->slotSpan(); i++) {
- if (!values.append(obj->getSlot(i)))
- return false;
- }
- return true;
-}
-
-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)));
-}
-
-bool
-js::TryConvertToUnboxedLayout(ExclusiveContext* cx, AutoEnterAnalysis& enter, Shape* templateShape,
- ObjectGroup* group, PreliminaryObjectArray* objects)
-{
- bool isArray = !templateShape;
-
- // Unboxed arrays are nightly only for now. The getenv() call will be
- // removed when they are on by default. See bug 1153266.
- if (isArray) {
-#ifdef NIGHTLY_BUILD
- if (!getenv("JS_OPTION_USE_UNBOXED_ARRAYS")) {
- if (!cx->options().unboxedArrays())
- return true;
- }
-#else
- return true;
-#endif
- } else {
- if (jit::JitOptions.disableUnboxedObjects)
- return true;
- }
-
- MOZ_ASSERT_IF(templateShape, !templateShape->getObjectFlags());
-
- if (group->runtimeFromAnyThread()->isSelfHostingGlobal(cx->global()))
- return true;
-
- if (!isArray && templateShape->slotSpan() == 0)
- return true;
-
- UnboxedLayout::PropertyVector properties;
- if (!isArray) {
- if (!properties.appendN(UnboxedLayout::Property(), templateShape->slotSpan()))
- return false;
- }
- JSValueType elementType = JSVAL_TYPE_MAGIC;
-
- size_t objectCount = 0;
- for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
- JSObject* obj = objects->get(i);
- if (!obj)
- continue;
-
- if (obj->isSingleton() || obj->group() != group)
- return true;
-
- objectCount++;
-
- if (isArray) {
- if (!CombineArrayObjectElements(cx, &obj->as<ArrayObject>(), &elementType))
- return true;
- } else {
- if (!CombinePlainObjectProperties(&obj->as<PlainObject>(), templateShape, properties))
- return true;
- }
- }
-
- size_t layoutSize = 0;
- if (isArray) {
- // Don't use an unboxed representation if we couldn't determine an
- // element type for the objects.
- if (UnboxedTypeSize(elementType) == 0)
- return true;
- } else {
- if (objectCount <= 1) {
- // If only one of the objects has been created, it is more likely
- // to have new properties added later. This heuristic is not used
- // for array objects, where we might want an unboxed representation
- // even if there is only one large array.
- return true;
- }
-
- for (size_t i = 0; i < templateShape->slotSpan(); i++) {
- // We can't use an unboxed representation if e.g. all the objects have
- // a null value for one of the properties, as we can't decide what type
- // it is supposed to have.
- if (UnboxedTypeSize(properties[i].type) == 0)
- return true;
- }
-
- // Make sure that all properties on the template shape are property
- // names, and not indexes.
- for (Shape::Range<NoGC> r(templateShape); !r.empty(); r.popFront()) {
- jsid id = r.front().propid();
- uint32_t dummy;
- if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&dummy))
- return true;
- }
-
- layoutSize = ComputePlainObjectLayout(cx, templateShape, properties);
-
- // The entire object must be allocatable inline.
- if (UnboxedPlainObject::offsetOfData() + layoutSize > JSObject::MAX_BYTE_SIZE)
- return true;
- }
-
- UniquePtr<UnboxedLayout>& layout = enter.unboxedLayoutToCleanUp;
- MOZ_ASSERT(!layout);
- layout = group->zone()->make_unique<UnboxedLayout>();
- if (!layout)
- return false;
-
- if (isArray) {
- layout->initArray(elementType);
- } else {
- if (!layout->initProperties(properties, layoutSize))
- return false;
-
- // The unboxedLayouts list only tracks layouts for plain objects.
- cx->compartment()->unboxedLayouts.insertFront(layout.get());
-
- if (!SetLayoutTraceList(cx, layout.get()))
- return false;
- }
-
- // We've determined that all the preliminary objects can use the new layout
- // just constructed, so convert the existing group to use the unboxed class,
- // and update the preliminary objects to use the new layout. Do the
- // fallible stuff first before modifying any objects.
-
- // Get an empty shape which we can use for the preliminary objects.
- const Class* clasp = isArray ? &UnboxedArrayObject::class_ : &UnboxedPlainObject::class_;
- Shape* newShape = EmptyShape::getInitialShape(cx, clasp, group->proto(), 0);
- if (!newShape) {
- cx->recoverFromOutOfMemory();
- return false;
- }
-
- // Accumulate a list of all the values in each preliminary object, and
- // update their shapes.
- Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
- for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
- JSObject* obj = objects->get(i);
- if (!obj)
- continue;
-
- bool ok;
- if (isArray)
- ok = GetValuesFromPreliminaryArrayObject(&obj->as<ArrayObject>(), &values);
- else
- ok = GetValuesFromPreliminaryPlainObject(&obj->as<PlainObject>(), &values);
-
- if (!ok) {
- cx->recoverFromOutOfMemory();
- return false;
- }
- }
-
- if (TypeNewScript* newScript = group->newScript())
- layout->setNewScript(newScript);
-
- for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
- if (JSObject* obj = objects->get(i))
- obj->as<NativeObject>().setLastPropertyMakeNonNative(newShape);
- }
-
- group->setClasp(clasp);
- group->setUnboxedLayout(layout.release());
-
- size_t valueCursor = 0;
- for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
- JSObject* obj = objects->get(i);
- if (!obj)
- continue;
-
- if (isArray)
- obj->as<UnboxedArrayObject>().fillAfterConvert(cx, values, &valueCursor);
- else
- obj->as<UnboxedPlainObject>().fillAfterConvert(cx, values, &valueCursor);
- }
-
- MOZ_ASSERT(valueCursor == values.length());
- return true;
-}
-
-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 ecff8be5b..000000000
--- a/js/src/vm/UnboxedObject.h
+++ /dev/null
@@ -1,531 +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]);
- }
-};
-
-// Try to construct an UnboxedLayout for each of the preliminary objects,
-// provided they all match the template shape. If successful, converts the
-// preliminary objects and their group to the new unboxed representation.
-bool
-TryConvertToUnboxedLayout(ExclusiveContext* cx, AutoEnterAnalysis& enter, Shape* templateShape,
- ObjectGroup* group, PreliminaryObjectArray* objects);
-
-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 7fade24fb..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"
@@ -249,14 +250,14 @@ typedef Vector<AsmJSImport, 0, SystemAllocPolicy> AsmJSImportVector;
// case the function is toString()ed.
class AsmJSExport
{
- uint32_t funcIndex_;
+ uint32_t funcIndex_ = 0;
// All fields are treated as cacheable POD:
- uint32_t startOffsetInModule_; // Store module-start-relative offsets
- uint32_t endOffsetInModule_; // so preserved by serialization.
+ uint32_t startOffsetInModule_ = 0; // Store module-start-relative offsets
+ uint32_t endOffsetInModule_ = 0; // so preserved by serialization.
public:
- AsmJSExport() { PodZero(this); }
+ AsmJSExport() = default;
AsmJSExport(uint32_t funcIndex, uint32_t startOffsetInModule, uint32_t endOffsetInModule)
: funcIndex_(funcIndex),
startOffsetInModule_(startOffsetInModule),
@@ -288,12 +289,12 @@ enum class CacheResult
struct AsmJSMetadataCacheablePod
{
- uint32_t numFFIs;
- uint32_t srcLength;
- uint32_t srcLengthWithRightBrace;
- bool usesSimd;
+ uint32_t numFFIs = 0;
+ uint32_t srcLength = 0;
+ uint32_t srcLengthWithRightBrace = 0;
+ bool usesSimd = false;
- AsmJSMetadataCacheablePod() { PodZero(this); }
+ AsmJSMetadataCacheablePod() = default;
};
struct js::AsmJSMetadata : Metadata, AsmJSMetadataCacheablePod
@@ -318,6 +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 toStringStart;
uint32_t srcStart;
uint32_t srcBodyStart;
bool strict;
@@ -1758,6 +1760,7 @@ class MOZ_STACK_CLASS ModuleValidator
if (!asmJSMetadata_)
return false;
+ 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() &&
@@ -3248,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");
@@ -7049,19 +7051,20 @@ ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line)
TokenStream& tokenStream = m.tokenStream();
tokenStream.consumeKnownToken(TOK_FUNCTION, TokenStream::Operand);
+ 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));
if (!name)
return false;
- ParseNode* fn = m.parser().handler.newFunctionDefinition();
+ ParseNode* fn = m.parser().handler.newFunctionStatement();
if (!fn)
return false;
@@ -7071,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, directives, NotGenerator,
+ FunctionBox* funbox = m.parser().newFunctionBox(fn, fun, toStringStart, directives, NotGenerator,
SyncFunction, /* tryAnnexB = */ false);
if (!funbox)
return false;
@@ -7463,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.
@@ -7476,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;
}
@@ -8054,7 +8071,7 @@ HandleInstantiationFailure(JSContext* cx, CallArgs args, const AsmJSMetadata& me
return false;
}
- uint32_t begin = metadata.srcStart;
+ uint32_t begin = metadata.toStringStart;
uint32_t end = metadata.srcEndAfterCurly();
Rooted<JSFlatString*> src(cx, source->substringDontDeflate(cx, begin, end));
if (!src)
@@ -8085,7 +8102,7 @@ HandleInstantiationFailure(JSContext* cx, CallArgs args, const AsmJSMetadata& me
SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller()
? SourceBufferHolder::GiveOwnership
: SourceBufferHolder::NoOwnership;
- SourceBufferHolder srcBuf(chars, stableChars.twoByteRange().length(), ownership);
+ SourceBufferHolder srcBuf(chars, end - begin, ownership);
if (!frontend::CompileStandaloneFunction(cx, &fun, options, srcBuf, Nothing()))
return false;
@@ -8537,6 +8554,7 @@ LookupAsmJSModuleInCache(ExclusiveContext* cx, AsmJSParser& parser, bool* loaded
return true;
// See AsmJSMetadata comment as well as ModuleValidator::init().
+ 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();
@@ -8834,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.srcStart;
+ uint32_t begin = metadata.toStringStart;
uint32_t end = metadata.srcEndAfterCurly();
ScriptSource* source = metadata.scriptSource.get();
@@ -8843,17 +8861,15 @@ js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda
if (addParenToLambda && fun->isLambda() && !out.append("("))
return nullptr;
- if (!out.append("function "))
- return nullptr;
-
- if (fun->explicitName() && !out.append(fun->explicitName()))
- return nullptr;
-
bool haveSource = source->hasSourceData();
if (!haveSource && !JSScript::loadSource(cx, source, &haveSource))
return nullptr;
if (!haveSource) {
+ if (!out.append("function "))
+ return nullptr;
+ if (fun->explicitName() && !out.append(fun->explicitName()))
+ return nullptr;
if (!out.append("() {\n [sourceless code]\n}"))
return nullptr;
} else {
diff --git a/js/src/wasm/WasmBinaryConstants.h b/js/src/wasm/WasmBinaryConstants.h
index fd3bd1264..9aa5091f6 100644
--- a/js/src/wasm/WasmBinaryConstants.h
+++ b/js/src/wasm/WasmBinaryConstants.h
@@ -434,15 +434,6 @@ enum class Op
Limit
};
-// Telemetry sample values for the JS_AOT_USAGE key, indicating whether asm.js
-// or WebAssembly is used.
-
-enum class Telemetry
-{
- ASMJS = 0,
- WASM = 1
-};
-
} // namespace wasm
} // namespace js
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/src/wasm/WasmModule.cpp b/js/src/wasm/WasmModule.cpp
index b24e01a40..f1ecd8620 100644
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -1066,8 +1066,5 @@ Module::instantiate(JSContext* cx,
return false;
}
- uint32_t mode = uint32_t(metadata().isAsmJS() ? Telemetry::ASMJS : Telemetry::WASM);
- cx->runtime()->addTelemetry(JS_TELEMETRY_AOT_USAGE, mode);
-
return true;
}
diff --git a/js/src/wasm/WasmSignalHandlers.cpp b/js/src/wasm/WasmSignalHandlers.cpp
index 78d21369d..21093ca9a 100644
--- a/js/src/wasm/WasmSignalHandlers.cpp
+++ b/js/src/wasm/WasmSignalHandlers.cpp
@@ -130,11 +130,16 @@ class AutoSetHandlingSegFault
# define EPC_sig(p) ((p)->sc_pc)
# define RFP_sig(p) ((p)->sc_regs[30])
# endif
-#elif defined(__linux__) || defined(SOLARIS)
+#elif defined(__linux__) || defined(__sun)
# if defined(__linux__)
# define XMM_sig(p,i) ((p)->uc_mcontext.fpregs->_xmm[i])
# define EIP_sig(p) ((p)->uc_mcontext.gregs[REG_EIP])
-# else
+# else // defined(__sun)
+/* See https://www.illumos.org/issues/5876. They keep arguing over whether
+ * <ucontext.h> should provide the register index defines in regset.h or
+ * require applications to request them specifically, and we need them here. */
+#include <ucontext.h>
+#include <sys/regset.h>
# define XMM_sig(p,i) ((p)->uc_mcontext.fpregs.fp_reg_set.fpchip_state.xmm[i])
# define EIP_sig(p) ((p)->uc_mcontext.gregs[REG_PC])
# endif