mirror of
https://github.com/openharmony/ark_js_runtime.git
synced 2026-07-25 22:15:32 -04:00
eee606c334
------------------------------------------
1. Support CommonJS's module export function
2. Related test cases
issue:https://gitee.com/openharmony/ark_js_runtime/issues/I5590V
Signed-off-by: DaiH <daihuina1@huawei.com>
Change-Id: I18968f6a6b971fbe833d91ef98813964cc8292c9
189 lines
9.3 KiB
C++
189 lines
9.3 KiB
C++
/*
|
|
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#include "js_cjs_module.h"
|
|
#include "ecmascript/interpreter/interpreter-inl.h"
|
|
#include "ecmascript/interpreter/slow_runtime_stub.h"
|
|
#include "ecmascript/require/js_cjs_module_cache.h"
|
|
#include "ecmascript/require/js_require_manager.h"
|
|
#include "ecmascript/jspandafile/js_pandafile.h"
|
|
#include "ecmascript/jspandafile/js_pandafile_executor.h"
|
|
#include "ecmascript/jspandafile/js_pandafile_manager.h"
|
|
|
|
namespace panda::ecmascript {
|
|
void JSCjsModule::InitializeModule(JSThread *thread, JSHandle<JSCjsModule> &module,
|
|
JSHandle<JSTaggedValue> &filename, JSHandle<JSTaggedValue> &dirname)
|
|
{
|
|
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
|
|
|
|
JSHandle<JSTaggedValue> dirKey(factory->NewFromASCII("path"));
|
|
SlowRuntimeStub::StObjByName(thread, module.GetTaggedValue(), dirKey.GetTaggedValue(),
|
|
dirname.GetTaggedValue());
|
|
JSHandle<JSTaggedValue> filenameKey(factory->NewFromASCII("filename"));
|
|
SlowRuntimeStub::StObjByName(thread, module.GetTaggedValue(), filenameKey.GetTaggedValue(),
|
|
filename.GetTaggedValue());
|
|
module->SetFilename(thread, filename.GetTaggedValue());
|
|
module->SetPath(thread, dirname.GetTaggedValue());
|
|
return;
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSCjsModule::SearchFromModuleCache(JSThread *thread, JSHandle<JSTaggedValue> &filename)
|
|
{
|
|
[[maybe_unused]] EcmaHandleScope handleScope(thread);
|
|
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
|
|
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
|
|
|
|
JSHandle<JSTaggedValue> moduleObj(env->GetCjsModuleFunction());
|
|
JSHandle<JSTaggedValue> cacheName = globalConst->GetHandledCjsCacheString();
|
|
JSTaggedValue modCache = SlowRuntimeStub::LdObjByName(thread, moduleObj.GetTaggedValue(),
|
|
cacheName.GetTaggedValue(),
|
|
false,
|
|
JSTaggedValue::Undefined());
|
|
JSHandle<CjsModuleCache> moduleCache = JSHandle<CjsModuleCache>(thread, modCache);
|
|
if (moduleCache->ContainsModule(filename.GetTaggedValue())) {
|
|
JSHandle<JSCjsModule> cachedModule = JSHandle<JSCjsModule>(thread,
|
|
moduleCache->GetModule(filename.GetTaggedValue()));
|
|
JSHandle<JSTaggedValue> exportsName = globalConst->GetHandledCjsExportsString();
|
|
JSTaggedValue cachedExports = SlowRuntimeStub::LdObjByName(thread, cachedModule.GetTaggedValue(),
|
|
exportsName.GetTaggedValue(),
|
|
false,
|
|
JSTaggedValue::Undefined());
|
|
return JSHandle<JSTaggedValue>(thread, cachedExports);
|
|
}
|
|
|
|
return JSHandle<JSTaggedValue>(thread, JSTaggedValue::Hole());
|
|
}
|
|
|
|
void JSCjsModule::PutIntoCache(JSThread *thread, JSHandle<JSCjsModule> &module, JSHandle<JSTaggedValue> &filename)
|
|
{
|
|
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
|
|
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
|
|
|
|
JSHandle<JSTaggedValue> moduleObj(env->GetCjsModuleFunction());
|
|
JSHandle<JSTaggedValue> cacheName = globalConst->GetHandledCjsCacheString();
|
|
JSTaggedValue modCache = SlowRuntimeStub::LdObjByName(thread, moduleObj.GetTaggedValue(),
|
|
cacheName.GetTaggedValue(),
|
|
false,
|
|
JSTaggedValue::Undefined());
|
|
JSHandle<CjsModuleCache> moduleCache = JSHandle<CjsModuleCache>(thread, modCache);
|
|
JSHandle<JSTaggedValue> moduleHandle = JSHandle<JSTaggedValue>::Cast(module);
|
|
JSHandle<CjsModuleCache> newCache = CjsModuleCache::PutIfAbsent(thread, moduleCache, filename,
|
|
moduleHandle);
|
|
SlowRuntimeStub::StObjByName(thread, moduleObj.GetTaggedValue(), cacheName.GetTaggedValue(),
|
|
newCache.GetTaggedValue());
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSCjsModule::Load(JSThread *thread, JSHandle<EcmaString> &request)
|
|
{
|
|
[[maybe_unused]] EcmaHandleScope handleScope(thread);
|
|
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
|
|
|
|
// Get local jsPandaFile's dirPath
|
|
JSMutableHandle<JSTaggedValue> parent(thread, JSTaggedValue::Undefined());
|
|
JSMutableHandle<JSTaggedValue> dirname(thread, JSTaggedValue::Undefined());
|
|
const JSPandaFile *jsPandaFile = EcmaInterpreter::GetNativeCallPandafile(thread);
|
|
JSRequireManager::ResolveCurrentPath(thread, parent, dirname, jsPandaFile);
|
|
// Get filename from Callback
|
|
JSHandle<EcmaString> filenameStr = ResolveFilename(thread, dirname.GetTaggedValue(),
|
|
request.GetTaggedValue());
|
|
|
|
JSHandle<JSTaggedValue> filename = JSHandle<JSTaggedValue>::Cast(filenameStr);
|
|
|
|
// Search from Module.cache
|
|
JSHandle<JSTaggedValue> maybeCachedExports = SearchFromModuleCache(thread, filename);
|
|
if (!maybeCachedExports->IsHole()) {
|
|
return maybeCachedExports;
|
|
}
|
|
|
|
// Don't get required exports from cache, execute required JSPandaFile.
|
|
// module = new Module(), which belongs to required JSPandaFile.
|
|
JSHandle<JSCjsModule> module = factory->NewCjsModule();
|
|
InitializeModule(thread, module, filename, dirname);
|
|
PutIntoCache(thread, module, filename);
|
|
|
|
// Execute required JSPandaFile
|
|
RequireExecution(thread, JSHandle<EcmaString>::Cast(filename));
|
|
|
|
// Search from Module.cache after execution.
|
|
JSHandle<JSTaggedValue> cachedExports = SearchFromModuleCache(thread, filename);
|
|
if (cachedExports->IsHole()) {
|
|
LOG_ECMA(ERROR) << "CJS REQUIRE FAIL : Can not obtain module, after executing required jsPandaFile";
|
|
UNREACHABLE();
|
|
}
|
|
return cachedExports;
|
|
}
|
|
|
|
void JSCjsModule::RequireExecution(JSThread *thread, const JSHandle<EcmaString> &moduleFileName)
|
|
{
|
|
CString moduleFilenameStr = ConvertToString(moduleFileName.GetTaggedValue());
|
|
const JSPandaFile *jsPandaFile =
|
|
JSPandaFileManager::GetInstance()->LoadJSPandaFile(moduleFilenameStr, JSPandaFile::ENTRY_MAIN_FUNCTION);
|
|
if (jsPandaFile == nullptr) {
|
|
LOG_ECMA(ERROR) << "open jsPandaFile " << moduleFilenameStr << " error";
|
|
UNREACHABLE();
|
|
}
|
|
JSPandaFileExecutor::Execute(thread, jsPandaFile);
|
|
}
|
|
|
|
JSHandle<EcmaString> JSCjsModule::ResolveFilename(JSThread *thread, JSTaggedValue dirname, JSTaggedValue request)
|
|
{
|
|
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
|
|
|
|
ResolvePathCallback resolvePathCallback = thread->GetEcmaVM()->GetResolvePathCallback();
|
|
if (resolvePathCallback == nullptr) {
|
|
JSHandle<EcmaString> nativeRequireName = ResolveFilenameFromNative(thread, dirname, request);
|
|
return nativeRequireName;
|
|
}
|
|
std::string modDirname = std::string(ConvertToString(EcmaString::Cast(dirname.GetHeapObject())));
|
|
std::string modFile = std::string(ConvertToString(EcmaString::Cast(request.GetHeapObject())));
|
|
std::string callbackRequireName = resolvePathCallback(modDirname, modFile);
|
|
CString fullname = callbackRequireName.c_str();
|
|
return factory->NewFromUtf8(fullname);
|
|
}
|
|
|
|
JSHandle<EcmaString> JSCjsModule::ResolveFilenameFromNative(JSThread *thread, JSTaggedValue dirname,
|
|
JSTaggedValue request)
|
|
{
|
|
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
|
|
|
|
CString dirnameStr = ConvertToString(EcmaString::Cast(dirname.GetHeapObject()));
|
|
CString requestStr = ConvertToString(EcmaString::Cast(request.GetHeapObject()));
|
|
if (requestStr.find("./") == 0) {
|
|
requestStr = requestStr.substr(2); // 2 : delete './'
|
|
}
|
|
int suffixEnd = static_cast<int>(requestStr.find_last_of('.'));
|
|
if (suffixEnd == -1) {
|
|
RETURN_HANDLE_IF_ABRUPT_COMPLETION(EcmaString, thread);
|
|
}
|
|
CString fullname;
|
|
if (requestStr[0] == '/') { // absolute FilePath
|
|
fullname = requestStr.substr(0, suffixEnd) + ".abc";
|
|
} else {
|
|
int pos = static_cast<int>(dirnameStr.find_last_of('/'));
|
|
if (pos == -1) {
|
|
RETURN_HANDLE_IF_ABRUPT_COMPLETION(EcmaString, thread);
|
|
}
|
|
fullname = dirnameStr.substr(0, pos + 1) + requestStr.substr(0, suffixEnd) + ".abc";
|
|
}
|
|
return factory->NewFromUtf8(fullname);
|
|
}
|
|
|
|
JSTaggedValue JSCjsModule::Require(JSThread *thread, JSHandle<EcmaString> &request,
|
|
[[maybe_unused]]JSHandle<JSCjsModule> &parent,
|
|
[[maybe_unused]]bool isMain)
|
|
{
|
|
Load(thread, request);
|
|
return JSTaggedValue::Undefined();
|
|
}
|
|
} // namespace panda::ecmascript
|