1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #ifndef vm_JSFunction_inl_h
8 #define vm_JSFunction_inl_h
9
10 #include "vm/JSFunction.h"
11
12 #include "gc/Allocator.h"
13 #include "gc/GCProbes.h"
14 #include "js/CharacterEncoding.h"
15 #include "vm/EnvironmentObject.h"
16
17 #include "vm/JSObject-inl.h"
18 #include "vm/NativeObject-inl.h"
19
20 namespace js {
21
GetFunctionNameBytes(JSContext * cx,JSFunction * fun,UniqueChars * bytes)22 inline const char* GetFunctionNameBytes(JSContext* cx, JSFunction* fun,
23 UniqueChars* bytes) {
24 if (JSAtom* name = fun->explicitName()) {
25 *bytes = StringToNewUTF8CharsZ(cx, *name);
26 return bytes->get();
27 }
28 return js_anonymous_str;
29 }
30
CanReuseFunctionForClone(JSContext * cx,HandleFunction fun)31 inline bool CanReuseFunctionForClone(JSContext* cx, HandleFunction fun) {
32 if (!fun->isSingleton()) {
33 return false;
34 }
35
36 if (fun->baseScript()->hasBeenCloned()) {
37 return false;
38 }
39 fun->baseScript()->setHasBeenCloned();
40
41 return true;
42 }
43
44 inline JSFunction* CloneFunctionObjectIfNotSingleton(
45 JSContext* cx, HandleFunction fun, HandleObject enclosingEnv,
46 HandleObject proto = nullptr, NewObjectKind newKind = GenericObject) {
47 /*
48 * For attempts to clone functions at a function definition opcode,
49 * try to avoid the the clone if the function has singleton type. This
50 * was called pessimistically, and we need to preserve the type's
51 * property that if it is singleton there is only a single object
52 * with its type in existence.
53 *
54 * For functions inner to run once lambda, it may be possible that
55 * the lambda runs multiple times and we repeatedly clone it. In these
56 * cases, fall through to CloneFunctionObject, which will deep clone
57 * the function's script.
58 */
59 if (CanReuseFunctionForClone(cx, fun)) {
60 if (proto && !SetPrototypeForClonedFunction(cx, fun, proto)) {
61 return nullptr;
62 }
63 fun->setEnvironment(enclosingEnv);
64 return fun;
65 }
66
67 // These intermediate variables are needed to avoid link errors on some
68 // platforms. Sigh.
69 gc::AllocKind finalizeKind = gc::AllocKind::FUNCTION;
70 gc::AllocKind extendedFinalizeKind = gc::AllocKind::FUNCTION_EXTENDED;
71 gc::AllocKind kind = fun->isExtended() ? extendedFinalizeKind : finalizeKind;
72
73 if (CanReuseScriptForClone(cx->realm(), fun, enclosingEnv)) {
74 return CloneFunctionReuseScript(cx, fun, enclosingEnv, kind, newKind,
75 proto);
76 }
77
78 RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
79 if (!script) {
80 return nullptr;
81 }
82 RootedScope enclosingScope(cx, script->enclosingScope());
83 Rooted<ScriptSourceObject*> sourceObject(cx, script->sourceObject());
84 return CloneFunctionAndScript(cx, fun, enclosingEnv, enclosingScope,
85 sourceObject, kind, proto);
86 }
87
88 } /* namespace js */
89
create(JSContext * cx,js::gc::AllocKind kind,js::gc::InitialHeap heap,js::HandleShape shape,js::HandleObjectGroup group)90 /* static */ inline JS::Result<JSFunction*, JS::OOM&> JSFunction::create(
91 JSContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
92 js::HandleShape shape, js::HandleObjectGroup group) {
93 MOZ_ASSERT(kind == js::gc::AllocKind::FUNCTION ||
94 kind == js::gc::AllocKind::FUNCTION_EXTENDED);
95
96 debugCheckNewObject(group, shape, kind, heap);
97
98 const JSClass* clasp = group->clasp();
99 MOZ_ASSERT(clasp->isJSFunction());
100
101 static constexpr size_t NumDynamicSlots = 0;
102 MOZ_ASSERT(dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(),
103 clasp) == NumDynamicSlots);
104
105 JSObject* obj = js::AllocateObject(cx, kind, NumDynamicSlots, heap, clasp);
106 if (!obj) {
107 return cx->alreadyReportedOOM();
108 }
109
110 NativeObject* nobj = static_cast<NativeObject*>(obj);
111 nobj->initGroup(group);
112 nobj->initShape(shape);
113
114 nobj->initSlots(nullptr);
115 nobj->setEmptyElements();
116
117 MOZ_ASSERT(!clasp->hasPrivate());
118 MOZ_ASSERT(shape->slotSpan() == 0);
119
120 JSFunction* fun = static_cast<JSFunction*>(nobj);
121 fun->nargs_ = 0;
122
123 // This must be overwritten by some ultimate caller: there's no default
124 // value to which we could sensibly initialize this.
125 MOZ_MAKE_MEM_UNDEFINED(&fun->u, sizeof(u));
126
127 // Safe: we're initializing for the very first time.
128 fun->atom_.unsafeSet(nullptr);
129
130 if (kind == js::gc::AllocKind::FUNCTION_EXTENDED) {
131 fun->setFlags(FunctionFlags::EXTENDED);
132 for (js::GCPtrValue& extendedSlot : fun->toExtended()->extendedSlots) {
133 extendedSlot.unsafeSet(JS::UndefinedValue());
134 }
135 } else {
136 fun->setFlags(0);
137 }
138
139 MOZ_ASSERT(!clasp->shouldDelayMetadataBuilder(),
140 "Function has no extra data hanging off it, that wouldn't be "
141 "allocated at this point, that would require delaying the "
142 "building of metadata for it");
143 fun = SetNewObjectMetadata(cx, fun);
144
145 js::gc::gcprobes::CreateObject(fun);
146
147 return fun;
148 }
149
150 #endif /* vm_JSFunction_inl_h */
151