inline Structure* getBoundFunctionStructure(VM& vm, ExecState* exec, JSGlobalObject* globalObject, JSObject* targetFunction) { auto scope = DECLARE_THROW_SCOPE(vm); JSValue prototype = targetFunction->getPrototype(vm, exec); RETURN_IF_EXCEPTION(scope, nullptr); JSFunction* targetJSFunction = jsDynamicCast<JSFunction*>(vm, targetFunction); // We only cache the structure of the bound function if the bindee is a JSFunction since there // isn't any good place to put the structure on Internal Functions. if (targetJSFunction) { Structure* structure = targetJSFunction->rareData(vm)->getBoundFunctionStructure(); if (structure && structure->storedPrototype() == prototype && structure->globalObject() == globalObject) return structure; } Structure* result = globalObject->boundFunctionStructure(); // It would be nice if the structure map was keyed global objects in addition to the other things. Unfortunately, it is not // currently. Whoever works on caching structure changes for prototype transistions should consider this problem as well. // See: https://bugs.webkit.org/show_bug.cgi?id=152738 if (prototype.isObject() && prototype.getObject()->globalObject() == globalObject) { result = vm.prototypeMap.emptyStructureForPrototypeFromBaseStructure(globalObject, prototype.getObject(), result); ASSERT_WITH_SECURITY_IMPLICATION(result->globalObject() == globalObject); } else result = Structure::create(vm, globalObject, prototype, result->typeInfo(), result->classInfo()); if (targetJSFunction) targetJSFunction->rareData(vm)->setBoundFunctionStructure(vm, result); return result; }
Structure* InternalFunction::createSubclassStructure(ExecState* exec, JSValue newTarget, Structure* baseClass) { VM& vm = exec->vm(); // We allow newTarget == JSValue() because the API needs to be able to create classes without having a real JS frame. // Since we don't allow subclassing in the API we just treat newTarget == JSValue() as newTarget == exec->callee() ASSERT(!newTarget || newTarget.isConstructor()); if (newTarget && newTarget != exec->callee()) { // newTarget may be an InternalFunction if we were called from Reflect.construct. JSFunction* targetFunction = jsDynamicCast<JSFunction*>(newTarget); if (LIKELY(targetFunction)) { Structure* structure = targetFunction->rareData(vm)->internalFunctionAllocationStructure(); if (LIKELY(structure && structure->classInfo() == baseClass->classInfo())) return structure; // Note, Reflect.construct might cause the profile to churn but we don't care. JSValue prototypeValue = newTarget.get(exec, exec->propertyNames().prototype); if (UNLIKELY(vm.exception())) return nullptr; if (JSObject* prototype = jsDynamicCast<JSObject*>(prototypeValue)) return targetFunction->rareData(vm)->createInternalFunctionAllocationStructureFromBase(vm, prototype, baseClass); } else { JSValue prototypeValue = newTarget.get(exec, exec->propertyNames().prototype); if (UNLIKELY(vm.exception())) return nullptr; if (JSObject* prototype = jsDynamicCast<JSObject*>(prototypeValue)) { // This only happens if someone Reflect.constructs our builtin constructor with another builtin constructor as the new.target. // Thus, we don't care about the cost of looking up the structure from our hash table every time. return vm.prototypeMap.emptyStructureForPrototypeFromBaseStructure(prototype, baseClass); } } } return baseClass; }