Skip to content

Commit 06dd001

Browse files
committed
Factor public library script function logic out into its own method for use by JsBuiltIns
1 parent be80d9b commit 06dd001

File tree

3 files changed

+80
-31
lines changed

3 files changed

+80
-31
lines changed

lib/Runtime/Library/EngineInterfaceObject.cpp

Lines changed: 76 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -267,20 +267,18 @@ namespace Js
267267
return scriptContext->GetLibrary()->GetUndefined();
268268
}
269269

270-
Var EngineInterfaceObject::Entry_TagPublicLibraryCode(RecyclableObject *function, CallInfo callInfo, ...)
270+
/* static */
271+
template <bool isConstructor>
272+
ScriptFunction *EngineInterfaceObject::CreateLibraryCodeScriptFunction(ScriptFunction *scriptFunction, JavascriptString *displayName)
271273
{
272-
EngineInterfaceObject_CommonFunctionProlog(function, callInfo);
273-
274-
AssertOrFailFast((callInfo.Count == 3 || callInfo.Count == 4) && JavascriptFunction::Is(args[1]) && JavascriptString::Is(args[2]));
275-
276-
JavascriptFunction *func = JavascriptFunction::UnsafeFromVar(args[1]);
277-
JavascriptString *methodName = JavascriptString::UnsafeFromVar(args[2]);
278-
279-
func->GetFunctionProxy()->SetIsPublicLibraryCode();
280-
281-
// use GetSz rather than GetString because we use wcsrchr below, which expects a null-terminated string
282-
const char16 *methodNameBuf = methodName->GetSz();
283-
charcount_t methodNameLength = methodName->GetLength();
274+
ScriptContext *scriptContext = scriptFunction->GetScriptContext();
275+
scriptFunction->GetFunctionProxy()->SetIsPublicLibraryCode();
276+
277+
// Use GetSz rather than GetString because we use wcsrchr below, which expects a null-terminated string
278+
// Callers can pass in a string like "get compare" or "Intl.Collator.prototype.resolvedOptions" -- only for the
279+
// latter do we extract a shortName.
280+
const char16 *methodNameBuf = displayName->GetSz();
281+
charcount_t methodNameLength = displayName->GetLength();
284282
const char16 *shortName = wcsrchr(methodNameBuf, _u('.'));
285283
charcount_t shortNameOffset = 0;
286284
if (shortName != nullptr)
@@ -289,25 +287,79 @@ namespace Js
289287
shortNameOffset = static_cast<charcount_t>(shortName - methodNameBuf);
290288
}
291289

292-
func->GetFunctionProxy()->EnsureDeserialized()->SetDisplayName(methodNameBuf, methodNameLength, shortNameOffset);
290+
scriptFunction->GetFunctionProxy()->EnsureDeserialized()->SetDisplayName(methodNameBuf, methodNameLength, shortNameOffset);
291+
292+
if (!isConstructor)
293+
{
294+
// set the ErrorOnNew attribute to disallow construction. JsBuiltIn/Intl functions are usually regular ScriptFunctions
295+
// (not lambdas or class methods), so they are usually constructable by default.
296+
FunctionInfo *info = scriptFunction->GetFunctionInfo();
297+
AssertMsg((info->GetAttributes() & FunctionInfo::Attributes::ErrorOnNew) == 0, "Why are we trying to disable construction of a function that already isn't constructable?");
298+
info->SetAttributes((FunctionInfo::Attributes) (info->GetAttributes() | FunctionInfo::Attributes::ErrorOnNew));
299+
300+
// Assert that the type handler is deferred to ensure that we aren't overwriting previous modifications.
301+
// Script functions start with deferred type handlers, which undefer as soon as any property is modified.
302+
// Since the function that is passed in should be an inline function expression, its type should still be deferred by the time it gets here.
303+
AssertOrFailFast(scriptFunction->GetDynamicType()->GetTypeHandler()->IsDeferredTypeHandler());
293304

294-
bool creatingConstructor = true;
295-
if (callInfo.Count == 4)
305+
// give the function a type handler with name and length but without prototype
306+
DynamicTypeHandler::SetInstanceTypeHandler(scriptFunction, scriptContext->GetLibrary()->GetDeferredFunctionWithLengthTypeHandler());
307+
}
308+
else
296309
{
297-
AssertOrFailFast(JavascriptBoolean::Is(args[3]));
298-
creatingConstructor = JavascriptBoolean::UnsafeFromVar(args[3])->GetValue();
310+
AssertMsg((scriptFunction->GetFunctionInfo()->GetAttributes() & FunctionInfo::Attributes::ErrorOnNew) == 0, "Why is the function not constructable by default?");
299311
}
300312

301-
if (!creatingConstructor)
313+
Var existingName = nullptr;
314+
if (JavascriptOperators::GetOwnProperty(scriptFunction, PropertyIds::name, &existingName, scriptContext, nullptr))
302315
{
303-
FunctionInfo *info = func->GetFunctionInfo();
304-
info->SetAttributes((FunctionInfo::Attributes) (info->GetAttributes() | FunctionInfo::Attributes::ErrorOnNew));
316+
JavascriptString *existingNameString = JavascriptString::FromVar(existingName);
317+
if (existingNameString->GetLength() == 0)
318+
{
319+
// Only overwrite the name of the function object if it was anonymous coming in
320+
// If the input function was named, it is likely intentional
321+
existingName = nullptr;
322+
}
323+
}
305324

306-
AssertOrFailFast(func->GetDynamicType()->GetTypeHandler()->IsDeferredTypeHandler());
307-
DynamicTypeHandler::SetInstanceTypeHandler(func, scriptContext->GetLibrary()->GetDeferredFunctionWithLengthTypeHandler());
325+
if (existingName == nullptr || JavascriptOperators::IsUndefined(existingName))
326+
{
327+
// It is convenient to set the name here rather than in script, since it is often duplicated.
328+
JavascriptString *funcName = displayName;
329+
if (shortName)
330+
{
331+
funcName = JavascriptString::NewCopyBuffer(shortName, methodNameLength - shortNameOffset, scriptContext);
332+
}
333+
334+
scriptFunction->SetPropertyWithAttributes(PropertyIds::name, funcName, PropertyConfigurable, nullptr);
335+
}
336+
337+
return scriptFunction;
338+
}
339+
340+
Var EngineInterfaceObject::Entry_TagPublicLibraryCode(RecyclableObject *function, CallInfo callInfo, ...)
341+
{
342+
#pragma warning(push)
343+
#pragma warning(disable: 4189) // 'scriptContext': local variable is initialized but not referenced
344+
EngineInterfaceObject_CommonFunctionProlog(function, callInfo);
345+
#pragma warning(pop)
346+
347+
AssertOrFailFast((args.Info.Count == 3 || args.Info.Count == 4) && ScriptFunction::Is(args.Values[1]) && JavascriptString::Is(args.Values[2]));
348+
349+
ScriptFunction *func = ScriptFunction::UnsafeFromVar(args[1]);
350+
JavascriptString *methodName = JavascriptString::UnsafeFromVar(args[2]);
351+
352+
if (args.Info.Count == 4)
353+
{
354+
AssertOrFailFast(JavascriptBoolean::Is(args.Values[3]));
355+
if (!JavascriptBoolean::UnsafeFromVar(args.Values[3])->GetValue())
356+
{
357+
return CreateLibraryCodeScriptFunction<false>(func, methodName);
358+
}
308359
}
309360

310-
return func;
361+
// isConstructor = true is the default (when no 3rd arg is provided)
362+
return CreateLibraryCodeScriptFunction<true>(func, methodName);
311363
}
312364

313365
/*

lib/Runtime/Library/EngineInterfaceObject.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ namespace Js
8888

8989
static bool __cdecl InitializeCommonNativeInterfaces(DynamicObject* engineInterface, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode);
9090

91+
template <bool isConstructor>
92+
static ScriptFunction *CreateLibraryCodeScriptFunction(ScriptFunction *scriptFunction, JavascriptString *displayName);
93+
9194
class EntryInfo
9295
{
9396
public:

lib/Runtime/Library/InJavascript/Intl.js

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -931,7 +931,7 @@
931931

932932
const CollatorPrototype = {};
933933

934-
const Collator = tagPublicFunction("Intl.Collator", function Collator(locales = undefined, options = undefined) {
934+
const Collator = tagPublicFunction("Intl.Collator", function (locales = undefined, options = undefined) {
935935
const newTarget = new.target === undefined ? Collator : new.target;
936936
const collator = OrdinaryCreateFromConstructor(newTarget, CollatorPrototype);
937937

@@ -1026,12 +1026,6 @@
10261026

10271027
return hiddenObject.boundCompare;
10281028
});
1029-
_.defineProperty(getCompare, "name", {
1030-
value: "get compare",
1031-
writable: false,
1032-
enumerable: false,
1033-
configurable: true,
1034-
});
10351029
_.defineProperty(CollatorPrototype, "compare", {
10361030
get: getCompare,
10371031
enumerable: false,

0 commit comments

Comments
 (0)