/ runtime / InternalFunction.cpp
InternalFunction.cpp
  1  /*
  2   *  Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
  3   *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
  4   *  Copyright (C) 2004-2019 Apple Inc. All rights reserved.
  5   *
  6   *  This library is free software; you can redistribute it and/or
  7   *  modify it under the terms of the GNU Library General Public
  8   *  License as published by the Free Software Foundation; either
  9   *  version 2 of the License, or (at your option) any later version.
 10   *
 11   *  This library is distributed in the hope that it will be useful,
 12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 14   *  Library General Public License for more details.
 15   *
 16   *  You should have received a copy of the GNU Library General Public License
 17   *  along with this library; see the file COPYING.LIB.  If not, write to
 18   *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 19   *  Boston, MA 02110-1301, USA.
 20   *
 21   */
 22  
 23  #include "config.h"
 24  #include "InternalFunction.h"
 25  
 26  #include "JSBoundFunction.h"
 27  #include "JSCInlines.h"
 28  #include "ProxyObject.h"
 29  
 30  namespace JSC {
 31  
 32  STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(InternalFunction);
 33  
 34  const ClassInfo InternalFunction::s_info = { "Function", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(InternalFunction) };
 35  
 36  InternalFunction::InternalFunction(VM& vm, Structure* structure, NativeFunction functionForCall, NativeFunction functionForConstruct)
 37      : Base(vm, structure)
 38      , m_functionForCall(functionForCall)
 39      , m_functionForConstruct(functionForConstruct ? functionForConstruct : callHostFunctionAsConstructor)
 40      , m_globalObject(vm, this, structure->globalObject())
 41  {
 42      ASSERT_WITH_MESSAGE(m_functionForCall, "[[Call]] must be implemented");
 43      ASSERT(m_functionForConstruct);
 44  }
 45  
 46  void InternalFunction::finishCreation(VM& vm, unsigned length, const String& name, PropertyAdditionMode nameAdditionMode)
 47  {
 48      Base::finishCreation(vm);
 49      ASSERT(jsDynamicCast<InternalFunction*>(vm, this));
 50      // JSCell::{getCallData,getConstructData} relies on the following conditions.
 51      ASSERT(methodTable(vm)->getCallData == InternalFunction::info()->methodTable.getCallData);
 52      ASSERT(methodTable(vm)->getConstructData == InternalFunction::info()->methodTable.getConstructData);
 53      ASSERT(type() == InternalFunctionType || type() == NullSetterFunctionType);
 54  
 55      JSString* nameString = jsString(vm, name);
 56      m_originalName.set(vm, this, nameString);
 57      // The enumeration order is length followed by name. So, we make sure to add the properties in that order.
 58      if (nameAdditionMode == PropertyAdditionMode::WithStructureTransition) {
 59          putDirect(vm, vm.propertyNames->length, jsNumber(length), PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum);
 60          putDirect(vm, vm.propertyNames->name, nameString, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum);
 61      } else {
 62          putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(length), PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum);
 63          putDirectWithoutTransition(vm, vm.propertyNames->name, nameString, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum);
 64      }
 65  }
 66  
 67  void InternalFunction::visitChildren(JSCell* cell, SlotVisitor& visitor)
 68  {
 69      InternalFunction* thisObject = jsCast<InternalFunction*>(cell);
 70      ASSERT_GC_OBJECT_INHERITS(thisObject, info());
 71      Base::visitChildren(thisObject, visitor);
 72      
 73      visitor.append(thisObject->m_originalName);
 74  }
 75  
 76  const String& InternalFunction::name()
 77  {
 78      const String& name = m_originalName->tryGetValue();
 79      ASSERT(name); // m_originalName was built from a String, and hence, there is no rope to resolve.
 80      return name;
 81  }
 82  
 83  const String InternalFunction::displayName(VM& vm)
 84  {
 85      JSValue displayName = getDirect(vm, vm.propertyNames->displayName);
 86      
 87      if (displayName && isJSString(displayName))
 88          return asString(displayName)->tryGetValue();
 89      
 90      return String();
 91  }
 92  
 93  CallData InternalFunction::getCallData(JSCell* cell)
 94  {
 95      // Keep this function OK for invocation from concurrent compilers.
 96      auto* function = jsCast<InternalFunction*>(cell);
 97      ASSERT(function->m_functionForCall);
 98  
 99      CallData callData;
100      callData.type = CallData::Type::Native;
101      callData.native.function = function->m_functionForCall;
102      return callData;
103  }
104  
105  CallData InternalFunction::getConstructData(JSCell* cell)
106  {
107      // Keep this function OK for invocation from concurrent compilers.
108      CallData constructData;
109      auto* function = jsCast<InternalFunction*>(cell);
110      if (function->m_functionForConstruct != callHostFunctionAsConstructor) {
111          constructData.type = CallData::Type::Native;
112          constructData.native.function = function->m_functionForConstruct;
113      }
114      return constructData;
115  }
116  
117  const String InternalFunction::calculatedDisplayName(VM& vm)
118  {
119      const String explicitName = displayName(vm);
120      
121      if (!explicitName.isEmpty())
122          return explicitName;
123      
124      return name();
125  }
126  
127  Structure* InternalFunction::createSubclassStructure(JSGlobalObject* globalObject, JSObject* newTarget, Structure* baseClass)
128  {
129      VM& vm = globalObject->vm();
130      auto scope = DECLARE_THROW_SCOPE(vm);
131  
132      ASSERT(baseClass->hasMonoProto());
133  
134      // newTarget may be an InternalFunction if we were called from Reflect.construct.
135      JSFunction* targetFunction = jsDynamicCast<JSFunction*>(vm, newTarget);
136      JSGlobalObject* baseGlobalObject = baseClass->globalObject();
137  
138      if (LIKELY(targetFunction)) {
139          FunctionRareData* rareData = targetFunction->ensureRareData(vm);
140          Structure* structure = rareData->internalFunctionAllocationStructure();
141          if (LIKELY(structure && structure->classInfo() == baseClass->classInfo() && structure->globalObject() == baseGlobalObject))
142              return structure;
143  
144          // Note, Reflect.construct might cause the profile to churn but we don't care.
145          JSValue prototypeValue = targetFunction->get(globalObject, vm.propertyNames->prototype);
146          RETURN_IF_EXCEPTION(scope, nullptr);
147          if (JSObject* prototype = jsDynamicCast<JSObject*>(vm, prototypeValue))
148              return rareData->createInternalFunctionAllocationStructureFromBase(vm, baseGlobalObject, prototype, baseClass);
149      } else {
150          JSValue prototypeValue = newTarget->get(globalObject, vm.propertyNames->prototype);
151          RETURN_IF_EXCEPTION(scope, nullptr);
152          if (JSObject* prototype = jsDynamicCast<JSObject*>(vm, prototypeValue)) {
153              // This only happens if someone Reflect.constructs our builtin constructor with another builtin constructor as the new.target.
154              // Thus, we don't care about the cost of looking up the structure from our hash table every time.
155              return vm.structureCache.emptyStructureForPrototypeFromBaseStructure(baseGlobalObject, prototype, baseClass);
156          }
157      }
158      
159      return baseClass;
160  }
161  
162  InternalFunction* InternalFunction::createFunctionThatMasqueradesAsUndefined(VM& vm, JSGlobalObject* globalObject, unsigned length, const String& name, NativeFunction nativeFunction)
163  {
164      Structure* structure = Structure::create(vm, globalObject, globalObject->objectPrototype(), TypeInfo(InternalFunctionType, InternalFunction::StructureFlags | MasqueradesAsUndefined), InternalFunction::info());
165      globalObject->masqueradesAsUndefinedWatchpoint()->fireAll(globalObject->vm(), "Allocated masquerading object");
166      InternalFunction* function = new (NotNull, allocateCell<InternalFunction>(vm.heap)) InternalFunction(vm, structure, nativeFunction);
167      function->finishCreation(vm, length, name, PropertyAdditionMode::WithoutStructureTransition);
168      return function;
169  }
170  
171  // https://tc39.es/ecma262/#sec-getfunctionrealm
172  JSGlobalObject* getFunctionRealm(VM& vm, JSObject* object)
173  {
174      ASSERT(object->isCallable(vm));
175  
176      while (true) {
177          if (object->inherits<JSBoundFunction>(vm)) {
178              object = jsCast<JSBoundFunction*>(object)->targetFunction();
179              continue;
180          }
181  
182          if (object->type() == ProxyObjectType) {
183              auto* proxy = jsCast<ProxyObject*>(object);
184              // Per step 4.a, a TypeError should be thrown for revoked Proxy, yet we skip it since:
185              // a) It is barely observable anyway: "prototype" lookup in createSubclassStructure() will throw for revoked Proxy.
186              // b) Throwing getFunctionRealm() will restrict calling it inline as an argument of createSubclassStructure().
187              // c) There is ongoing discussion on removing it: https://github.com/tc39/ecma262/issues/1798.
188              if (!proxy->isRevoked()) {
189                  object = proxy->target();
190                  continue;
191              }
192          }
193  
194          return object->globalObject(vm);
195      }
196  }
197  
198  
199  } // namespace JSC