/ runtime / MapConstructor.cpp
MapConstructor.cpp
  1  /*
  2   * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
  3   *
  4   * Redistribution and use in source and binary forms, with or without
  5   * modification, are permitted provided that the following conditions
  6   * are met:
  7   * 1. Redistributions of source code must retain the above copyright
  8   *    notice, this list of conditions and the following disclaimer.
  9   * 2. Redistributions in binary form must reproduce the above copyright
 10   *    notice, this list of conditions and the following disclaimer in the
 11   *    documentation and/or other materials provided with the distribution.
 12   *
 13   * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 14   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 15   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 17   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 18   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 19   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 20   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 21   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 22   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 23   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 24   */
 25  
 26  #include "config.h"
 27  #include "MapConstructor.h"
 28  
 29  #include "IteratorOperations.h"
 30  #include "JSCInlines.h"
 31  #include "JSMap.h"
 32  #include "MapPrototype.h"
 33  
 34  namespace JSC {
 35  
 36  const ClassInfo MapConstructor::s_info = { "Function", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(MapConstructor) };
 37  
 38  void MapConstructor::finishCreation(VM& vm, MapPrototype* mapPrototype, GetterSetter* speciesSymbol)
 39  {
 40      Base::finishCreation(vm, 0, "Map"_s, PropertyAdditionMode::WithoutStructureTransition);
 41      putDirectWithoutTransition(vm, vm.propertyNames->prototype, mapPrototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
 42      putDirectNonIndexAccessorWithoutTransition(vm, vm.propertyNames->speciesSymbol, speciesSymbol, PropertyAttribute::Accessor | PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum);
 43  }
 44  
 45  static JSC_DECLARE_HOST_FUNCTION(callMap);
 46  static JSC_DECLARE_HOST_FUNCTION(constructMap);
 47  
 48  MapConstructor::MapConstructor(VM& vm, Structure* structure)
 49      : Base(vm, structure, callMap, constructMap)
 50  {
 51  }
 52  
 53  JSC_DEFINE_HOST_FUNCTION(callMap, (JSGlobalObject* globalObject, CallFrame*))
 54  {
 55      VM& vm = globalObject->vm();
 56      auto scope = DECLARE_THROW_SCOPE(vm);
 57      return JSValue::encode(throwConstructorCannotBeCalledAsFunctionTypeError(globalObject, scope, "Map"));
 58  }
 59  
 60  JSC_DEFINE_HOST_FUNCTION(constructMap, (JSGlobalObject* globalObject, CallFrame* callFrame))
 61  {
 62      VM& vm = globalObject->vm();
 63      auto scope = DECLARE_THROW_SCOPE(vm);
 64  
 65      JSObject* newTarget = asObject(callFrame->newTarget());
 66      Structure* mapStructure = newTarget == callFrame->jsCallee()
 67          ? globalObject->mapStructure()
 68          : InternalFunction::createSubclassStructure(globalObject, newTarget, getFunctionRealm(vm, newTarget)->mapStructure());
 69      RETURN_IF_EXCEPTION(scope, { });
 70  
 71      JSValue iterable = callFrame->argument(0);
 72      if (iterable.isUndefinedOrNull())
 73          RELEASE_AND_RETURN(scope, JSValue::encode(JSMap::create(globalObject, vm, mapStructure)));
 74  
 75      if (auto* iterableMap = jsDynamicCast<JSMap*>(vm, iterable)) {
 76          if (iterableMap->canCloneFastAndNonObservable(mapStructure))
 77              RELEASE_AND_RETURN(scope, JSValue::encode(iterableMap->clone(globalObject, vm, mapStructure)));
 78      }
 79  
 80      JSMap* map = JSMap::create(globalObject, vm, mapStructure);
 81      RETURN_IF_EXCEPTION(scope, encodedJSValue());
 82  
 83      JSValue adderFunction = map->JSObject::get(globalObject, vm.propertyNames->set);
 84      RETURN_IF_EXCEPTION(scope, encodedJSValue());
 85  
 86      auto adderFunctionCallData = getCallData(vm, adderFunction);
 87      if (adderFunctionCallData.type == CallData::Type::None)
 88          return JSValue::encode(throwTypeError(globalObject, scope));
 89  
 90      scope.release();
 91      forEachInIterable(globalObject, iterable, [&](VM& vm, JSGlobalObject* globalObject, JSValue nextItem) {
 92          auto scope = DECLARE_THROW_SCOPE(vm);
 93          if (!nextItem.isObject()) {
 94              throwTypeError(globalObject, scope);
 95              return;
 96          }
 97  
 98          JSValue key = nextItem.get(globalObject, static_cast<unsigned>(0));
 99          RETURN_IF_EXCEPTION(scope, void());
100  
101          JSValue value = nextItem.get(globalObject, static_cast<unsigned>(1));
102          RETURN_IF_EXCEPTION(scope, void());
103  
104          MarkedArgumentBuffer arguments;
105          arguments.append(key);
106          arguments.append(value);
107          ASSERT(!arguments.hasOverflowed());
108          scope.release();
109          call(globalObject, adderFunction, adderFunctionCallData, map, arguments);
110      });
111  
112      return JSValue::encode(map);
113  }
114  
115  JSC_DEFINE_HOST_FUNCTION(mapPrivateFuncMapBucketHead, (JSGlobalObject* globalObject, CallFrame* callFrame))
116  {
117      ASSERT_UNUSED(globalObject, jsDynamicCast<JSMap*>(globalObject->vm(), callFrame->argument(0)));
118      JSMap* map = jsCast<JSMap*>(callFrame->uncheckedArgument(0));
119      auto* head = map->head();
120      ASSERT(head);
121      return JSValue::encode(head);
122  }
123  
124  JSC_DEFINE_HOST_FUNCTION(mapPrivateFuncMapBucketNext, (JSGlobalObject* globalObject, CallFrame* callFrame))
125  {
126      ASSERT(jsDynamicCast<JSMap::BucketType*>(globalObject->vm(), callFrame->argument(0)));
127      auto* bucket = jsCast<JSMap::BucketType*>(callFrame->uncheckedArgument(0));
128      ASSERT(bucket);
129      bucket = bucket->next();
130      while (bucket) {
131          if (!bucket->deleted())
132              return JSValue::encode(bucket);
133          bucket = bucket->next();
134      }
135      return JSValue::encode(globalObject->vm().sentinelMapBucket());
136  }
137  
138  JSC_DEFINE_HOST_FUNCTION(mapPrivateFuncMapBucketKey, (JSGlobalObject* globalObject, CallFrame* callFrame))
139  {
140      ASSERT_UNUSED(globalObject, jsDynamicCast<JSMap::BucketType*>(globalObject->vm(), callFrame->argument(0)));
141      auto* bucket = jsCast<JSMap::BucketType*>(callFrame->uncheckedArgument(0));
142      ASSERT(bucket);
143      return JSValue::encode(bucket->key());
144  }
145  
146  JSC_DEFINE_HOST_FUNCTION(mapPrivateFuncMapBucketValue, (JSGlobalObject* globalObject, CallFrame* callFrame))
147  {
148      ASSERT_UNUSED(globalObject, jsDynamicCast<JSMap::BucketType*>(globalObject->vm(), callFrame->argument(0)));
149      auto* bucket = jsCast<JSMap::BucketType*>(callFrame->uncheckedArgument(0));
150      ASSERT(bucket);
151      return JSValue::encode(bucket->value());
152  }
153  
154  }