-
Notifications
You must be signed in to change notification settings - Fork 54
/
JSFunction-inl.h
168 lines (136 loc) · 5.64 KB
/
JSFunction-inl.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/* -*- 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_JSFunction_inl_h
#define vm_JSFunction_inl_h
#include "vm/JSFunction.h"
#include "gc/Allocator.h"
#include "gc/GCTrace.h"
#include "js/CharacterEncoding.h"
#include "vm/EnvironmentObject.h"
#include "vm/JSObject-inl.h"
#include "vm/NativeObject-inl.h"
namespace js {
inline const char* GetFunctionNameBytes(JSContext* cx, JSFunction* fun,
UniqueChars* bytes) {
if (JSAtom* name = fun->explicitName()) {
*bytes = StringToNewUTF8CharsZ(cx, *name);
return bytes->get();
}
return js_anonymous_str;
}
inline bool CanReuseFunctionForClone(JSContext* cx, HandleFunction fun) {
if (!fun->isSingleton()) {
return false;
}
if (fun->baseScript()->hasBeenCloned()) {
return false;
}
fun->baseScript()->setHasBeenCloned();
return true;
}
inline JSFunction* CloneFunctionObjectIfNotSingleton(
JSContext* cx, HandleFunction fun, HandleObject enclosingEnv,
HandleObject proto = nullptr, NewObjectKind newKind = GenericObject) {
/*
* For attempts to clone functions at a function definition opcode,
* try to avoid the the clone if the function has singleton type. This
* was called pessimistically, and we need to preserve the type's
* property that if it is singleton there is only a single object
* with its type in existence.
*
* For functions inner to run once lambda, it may be possible that
* the lambda runs multiple times and we repeatedly clone it. In these
* cases, fall through to CloneFunctionObject, which will deep clone
* the function's script.
*/
if (CanReuseFunctionForClone(cx, fun)) {
if (proto && proto != fun->staticPrototype()) {
// |CanReuseFunctionForClone| ensures |fun| is a singleton function. |fun|
// must also be extensible and have a mutable prototype for its prototype
// to be modifiable, so assert both conditions, too.
MOZ_ASSERT(fun->isSingleton());
MOZ_ASSERT(!fun->staticPrototypeIsImmutable());
MOZ_ASSERT(fun->isExtensible());
if (!JSObject::setDelegate(cx, proto)) {
return nullptr;
}
// Directly splice the prototype instead of calling |js::SetPrototype| to
// ensure we don't mark the function as having "unknown properties". This
// is safe to do, because the singleton function hasn't yet been exposed
// to scripts.
Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
if (!JSObject::splicePrototype(cx, fun, tagged)) {
return nullptr;
}
}
fun->setEnvironment(enclosingEnv);
return fun;
}
// These intermediate variables are needed to avoid link errors on some
// platforms. Sigh.
gc::AllocKind finalizeKind = gc::AllocKind::FUNCTION;
gc::AllocKind extendedFinalizeKind = gc::AllocKind::FUNCTION_EXTENDED;
gc::AllocKind kind = fun->isExtended() ? extendedFinalizeKind : finalizeKind;
if (CanReuseScriptForClone(cx->realm(), fun, enclosingEnv)) {
return CloneFunctionReuseScript(cx, fun, enclosingEnv, kind, newKind,
proto);
}
RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
if (!script) {
return nullptr;
}
RootedScope enclosingScope(cx, script->enclosingScope());
Rooted<ScriptSourceObject*> sourceObject(cx, script->sourceObject());
return CloneFunctionAndScript(cx, fun, enclosingEnv, enclosingScope,
sourceObject, kind, proto);
}
} /* namespace js */
/* static */ inline JS::Result<JSFunction*, JS::OOM&> JSFunction::create(
JSContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
js::HandleShape shape, js::HandleObjectGroup group) {
MOZ_ASSERT(kind == js::gc::AllocKind::FUNCTION ||
kind == js::gc::AllocKind::FUNCTION_EXTENDED);
debugCheckNewObject(group, shape, kind, heap);
const JSClass* clasp = group->clasp();
MOZ_ASSERT(clasp->isJSFunction());
static constexpr size_t NumDynamicSlots = 0;
MOZ_ASSERT(dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(),
clasp) == NumDynamicSlots);
JSObject* obj = js::AllocateObject(cx, kind, NumDynamicSlots, heap, clasp);
if (!obj) {
return cx->alreadyReportedOOM();
}
NativeObject* nobj = static_cast<NativeObject*>(obj);
nobj->initGroup(group);
nobj->initShape(shape);
nobj->initSlots(nullptr);
nobj->setEmptyElements();
MOZ_ASSERT(!clasp->hasPrivate());
MOZ_ASSERT(shape->slotSpan() == 0);
JSFunction* fun = static_cast<JSFunction*>(nobj);
fun->nargs_ = 0;
// This must be overwritten by some ultimate caller: there's no default
// value to which we could sensibly initialize this.
MOZ_MAKE_MEM_UNDEFINED(&fun->u, sizeof(u));
// Safe: we're initializing for the very first time.
fun->atom_.unsafeSet(nullptr);
if (kind == js::gc::AllocKind::FUNCTION_EXTENDED) {
fun->setFlags(FunctionFlags::EXTENDED);
for (js::GCPtrValue& extendedSlot : fun->toExtended()->extendedSlots) {
extendedSlot.unsafeSet(JS::UndefinedValue());
}
} else {
fun->setFlags(0);
}
MOZ_ASSERT(!clasp->shouldDelayMetadataBuilder(),
"Function has no extra data hanging off it, that wouldn't be "
"allocated at this point, that would require delaying the "
"building of metadata for it");
fun = SetNewObjectMetadata(cx, fun);
js::gc::gcTracer.traceCreateObject(fun);
return fun;
}
#endif /* vm_JSFunction_inl_h */