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