Var JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_RegisterChakraLibraryFunction(RecyclableObject* function, CallInfo callInfo, ...) { EngineInterfaceObject_CommonFunctionProlog(function, callInfo); AssertOrFailFast(args.Info.Count >= 3 && JavascriptString::Is(args.Values[1]) && JavascriptFunction::Is(args.Values[2])); JavascriptLibrary * library = scriptContext->GetLibrary(); // retrieves arguments JavascriptString* methodName = JavascriptString::FromVar(args.Values[1]); JavascriptFunction* func = JavascriptFunction::FromVar(args.Values[2]); // Set the function's display name, as the function we pass in argument are anonym. func->GetFunctionProxy()->SetIsPublicLibraryCode(); func->GetFunctionProxy()->EnsureDeserialized()->SetDisplayName(methodName->GetString(), methodName->GetLength(), 0); DynamicObject* chakraLibraryObject = GetPrototypeFromName(PropertyIds::__chakraLibrary, scriptContext); PropertyIds functionIdentifier = JavascriptOperators::GetPropertyId(methodName, scriptContext); // Link the function to __chakraLibrary. ScriptFunction* scriptFunction = library->CreateScriptFunction(func->GetFunctionProxy()); scriptFunction->GetFunctionProxy()->SetIsJsBuiltInCode(); Assert(scriptFunction->HasFunctionBody()); scriptFunction->GetFunctionBody()->SetJsBuiltInForceInline(); scriptFunction->SetPropertyWithAttributes(PropertyIds::name, methodName, PropertyConfigurable, nullptr); library->AddMember(chakraLibraryObject, functionIdentifier, scriptFunction); //Don't need to return anything return library->GetUndefined(); }
Var AsmJsExternalEntryPoint(RecyclableObject* entryObject, CallInfo callInfo, ...) { ARGUMENTS(args, callInfo); ScriptFunction* func = (ScriptFunction*)entryObject; FunctionBody* body = func->GetFunctionBody(); AsmJsFunctionInfo* info = body->GetAsmJsFunctionInfo(); ScriptContext* scriptContext = func->GetScriptContext(); const uint argInCount = callInfo.Count - 1; int argSize = info->GetArgByteSize(); char* dst; Var returnValue = 0; AsmJsModuleInfo::EnsureHeapAttached(func); argSize = ::Math::Align<int32>(argSize, 8); // Allocate stack space for args __asm { sub esp, argSize mov dst, esp }; // Unbox Var to primitive type { int32 intVal; double doubleVal; float floatVal; for (ArgSlot i = 0; i < info->GetArgCount(); i++) { if (info->GetArgType(i).isInt()) { if (i < argInCount) { intVal = JavascriptMath::ToInt32(args.Values[i + 1], scriptContext); } else { intVal = 0; } *(int32*)dst = intVal; dst += sizeof(int32); } else if (info->GetArgType(i).isFloat()) { if (i < argInCount) { floatVal = (float)(JavascriptConversion::ToNumber(args.Values[i + 1], scriptContext)); } else { floatVal = (float)(JavascriptNumber::NaN); } *(float*)dst = floatVal; dst += sizeof(float); } else if (info->GetArgType(i).isDouble()) { if (i < argInCount) { doubleVal = JavascriptConversion::ToNumber(args.Values[i + 1], scriptContext); } else { doubleVal = JavascriptNumber::NaN; } *(double*)dst = doubleVal; dst += sizeof(double); } else if (info->GetArgType(i).isSIMD()) { AsmJsVarType argType = info->GetArgType(i); AsmJsSIMDValue simdVal; // SIMD values are copied unaligned. // SIMD values cannot be implicitly coerced from/to other types. If the SIMD parameter is missing (i.e. Undefined), we throw type error since there is not equivalent SIMD value to coerce to. switch (argType.which()) { case AsmJsType::Int32x4: if (i >= argInCount || !JavascriptSIMDInt32x4::Is(args.Values[i + 1])) { JavascriptError::ThrowTypeError(scriptContext, JSERR_SimdInt32x4TypeMismatch, L"Int32x4"); } simdVal = ((JavascriptSIMDInt32x4*)(args.Values[i + 1]))->GetValue(); break; case AsmJsType::Float32x4: if (i >= argInCount || !JavascriptSIMDFloat32x4::Is(args.Values[i + 1])) { JavascriptError::ThrowTypeError(scriptContext, JSERR_SimdFloat32x4TypeMismatch, L"Float32x4"); } simdVal = ((JavascriptSIMDFloat32x4*)(args.Values[i + 1]))->GetValue(); break; case AsmJsType::Float64x2: if (i >= argInCount || !JavascriptSIMDFloat64x2::Is(args.Values[i + 1])) { JavascriptError::ThrowTypeError(scriptContext, JSERR_SimdFloat64x2TypeMismatch, L"Float64x2"); } simdVal = ((JavascriptSIMDFloat64x2*)(args.Values[i + 1]))->GetValue(); break; default: Assert(UNREACHED); } *(AsmJsSIMDValue*)dst = simdVal; dst += sizeof(AsmJsSIMDValue); } else { AssertMsg(UNREACHED, "Invalid function arg type."); } } } const void * asmJSEntryPoint = func->GetEntryPointInfo()->address; // make call and convert primitive type back to Var switch (info->GetReturnType().which()) { case AsmJsRetType::Void: __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx } returnValue = JavascriptOperators::OP_LdUndef(func->GetScriptContext()); break; case AsmJsRetType::Signed:{ int32 ival = 0; __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx mov ival, eax } returnValue = JavascriptNumber::ToVar(ival, func->GetScriptContext()); break; } case AsmJsRetType::Double:{ double dval = 0; __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx movsd dval, xmm0 } returnValue = JavascriptNumber::New(dval, func->GetScriptContext()); break; } case AsmJsRetType::Float:{ float fval = 0; __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx movss fval, xmm0 } returnValue = JavascriptNumber::New((double)fval, func->GetScriptContext()); break; } case AsmJsRetType::Int32x4: AsmJsSIMDValue simdVal; simdVal.Zero(); __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx movups simdVal, xmm0 } returnValue = JavascriptSIMDInt32x4::New(&simdVal, func->GetScriptContext()); break; case AsmJsRetType::Float32x4: simdVal.Zero(); __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx movups simdVal, xmm0 } returnValue = JavascriptSIMDFloat32x4::New(&simdVal, func->GetScriptContext()); break; case AsmJsRetType::Float64x2: simdVal.Zero(); __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx movups simdVal, xmm0 } returnValue = JavascriptSIMDFloat64x2::New(&simdVal, func->GetScriptContext()); break; default: Assume(UNREACHED); } return returnValue; }
Var JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_RegisterFunction(RecyclableObject* function, CallInfo callInfo, ...) { EngineInterfaceObject_CommonFunctionProlog(function, callInfo); AssertOrFailFast(args.Info.Count >= 3 && JavascriptObject::Is(args.Values[1]) && JavascriptFunction::Is(args.Values[2])); JavascriptLibrary * library = scriptContext->GetLibrary(); // retrieves arguments RecyclableObject* funcInfo = nullptr; if (!JavascriptConversion::ToObject(args.Values[1], scriptContext, &funcInfo)) { JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Object.assign")); } Var classNameProperty = JavascriptOperators::OP_GetProperty(funcInfo, Js::PropertyIds::className, scriptContext); Var methodNameProperty = JavascriptOperators::OP_GetProperty(funcInfo, Js::PropertyIds::methodName, scriptContext); Var argumentsCountProperty = JavascriptOperators::OP_GetProperty(funcInfo, Js::PropertyIds::argumentsCount, scriptContext); Var forceInlineProperty = JavascriptOperators::OP_GetProperty(funcInfo, Js::PropertyIds::forceInline, scriptContext); Var aliasProperty = JavascriptOperators::OP_GetProperty(funcInfo, Js::PropertyIds::alias, scriptContext); Assert(JavascriptString::Is(classNameProperty)); Assert(JavascriptString::Is(methodNameProperty)); Assert(TaggedInt::Is(argumentsCountProperty)); JavascriptString* className = JavascriptString::FromVar(classNameProperty); JavascriptString* methodName = JavascriptString::FromVar(methodNameProperty); int argumentsCount = TaggedInt::ToInt32(argumentsCountProperty); BOOL forceInline = JavascriptConversion::ToBoolean(forceInlineProperty, scriptContext); JavascriptFunction* func = JavascriptFunction::FromVar(args.Values[2]); // Set the function's display name, as the function we pass in argument are anonym. func->GetFunctionProxy()->SetIsPublicLibraryCode(); func->GetFunctionProxy()->EnsureDeserialized()->SetDisplayName(methodName->GetString(), methodName->GetLength(), 0); DynamicObject* prototype = GetPrototypeFromName(JavascriptOperators::GetPropertyId(className, scriptContext), scriptContext); PropertyIds functionIdentifier = methodName->BufferEquals(_u("Symbol.iterator"), 15)? PropertyIds::_symbolIterator : JavascriptOperators::GetPropertyId(methodName, scriptContext); // Link the function to the prototype. ScriptFunction* scriptFunction = library->CreateScriptFunction(func->GetFunctionProxy()); scriptFunction->GetFunctionProxy()->SetIsJsBuiltInCode(); if (forceInline) { Assert(scriptFunction->HasFunctionBody()); scriptFunction->GetFunctionBody()->SetJsBuiltInForceInline(); } scriptFunction->SetPropertyWithAttributes(PropertyIds::length, TaggedInt::ToVarUnchecked(argumentsCount), PropertyConfigurable, nullptr); scriptFunction->SetConfigurable(PropertyIds::prototype, true); scriptFunction->DeleteProperty(PropertyIds::prototype, Js::PropertyOperationFlags::PropertyOperation_None); scriptFunction->SetPropertyWithAttributes(PropertyIds::name, methodName, PropertyConfigurable, nullptr); library->AddMember(prototype, functionIdentifier, scriptFunction); RecordCommonNativeInterfaceBuiltIns(functionIdentifier, scriptContext, scriptFunction); if (!JavascriptOperators::IsUndefinedOrNull(aliasProperty)) { JavascriptString * alias = JavascriptConversion::ToString(aliasProperty, scriptContext); // Cannot do a string to property id search here, Symbol.* have different hashing mechanism, so resort to this str compare PropertyIds aliasFunctionIdentifier = alias->BufferEquals(_u("Symbol.iterator"), 15) ? PropertyIds::_symbolIterator : JavascriptOperators::GetPropertyId(alias, scriptContext); library->AddMember(prototype, aliasFunctionIdentifier, scriptFunction); } if (prototype == library->arrayPrototype) { RecordDefaultIteratorFunctions(functionIdentifier, scriptContext, scriptFunction); } //Don't need to return anything return library->GetUndefined(); }
Var AsmJsExternalEntryPoint(RecyclableObject* entryObject, CallInfo callInfo, ...) { ARGUMENTS(args, callInfo); ScriptFunction* func = (ScriptFunction*)entryObject; FunctionBody* body = func->GetFunctionBody(); AsmJsFunctionInfo* info = body->GetAsmJsFunctionInfo(); int argSize = info->GetArgByteSize(); void* dst; Var returnValue = 0; argSize = ::Math::Align<int32>(argSize, 8); // Allocate stack space for args PROBE_STACK_CALL(func->GetScriptContext(), func, argSize + Js::Constants::MinStackDefault); dst = _alloca(argSize); const void * asmJSEntryPoint = UnboxAsmJsArguments(func, args.Values + 1, ((char*)dst) - MachPtr, callInfo); // make call and convert primitive type back to Var switch (info->GetReturnType().which()) { case AsmJsRetType::Void: __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx } returnValue = JavascriptOperators::OP_LdUndef(func->GetScriptContext()); break; case AsmJsRetType::Signed:{ int32 ival = 0; __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx mov ival, eax } returnValue = JavascriptNumber::ToVar(ival, func->GetScriptContext()); break; } case AsmJsRetType::Int64: { int32 iLow = 0, iHigh = 0; __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx mov iLow, eax; mov iHigh, edx; } #if ENABLE_DEBUG_CONFIG_OPTIONS if (CONFIG_FLAG(WasmI64)) { uint64 lHigh = ((uint64)iHigh) << 32; uint64 lLow = (uint64)(uint32)iLow; returnValue = CreateI64ReturnObject((int64)(lHigh | lLow), func->GetScriptContext()); break; } #endif JavascriptError::ThrowTypeError(func->GetScriptContext(), WASMERR_InvalidTypeConversion); } case AsmJsRetType::Double:{ double dval = 0; __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx movsd dval, xmm0 } returnValue = JavascriptNumber::NewWithCheck(dval, func->GetScriptContext()); break; } case AsmJsRetType::Float:{ float fval = 0; __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx movss fval, xmm0 } returnValue = JavascriptNumber::NewWithCheck((double)fval, func->GetScriptContext()); break; } #ifdef ENABLE_WASM_SIMD case AsmJsRetType::Bool32x4: case AsmJsRetType::Bool16x8: case AsmJsRetType::Bool8x16: case AsmJsRetType::Float32x4: case AsmJsRetType::Float64x2: case AsmJsRetType::Int64x2: case AsmJsRetType::Int32x4: case AsmJsRetType::Int16x8: case AsmJsRetType::Int8x16: case AsmJsRetType::Uint32x4: case AsmJsRetType::Uint16x8: case AsmJsRetType::Uint8x16: AsmJsSIMDValue simdVal; simdVal.Zero(); __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx movups simdVal, xmm0 } // Todo:: support test return object (like int64) for wasm.simd JavascriptError::ThrowTypeError(func->GetScriptContext(), WASMERR_InvalidTypeConversion); break; #endif default: Assume(UNREACHED); JavascriptError::ThrowTypeError(func->GetScriptContext(), WASMERR_InvalidTypeConversion); } return returnValue; }