Bug 1655465 - Part 9: Change JSOp::FunctionProto to JSOp::BuiltinObject. r=jandem

Callers to `GetBuiltinPrototype()` rely on inlining the function itself plus
optimising the object access, so that the property value is directly seen as a
constant in the compiler. By changing `GetBuiltinPrototype()` and
`GetBuiltinConstructor()` to be directly translated into a JSOp, we can avoid
heavily relying on the compiler to optimise these two functions.

The new opcode replaces the existing JSOp::FunctionProto opcode. It doesn't
use JSProtoKey directly in order to help jsparagus (bug 1622530), but instead
uses its own set of mapping from enum values to built-in prototypes and
constructors.

This change also resolves bug 1377264.

Differential Revision: https://phabricator.services.mozilla.com/D84991
This commit is contained in:
André Bargull 2020-08-07 13:03:19 +00:00
Родитель 17346573ef
Коммит 8aa2e27c24
29 изменённых файлов: 426 добавлений и 132 удалений

Просмотреть файл

@ -103,19 +103,6 @@ function IsPropertyKey(argument) {
#define TO_PROPERTY_KEY(name) \
(typeof name !== "string" && typeof name !== "number" && typeof name !== "symbol" ? ToPropertyKey(name) : name)
var _builtinCtorsCache = {__proto__: null};
function GetBuiltinConstructor(builtinName) {
var ctor = _builtinCtorsCache[builtinName] ||
(_builtinCtorsCache[builtinName] = GetBuiltinConstructorImpl(builtinName));
assert(ctor, `No builtin with name "${builtinName}" found`);
return ctor;
}
function GetBuiltinPrototype(builtinName) {
return (_builtinCtorsCache[builtinName] || GetBuiltinConstructor(builtinName)).prototype;
}
// ES 2016 draft Mar 25, 2016 7.3.20.
function SpeciesConstructor(obj, defaultConstructor) {
// Step 1.

Просмотреть файл

@ -1544,7 +1544,7 @@ static bool BytecodeIsEffectful(JSOp op) {
case JSOp::CheckClassHeritage:
case JSOp::FunWithProto:
case JSOp::ObjWithProto:
case JSOp::FunctionProto:
case JSOp::BuiltinObject:
case JSOp::DerivedConstructor:
case JSOp::CheckThis:
case JSOp::CheckReturn:

Просмотреть файл

@ -510,6 +510,10 @@ bool BytecodeEmitter::emitCheckIsObj(CheckIsObjectKind kind) {
return emit2(JSOp::CheckIsObj, uint8_t(kind));
}
bool BytecodeEmitter::emitBuiltinObject(BuiltinObjectKind kind) {
return emit2(JSOp::BuiltinObject, uint8_t(kind));
}
/* Updates line number notes, not column notes. */
bool BytecodeEmitter::updateLineNumberNotes(uint32_t offset) {
if (skipLocationSrcNotes()) {
@ -7304,6 +7308,54 @@ bool BytecodeEmitter::emitSelfHostedToString(BinaryNode* callNode) {
return emit1(JSOp::ToString);
}
bool BytecodeEmitter::emitSelfHostedGetBuiltinConstructorOrPrototype(
BinaryNode* callNode, bool isConstructor) {
ListNode* argsList = &callNode->right()->as<ListNode>();
if (argsList->count() != 1) {
const char* name =
isConstructor ? "GetBuiltinConstructor" : "GetBuiltinPrototype";
reportNeedMoreArgsError(callNode, name, "1", "", argsList);
return false;
}
ParseNode* argNode = argsList->head();
if (!argNode->isKind(ParseNodeKind::StringExpr)) {
reportError(callNode, JSMSG_UNEXPECTED_TYPE, "built-in name",
"not a string constant");
return false;
}
JSAtom* name = argNode->as<NameNode>().atom();
BuiltinObjectKind kind;
if (isConstructor) {
kind = BuiltinConstructorForName(cx, name);
} else {
kind = BuiltinPrototypeForName(cx, name);
}
if (kind == BuiltinObjectKind::None) {
reportError(callNode, JSMSG_UNEXPECTED_TYPE, "built-in name",
"not a valid built-in");
return false;
}
return emitBuiltinObject(kind);
}
bool BytecodeEmitter::emitSelfHostedGetBuiltinConstructor(
BinaryNode* callNode) {
return emitSelfHostedGetBuiltinConstructorOrPrototype(
callNode, /* isConstructor = */ true);
}
bool BytecodeEmitter::emitSelfHostedGetBuiltinPrototype(BinaryNode* callNode) {
return emitSelfHostedGetBuiltinConstructorOrPrototype(
callNode, /* isConstructor = */ false);
}
#ifdef DEBUG
bool BytecodeEmitter::checkSelfHostedUnsafeGetReservedSlot(
BinaryNode* callNode) {
@ -7825,6 +7877,12 @@ bool BytecodeEmitter::emitCallOrNew(
if (calleeName == cx->parserNames().ToString) {
return emitSelfHostedToString(callNode);
}
if (calleeName == cx->parserNames().GetBuiltinConstructor) {
return emitSelfHostedGetBuiltinConstructor(callNode);
}
if (calleeName == cx->parserNames().GetBuiltinPrototype) {
return emitSelfHostedGetBuiltinPrototype(callNode);
}
#ifdef DEBUG
if (calleeName == cx->parserNames().UnsafeGetReservedSlot ||
calleeName == cx->parserNames().UnsafeGetObjectFromReservedSlot ||

Просмотреть файл

@ -41,6 +41,7 @@
#include "frontend/ValueUsage.h" // ValueUsage
#include "js/RootingAPI.h" // JS::Rooted, JS::Handle
#include "js/TypeDecls.h" // jsbytecode
#include "vm/BuiltinObjectKind.h" // BuiltinObjectKind
#include "vm/BytecodeUtil.h" // JSOp
#include "vm/CheckIsObjectKind.h" // CheckIsObjectKind
#include "vm/FunctionPrefixKind.h" // FunctionPrefixKind
@ -404,6 +405,9 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
// Helper to emit JSOp::CheckIsObj.
MOZ_MUST_USE bool emitCheckIsObj(CheckIsObjectKind kind);
// Helper to emit JSOp::BuiltinObject.
MOZ_MUST_USE bool emitBuiltinObject(BuiltinObjectKind kind);
// Push whether the value atop of the stack is non-undefined and non-null.
MOZ_MUST_USE bool emitPushNotUndefinedOrNull();
@ -763,6 +767,8 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
MOZ_MUST_USE bool emitSelfHostedHasOwn(BinaryNode* callNode);
MOZ_MUST_USE bool emitSelfHostedToNumeric(BinaryNode* callNode);
MOZ_MUST_USE bool emitSelfHostedToString(BinaryNode* callNode);
MOZ_MUST_USE bool emitSelfHostedGetBuiltinConstructor(BinaryNode* callNode);
MOZ_MUST_USE bool emitSelfHostedGetBuiltinPrototype(BinaryNode* callNode);
#ifdef DEBUG
MOZ_MUST_USE bool checkSelfHostedUnsafeGetReservedSlot(BinaryNode* callNode);
MOZ_MUST_USE bool checkSelfHostedUnsafeSetReservedSlot(BinaryNode* callNode);
@ -864,6 +870,9 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
GCThingIndex atomIndex);
MOZ_MUST_USE bool allowSelfHostedIter(ParseNode* parseNode);
MOZ_MUST_USE bool emitSelfHostedGetBuiltinConstructorOrPrototype(
BinaryNode* callNode, bool isConstructor);
};
class MOZ_RAII AutoCheckUnstableEmitterScope {

Просмотреть файл

@ -565,7 +565,7 @@ bool ClassEmitter::emitDerivedClass(JS::Handle<JSAtom*> name,
// [stack]
return false;
}
if (!bce_->emit1(JSOp::FunctionProto)) {
if (!bce_->emitBuiltinObject(BuiltinObjectKind::FunctionPrototype)) {
// [stack] PROTO
return false;
}

Просмотреть файл

@ -1,13 +0,0 @@
let getCtor = getSelfHostedValue('GetBuiltinConstructor');
assertEq(getCtor('Array'), Array);
let origArray = Array;
Array = function(){};
assertEq(getCtor('Array') == Array, false);
assertEq(getCtor('Array'), origArray);
let origMap = Map;
Map = function(){};
assertEq(getCtor('Map') == Map, false);
assertEq(getCtor('Map'), origMap);

Просмотреть файл

@ -24,6 +24,7 @@
#include "js/UniquePtr.h"
#include "vm/AsyncFunction.h"
#include "vm/AsyncIteration.h"
#include "vm/BuiltinObjectKind.h"
#include "vm/EnvironmentObject.h"
#include "vm/FunctionFlags.h" // js::FunctionFlags
#include "vm/Interpreter.h"
@ -6386,22 +6387,25 @@ bool BaselineCodeGen<Handler>::emit_InitHomeObject() {
}
template <>
bool BaselineCompilerCodeGen::emit_FunctionProto() {
// The function prototype is a constant for a given global.
JSObject* funProto = FunctionProtoOperation(cx);
if (!funProto) {
bool BaselineCompilerCodeGen::emit_BuiltinObject() {
// Built-in objects are constants for a given global.
auto kind = BuiltinObjectKind(GET_UINT8(handler.pc()));
JSObject* builtin = BuiltinObjectOperation(cx, kind);
if (!builtin) {
return false;
}
frame.push(ObjectValue(*funProto));
frame.push(ObjectValue(*builtin));
return true;
}
template <>
bool BaselineInterpreterCodeGen::emit_FunctionProto() {
bool BaselineInterpreterCodeGen::emit_BuiltinObject() {
prepareVMCall();
using Fn = JSObject* (*)(JSContext*);
if (!callVM<Fn, FunctionProtoOperation>()) {
pushUint8BytecodeOperandArg(R0.scratchReg());
using Fn = JSObject* (*)(JSContext*, BuiltinObjectKind);
if (!callVM<Fn, BuiltinObjectOperation>()) {
return false;
}

Просмотреть файл

@ -56,6 +56,7 @@
#include "vm/ArrayBufferViewObject.h"
#include "vm/AsyncFunction.h"
#include "vm/AsyncIteration.h"
#include "vm/BuiltinObjectKind.h"
#include "vm/EqualityOperations.h" // js::SameValue
#include "vm/FunctionFlags.h" // js::FunctionFlags
#include "vm/MatchPairs.h"
@ -14454,9 +14455,11 @@ void CodeGenerator::visitObjectStaticProto(LObjectStaticProto* lir) {
#endif
}
void CodeGenerator::visitFunctionProto(LFunctionProto* lir) {
using Fn = JSObject* (*)(JSContext*);
callVM<Fn, js::FunctionProtoOperation>(lir);
void CodeGenerator::visitBuiltinObject(LBuiltinObject* lir) {
pushArg(Imm32(static_cast<int32_t>(lir->mir()->builtinObjectKind())));
using Fn = JSObject* (*)(JSContext*, BuiltinObjectKind);
callVM<Fn, js::BuiltinObjectOperation>(lir);
}
void CodeGenerator::visitSuperFunction(LSuperFunction* lir) {

Просмотреть файл

@ -25,6 +25,7 @@
#include "js/ScalarType.h" // js::Scalar::Type
#include "util/CheckedArithmetic.h"
#include "vm/ArgumentsObject.h"
#include "vm/BuiltinObjectKind.h"
#include "vm/BytecodeIterator.h"
#include "vm/BytecodeLocation.h"
#include "vm/BytecodeUtil.h"
@ -2386,8 +2387,8 @@ AbortReasonOr<Ok> IonBuilder::inspectOpcode(JSOp op, bool* restarted) {
case JSOp::ObjWithProto:
return jsop_objwithproto();
case JSOp::FunctionProto:
return jsop_functionproto();
case JSOp::BuiltinObject:
return jsop_builtinobject();
case JSOp::CheckReturn:
return jsop_checkreturn();
@ -12700,17 +12701,17 @@ AbortReasonOr<Ok> IonBuilder::jsop_objwithproto() {
return resumeAfter(ins);
}
AbortReasonOr<Ok> IonBuilder::jsop_functionproto() {
JSProtoKey key = JSProto_Function;
AbortReasonOr<Ok> IonBuilder::jsop_builtinobject() {
auto kind = BuiltinObjectKind(GET_UINT8(pc));
// Bake in the prototype if it exists.
if (JSObject* proto = script()->global().maybeGetPrototype(key)) {
pushConstant(ObjectValue(*proto));
// Bake in the built-in if it exists.
if (JSObject* builtin = MaybeGetBuiltinObject(&script()->global(), kind)) {
pushConstant(ObjectValue(*builtin));
return Ok();
}
// Otherwise emit code to generate it.
auto* ins = MFunctionProto::New(alloc());
auto* ins = MBuiltinObject::New(alloc(), kind);
current->add(ins);
current->push(ins);
return resumeAfter(ins);

Просмотреть файл

@ -672,7 +672,7 @@ class MOZ_STACK_CLASS IonBuilder {
AbortReasonOr<Ok> jsop_instrumentation_scriptid();
AbortReasonOr<Ok> jsop_coalesce();
AbortReasonOr<Ok> jsop_objwithproto();
AbortReasonOr<Ok> jsop_functionproto();
AbortReasonOr<Ok> jsop_builtinobject();
AbortReasonOr<Ok> jsop_checkreturn();
AbortReasonOr<Ok> jsop_checkthis();
AbortReasonOr<Ok> jsop_checkthisreinit();

Просмотреть файл

@ -5196,10 +5196,10 @@ void LIRGenerator::visitObjectStaticProto(MObjectStaticProto* ins) {
define(lir, ins);
};
void LIRGenerator::visitFunctionProto(MFunctionProto* ins) {
void LIRGenerator::visitBuiltinObject(MBuiltinObject* ins) {
MOZ_ASSERT(ins->type() == MIRType::Object);
auto* lir = new (alloc()) LFunctionProto();
auto* lir = new (alloc()) LBuiltinObject();
defineReturn(lir, ins);
assignSafepoint(lir, ins);
}

Просмотреть файл

@ -32,6 +32,7 @@
#include "js/HeapAPI.h"
#include "js/ScalarType.h" // js::Scalar::Type
#include "vm/ArrayObject.h"
#include "vm/BuiltinObjectKind.h"
#include "vm/EnvironmentObject.h"
#include "vm/FunctionFlags.h" // js::FunctionFlags
#include "vm/RegExpObject.h"
@ -11624,15 +11625,20 @@ class MObjectStaticProto : public MUnaryInstruction,
}
};
class MFunctionProto : public MNullaryInstruction {
explicit MFunctionProto() : MNullaryInstruction(classOpcode) {
class MBuiltinObject : public MNullaryInstruction {
BuiltinObjectKind builtinObjectKind_;
explicit MBuiltinObject(BuiltinObjectKind kind)
: MNullaryInstruction(classOpcode), builtinObjectKind_(kind) {
setResultType(MIRType::Object);
}
public:
INSTRUCTION_HEADER(FunctionProto)
INSTRUCTION_HEADER(BuiltinObject)
TRIVIAL_NEW_WRAPPERS
BuiltinObjectKind builtinObjectKind() const { return builtinObjectKind_; }
bool possiblyCalls() const override { return true; }
};

Просмотреть файл

@ -74,6 +74,7 @@ namespace jit {
_(BindVarOperation, js::BindVarOperation) \
_(BoxBoxableValue, js::wasm::BoxBoxableValue) \
_(BoxNonStrictThis, js::BoxNonStrictThis) \
_(BuiltinObjectOperation, js::BuiltinObjectOperation) \
_(CallNativeGetter, js::jit::CallNativeGetter) \
_(CallNativeGetterByValue, js::jit::CallNativeGetterByValue) \
_(CallNativeSetter, js::jit::CallNativeSetter) \
@ -126,7 +127,6 @@ namespace jit {
_(FinishBoundFunctionInit, JSFunction::finishBoundFunctionInit) \
_(FreshenLexicalEnv, js::jit::FreshenLexicalEnv) \
_(FunWithProtoOperation, js::FunWithProtoOperation) \
_(FunctionProtoOperation, js::FunctionProtoOperation) \
_(GeneratorThrowOrReturn, js::jit::GeneratorThrowOrReturn) \
_(GetAndClearException, js::GetAndClearException) \
_(GetElementOperation, js::GetElementOperation) \

Просмотреть файл

@ -2213,14 +2213,15 @@ bool WarpBuilder::build_SuperFun(BytecodeLocation) {
return true;
}
bool WarpBuilder::build_FunctionProto(BytecodeLocation loc) {
if (auto* snapshot = getOpSnapshot<WarpFunctionProto>(loc)) {
JSObject* proto = snapshot->proto();
pushConstant(ObjectValue(*proto));
bool WarpBuilder::build_BuiltinObject(BytecodeLocation loc) {
if (auto* snapshot = getOpSnapshot<WarpBuiltinObject>(loc)) {
JSObject* builtin = snapshot->builtin();
pushConstant(ObjectValue(*builtin));
return true;
}
auto* ins = MFunctionProto::New(alloc());
auto kind = loc.getBuiltinObjectKind();
auto* ins = MBuiltinObject::New(alloc(), kind);
current->add(ins);
current->push(ins);
return resumeAfter(ins, loc);

Просмотреть файл

@ -20,6 +20,7 @@
#include "jit/MIRGenerator.h"
#include "jit/WarpBuilder.h"
#include "jit/WarpCacheIRTranspiler.h"
#include "vm/BuiltinObjectKind.h"
#include "vm/BytecodeIterator.h"
#include "vm/BytecodeLocation.h"
#include "vm/EnvironmentObject.h"
@ -331,11 +332,11 @@ AbortReasonOr<WarpScriptSnapshot*> WarpScriptOracle::createScriptSnapshot() {
}
break;
case JSOp::FunctionProto: {
// If we already resolved this proto we can bake it in.
if (JSObject* proto =
cx_->global()->maybeGetPrototype(JSProto_Function)) {
if (!AddOpSnapshot<WarpFunctionProto>(alloc_, opSnapshots, offset,
case JSOp::BuiltinObject: {
// If we already resolved this built-in we can bake it in.
auto kind = loc.getBuiltinObjectKind();
if (JSObject* proto = MaybeGetBuiltinObject(cx_->global(), kind)) {
if (!AddOpSnapshot<WarpBuiltinObject>(alloc_, opSnapshots, offset,
proto)) {
return abort(AbortReason::Alloc);
}

Просмотреть файл

@ -133,8 +133,8 @@ void WarpRegExp::dumpData(GenericPrinter& out) const {
out.printf(" hasShared: %u\n", hasShared());
}
void WarpFunctionProto::dumpData(GenericPrinter& out) const {
out.printf(" proto: 0x%p\n", proto());
void WarpBuiltinObject::dumpData(GenericPrinter& out) const {
out.printf(" builtin: 0x%p\n", builtin());
}
void WarpGetIntrinsic::dumpData(GenericPrinter& out) const {
@ -267,8 +267,8 @@ void WarpRegExp::traceData(JSTracer* trc) {
// No GC pointers.
}
void WarpFunctionProto::traceData(JSTracer* trc) {
TraceWarpGCPtr(trc, proto_, "warp-function-proto");
void WarpBuiltinObject::traceData(JSTracer* trc) {
TraceWarpGCPtr(trc, builtin_, "warp-builtin-object");
}
void WarpGetIntrinsic::traceData(JSTracer* trc) {

Просмотреть файл

@ -29,7 +29,7 @@ class WarpScriptSnapshot;
#define WARP_OP_SNAPSHOT_LIST(_) \
_(WarpArguments) \
_(WarpRegExp) \
_(WarpFunctionProto) \
_(WarpBuiltinObject) \
_(WarpGetIntrinsic) \
_(WarpGetImport) \
_(WarpLambda) \
@ -147,18 +147,18 @@ class WarpRegExp : public WarpOpSnapshot {
#endif
};
// The proto for JSOp::FunctionProto if it exists at compile-time.
class WarpFunctionProto : public WarpOpSnapshot {
WarpGCPtr<JSObject*> proto_;
// The object for JSOp::BuiltinObject if it exists at compile-time.
class WarpBuiltinObject : public WarpOpSnapshot {
WarpGCPtr<JSObject*> builtin_;
public:
static constexpr Kind ThisKind = Kind::WarpFunctionProto;
static constexpr Kind ThisKind = Kind::WarpBuiltinObject;
WarpFunctionProto(uint32_t offset, JSObject* proto)
: WarpOpSnapshot(ThisKind, offset), proto_(proto) {
MOZ_ASSERT(proto);
WarpBuiltinObject(uint32_t offset, JSObject* builtin)
: WarpOpSnapshot(ThisKind, offset), builtin_(builtin) {
MOZ_ASSERT(builtin);
}
JSObject* proto() const { return proto_; }
JSObject* builtin() const { return builtin_; }
void traceData(JSTracer* trc);

Просмотреть файл

@ -7716,13 +7716,13 @@ class LObjectStaticProto : public LInstructionHelper<1, 1, 0> {
}
};
class LFunctionProto : public LCallInstructionHelper<1, 0, 0> {
class LBuiltinObject : public LCallInstructionHelper<1, 0, 0> {
public:
LIR_HEADER(FunctionProto)
LIR_HEADER(BuiltinObject)
LFunctionProto() : LCallInstructionHelper(classOpcode) {}
LBuiltinObject() : LCallInstructionHelper(classOpcode) {}
MFunctionProto* mir() const { return mir_->toFunctionProto(); }
MBuiltinObject* mir() const { return mir_->toBuiltinObject(); }
};
class LSuperFunction : public LInstructionHelper<BOX_PIECES, 1, 1> {

Просмотреть файл

@ -320,6 +320,7 @@ UNIFIED_SOURCES += [
'vm/AsyncIteration.cpp',
'vm/BigIntType.cpp',
'vm/BuildId.cpp',
'vm/BuiltinObjectKind.cpp',
'vm/BytecodeLocation.cpp',
'vm/BytecodeUtil.cpp',
'vm/Caches.cpp',

Просмотреть файл

@ -0,0 +1,170 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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/BuiltinObjectKind.h"
#include "jspubtd.h"
#include "vm/GlobalObject.h"
#include "vm/JSContext.h"
using namespace js;
static JSProtoKey ToProtoKey(BuiltinObjectKind kind) {
switch (kind) {
case BuiltinObjectKind::Array:
return JSProto_Array;
case BuiltinObjectKind::ArrayBuffer:
return JSProto_ArrayBuffer;
case BuiltinObjectKind::Iterator:
return JSProto_Iterator;
case BuiltinObjectKind::Promise:
return JSProto_Promise;
case BuiltinObjectKind::RegExp:
return JSProto_RegExp;
case BuiltinObjectKind::SharedArrayBuffer:
return JSProto_SharedArrayBuffer;
case BuiltinObjectKind::FunctionPrototype:
return JSProto_Function;
case BuiltinObjectKind::ObjectPrototype:
return JSProto_Object;
case BuiltinObjectKind::RegExpPrototype:
return JSProto_RegExp;
case BuiltinObjectKind::StringPrototype:
return JSProto_String;
case BuiltinObjectKind::DateTimeFormat:
return JSProto_DateTimeFormat;
case BuiltinObjectKind::NumberFormat:
return JSProto_NumberFormat;
case BuiltinObjectKind::None:
break;
}
MOZ_CRASH("Unexpected builtin object kind");
}
static bool IsPrototype(BuiltinObjectKind kind) {
switch (kind) {
case BuiltinObjectKind::Array:
case BuiltinObjectKind::ArrayBuffer:
case BuiltinObjectKind::Iterator:
case BuiltinObjectKind::Promise:
case BuiltinObjectKind::RegExp:
case BuiltinObjectKind::SharedArrayBuffer:
return false;
case BuiltinObjectKind::FunctionPrototype:
case BuiltinObjectKind::ObjectPrototype:
case BuiltinObjectKind::RegExpPrototype:
case BuiltinObjectKind::StringPrototype:
return true;
case BuiltinObjectKind::DateTimeFormat:
case BuiltinObjectKind::NumberFormat:
return false;
case BuiltinObjectKind::None:
break;
}
MOZ_CRASH("Unexpected builtin object kind");
}
using BuiltinName = js::ImmutablePropertyNamePtr JSAtomState::*;
struct BuiltinObjectMap {
BuiltinName name;
BuiltinObjectKind kind;
};
BuiltinObjectKind js::BuiltinConstructorForName(JSContext* cx, JSAtom* name) {
static constexpr BuiltinObjectMap constructors[] = {
{&JSAtomState::Array, BuiltinObjectKind::Array},
{&JSAtomState::ArrayBuffer, BuiltinObjectKind::ArrayBuffer},
{&JSAtomState::Iterator, BuiltinObjectKind::Iterator},
{&JSAtomState::Promise, BuiltinObjectKind::Promise},
{&JSAtomState::RegExp, BuiltinObjectKind::RegExp},
{&JSAtomState::SharedArrayBuffer, BuiltinObjectKind::SharedArrayBuffer},
{&JSAtomState::DateTimeFormat, BuiltinObjectKind::DateTimeFormat},
{&JSAtomState::NumberFormat, BuiltinObjectKind::NumberFormat},
};
for (auto& builtin : constructors) {
if (name == cx->names().*(builtin.name)) {
return builtin.kind;
}
}
return BuiltinObjectKind::None;
}
BuiltinObjectKind js::BuiltinPrototypeForName(JSContext* cx, JSAtom* name) {
static constexpr BuiltinObjectMap prototypes[] = {
{&JSAtomState::Function, BuiltinObjectKind::FunctionPrototype},
{&JSAtomState::Object, BuiltinObjectKind::ObjectPrototype},
{&JSAtomState::RegExp, BuiltinObjectKind::RegExpPrototype},
{&JSAtomState::String, BuiltinObjectKind::StringPrototype},
};
for (auto& builtin : prototypes) {
if (name == cx->names().*(builtin.name)) {
return builtin.kind;
}
}
return BuiltinObjectKind::None;
}
JSObject* js::MaybeGetBuiltinObject(GlobalObject* global,
BuiltinObjectKind kind) {
JSProtoKey key = ToProtoKey(kind);
if (IsPrototype(kind)) {
return global->maybeGetPrototype(key);
}
return global->maybeGetConstructor(key);
}
JSObject* js::GetOrCreateBuiltinObject(JSContext* cx, BuiltinObjectKind kind) {
JSProtoKey key = ToProtoKey(kind);
if (IsPrototype(kind)) {
return GlobalObject::getOrCreatePrototype(cx, key);
}
return GlobalObject::getOrCreateConstructor(cx, key);
}
const char* js::BuiltinObjectName(BuiltinObjectKind kind) {
switch (kind) {
case BuiltinObjectKind::Array:
return "Array";
case BuiltinObjectKind::ArrayBuffer:
return "ArrayBuffer";
case BuiltinObjectKind::Iterator:
return "Iterator";
case BuiltinObjectKind::Promise:
return "Promise";
case BuiltinObjectKind::RegExp:
return "RegExp";
case BuiltinObjectKind::SharedArrayBuffer:
return "SharedArrayBuffer";
case BuiltinObjectKind::FunctionPrototype:
return "Function.prototype";
case BuiltinObjectKind::ObjectPrototype:
return "Object.prototype";
case BuiltinObjectKind::RegExpPrototype:
return "RegExp.prototype";
case BuiltinObjectKind::StringPrototype:
return "String.prototype";
case BuiltinObjectKind::DateTimeFormat:
return "DateTimeFormat";
case BuiltinObjectKind::NumberFormat:
return "NumberFormat";
case BuiltinObjectKind::None:
break;
}
MOZ_CRASH("Unexpected builtin object kind");
}

Просмотреть файл

@ -0,0 +1,79 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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_BuiltinObjectKind_h
#define vm_BuiltinObjectKind_h
#include <stdint.h>
#include "jstypes.h"
class JS_PUBLIC_API JSAtom;
struct JS_PUBLIC_API JSContext;
class JS_PUBLIC_API JSObject;
namespace js {
class GlobalObject;
/**
* Built-in objects used by the GetBuiltinConstructor and GetBuiltinPrototype
* self-hosted intrinsics.
*/
enum class BuiltinObjectKind : uint8_t {
// Built-in constructors.
Array,
ArrayBuffer,
Iterator,
Promise,
RegExp,
SharedArrayBuffer,
// Built-in prototypes.
FunctionPrototype,
ObjectPrototype,
RegExpPrototype,
StringPrototype,
// Built-in Intl constructors.
DateTimeFormat,
NumberFormat,
// Invalid placeholder.
None,
};
/**
* Return the BuiltinObjectKind for the given constructor name. Return
* BuiltinObjectKind::None if no matching constructor was found.
*/
BuiltinObjectKind BuiltinConstructorForName(JSContext* cx, JSAtom* name);
/**
* Return the BuiltinObjectKind for the given prototype name. Return
* BuiltinObjectKind::None if no matching prototype was found.
*/
BuiltinObjectKind BuiltinPrototypeForName(JSContext* cx, JSAtom* name);
/**
* Return the built-in object if already created for the given global. Otherwise
* return nullptr.
*/
JSObject* MaybeGetBuiltinObject(GlobalObject* global, BuiltinObjectKind kind);
/**
* Return the built-in object for the given global.
*/
JSObject* GetOrCreateBuiltinObject(JSContext* cx, BuiltinObjectKind kind);
/**
* Return the display name for a built-in object.
*/
const char* BuiltinObjectName(BuiltinObjectKind kind);
} // namespace js
#endif /* vm_BuiltinObjectKind_h */

Просмотреть файл

@ -9,6 +9,7 @@
#include "frontend/NameAnalysisTypes.h"
#include "js/TypeDecls.h"
#include "vm/BuiltinObjectKind.h"
#include "vm/BytecodeUtil.h"
#include "vm/CheckIsObjectKind.h" // CheckIsObjectKind
#include "vm/FunctionPrefixKind.h" // FunctionPrefixKind
@ -279,6 +280,11 @@ class BytecodeLocation {
return CheckIsObjectKind(GET_UINT8(rawBytecode_));
}
BuiltinObjectKind getBuiltinObjectKind() const {
MOZ_ASSERT(is(JSOp::BuiltinObject));
return BuiltinObjectKind(GET_UINT8(rawBytecode_));
}
uint32_t getNewArrayLength() const {
MOZ_ASSERT(is(JSOp::NewArray));
return GET_UINT32(rawBytecode_);

Просмотреть файл

@ -39,6 +39,7 @@
#include "util/Memory.h"
#include "util/StringBuffer.h"
#include "util/Text.h"
#include "vm/BuiltinObjectKind.h"
#include "vm/BytecodeLocation.h"
#include "vm/CodeCoverage.h"
#include "vm/EnvironmentObject.h"
@ -2004,6 +2005,11 @@ bool ExpressionDecompiler::decompilePC(jsbytecode* pc, uint8_t defIndex) {
return write("[bigint]");
#endif
case JSOp::BuiltinObject: {
auto kind = BuiltinObjectKind(GET_UINT8(pc));
return write(BuiltinObjectName(kind));
}
default:
break;
}

Просмотреть файл

@ -183,6 +183,8 @@
MACRO(GeneratorThrow, GeneratorThrow, "GeneratorThrow") \
MACRO(get, get, "get") \
MACRO(GetAggregateError, GetAggregateError, "GetAggregateError") \
MACRO(GetBuiltinConstructor, GetBuiltinConstructor, "GetBuiltinConstructor") \
MACRO(GetBuiltinPrototype, GetBuiltinPrototype, "GetBuiltinPrototype") \
MACRO(GetInternalError, GetInternalError, "GetInternalError") \
MACRO(getBigInt64, getBigInt64, "getBigInt64") \
MACRO(getBigUint64, getBigUint64, "getBigUint64") \

Просмотреть файл

@ -201,6 +201,13 @@ class GlobalObject : public NativeObject {
return &global->getPrototype(key).toObject();
}
JSObject* maybeGetConstructor(JSProtoKey protoKey) const {
MOZ_ASSERT(JSProto_Null < protoKey);
MOZ_ASSERT(protoKey < JSProto_LIMIT);
const Value& v = getConstructor(protoKey);
return v.isObject() ? &v.toObject() : nullptr;
}
JSObject* maybeGetPrototype(JSProtoKey protoKey) const {
MOZ_ASSERT(JSProto_Null < protoKey);
MOZ_ASSERT(protoKey < JSProto_LIMIT);

Просмотреть файл

@ -4378,14 +4378,15 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER bool Interpret(JSContext* cx,
}
END_CASE(CheckClassHeritage)
CASE(FunctionProto) {
JSObject* builtin = FunctionProtoOperation(cx);
CASE(BuiltinObject) {
auto kind = BuiltinObjectKind(GET_UINT8(REGS.pc));
JSObject* builtin = BuiltinObjectOperation(cx, kind);
if (!builtin) {
goto error;
}
PUSH_OBJECT(*builtin);
}
END_CASE(FunctionProto)
END_CASE(BuiltinObject)
CASE(FunWithProto) {
ReservedRooted<JSObject*> proto(&rootObject1, &REGS.sp[-1].toObject());
@ -4961,8 +4962,8 @@ JSObject* js::ImportMetaOperation(JSContext* cx, HandleScript script) {
return GetOrCreateModuleMetaObject(cx, module);
}
JSObject* js::FunctionProtoOperation(JSContext* cx) {
return GlobalObject::getOrCreatePrototype(cx, JSProto_Function);
JSObject* js::BuiltinObjectOperation(JSContext* cx, BuiltinObjectKind kind) {
return GetOrCreateBuiltinObject(cx, kind);
}
bool js::ThrowMsgOperation(JSContext* cx, const unsigned throwMsgKind) {

Просмотреть файл

@ -13,6 +13,7 @@
#include "jspubtd.h"
#include "vm/BuiltinObjectKind.h"
#include "vm/CheckIsObjectKind.h" // CheckIsObjectKind
#include "vm/Iteration.h"
#include "vm/Stack.h"
@ -600,7 +601,7 @@ JSObject* SingletonObjectLiteralOperation(JSContext* cx, HandleScript script,
JSObject* ImportMetaOperation(JSContext* cx, HandleScript script);
JSObject* FunctionProtoOperation(JSContext* cx);
JSObject* BuiltinObjectOperation(JSContext* cx, BuiltinObjectKind kind);
bool ThrowMsgOperation(JSContext* cx, const unsigned throwMsgKind);

Просмотреть файл

@ -225,6 +225,7 @@
* SetPrototype
* Array literals
* RegExp literals
* Built-in objects
* [Functions]
* Creating functions
* Creating constructors
@ -1700,17 +1701,17 @@
*/ \
MACRO(DerivedConstructor, derived_constructor, NULL, 13, 1, 1, JOF_CLASS_CTOR) \
/*
* Pushes the current global's FunctionPrototype.
* Pushes the current global's %BuiltinObject%.
*
* `kind` must be in range for `JSProtoKey` (and must not be
* `JSProto_LIMIT`).
* `kind` must be a valid `BuiltinObjectKind` (and must not be
* `BuiltinObjectKind::None`).
*
* Category: Functions
* Type: Creating constructors
* Operands:
* Stack: => %FunctionPrototype%
* Category: Objects
* Type: Built-in objects
* Operands: uint8_t kind
* Stack: => %BuiltinObject%
*/ \
MACRO(FunctionProto, function_proto, NULL, 1, 0, 1, JOF_BYTE) \
MACRO(BuiltinObject, builtin_object, NULL, 2, 0, 1, JOF_UINT8) \
/*
* Invoke `callee` with `this` and `args`, and push the return value. Throw
* a TypeError if `callee` isn't a function.

Просмотреть файл

@ -293,42 +293,6 @@ static bool intrinsic_IsPossiblyWrappedInstanceOfBuiltin(JSContext* cx,
return true;
}
/**
* Self-hosting intrinsic returning the original constructor for a builtin
* the name of which is the first and only argument.
*
* The return value is guaranteed to be the original constructor even if
* content code changed the named binding on the global object.
*
* This intrinsic shouldn't be called directly. Instead, the
* `GetBuiltinConstructor` and `GetBuiltinPrototype` helper functions in
* Utilities.js should be used, as they cache results, improving performance.
*/
static bool intrinsic_GetBuiltinConstructor(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
RootedString str(cx, args[0].toString());
JSAtom* atom;
if (str->isAtom()) {
atom = &str->asAtom();
} else {
atom = AtomizeString(cx, str);
if (!atom) {
return false;
}
}
RootedId id(cx, AtomToId(atom));
JSProtoKey key = JS_IdToProtoKey(cx, id);
MOZ_ASSERT(key != JSProto_Null);
JSObject* ctor = GlobalObject::getOrCreateConstructor(cx, key);
if (!ctor) {
return false;
}
args.rval().setObject(*ctor);
return true;
}
static bool intrinsic_SubstringKernel(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args[0].isString());
@ -2244,7 +2208,6 @@ static const JSFunctionSpec intrinsic_functions[] = {
IntrinsicIsCallable),
JS_INLINABLE_FN("IsConstructor", intrinsic_IsConstructor, 1, 0,
IntrinsicIsConstructor),
JS_FN("GetBuiltinConstructorImpl", intrinsic_GetBuiltinConstructor, 1, 0),
JS_FN("MakeConstructible", intrinsic_MakeConstructible, 2, 0),
JS_FN("_ConstructFunction", intrinsic_ConstructFunction, 2, 0),
JS_FN("ThrowRangeError", intrinsic_ThrowRangeError, 4, 0),