JavaScriptCore引擎深度解析6——LLInt解释器篇

前言

运行指令字节码(ByteCode)是JavaScriptCore引擎中很核心的部分,各家JavaScript引擎的优化也主要集中于此。JSByteCode的解释执行是一套很复杂的系统,特别是加入了OSR和多级JIT技术之后,整个解释执行变的越来越高效,并且让整个ByteCode的执行在低延时之间和高吞吐之间有个很好的平衡:由低延时的LLInt来解释执行ByteCode,当遇到多次重复调用或者是递归,循环等条件会通过OSR切换成JIT进行解释执行(根据具体触发条件会进入不同的JIT进行动态解释)来加快速度。

// CLoopRegister来模拟真正的CPU寄存器,它定义了如下策略:
// 1、如何将小于intptr_t的整形打包进假寄存器;
// 2、隐藏了大端和小端的区别

1
2
3
4
5
6
7
8
9
10
11
struct UnlinkedInstruction 
{
UnlinkedInstruction() { u.operand = 0; }
UnlinkedInstruction(OpcodeID opcode) { u.opcode = opcode; }
UnlinkedInstruction(int operand) { u.operand = operand; }
union {
OpcodeID opcode;
int32_t operand;
unsigned index;
} u;
};
1
2
3
4
5
6
7
8
9
struct ProtoCallFrame {
Register codeBlockValue;
Register calleeValue;
Register argCountAndCodeOriginValue;
Register thisArg;
uint32_t paddedArgCount;
bool arityMissMatch;
JSValue *args;
}
1
2
3
4
5
6
JSValue execute(ProgramExecutable*, CallFrame*, JSObject* thisObj);
JSValue execute(EvalExecutable*, CallFrame*, JSValue thisValue, JSScope*);
JSValue execute(ModuleProgramExecutable*, CallFrame*, JSModuleEnvironment*);

JSValue executeCall(CallFrame*, JSObject* function, CallType, const CallData&, JSValue thisValue, const ArgList&);
JSObject* executeConstruct(CallFrame*, JSObject* function, ConstructType, const ConstructData&, const ArgList&, JSValue newTarget);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// 解释器执行要素:可执行程序、执行栈、全局对象
JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, JSObject* thisObj)
{
JSScope* scope = thisObj->globalObject()->globalScope();
VM& vm = *scope->vm();

ASSERT(!vm.exception());
ASSERT(!vm.isCollectorBusy());
RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock());

// 看来在垃圾回收器正在忙的时候,是不能执行的
if (vm.isCollectorBusy())
return jsNull();

if (!vm.isSafeToRecurse())
return checkedReturn(throwStackOverflowError(callFrame));

// First check if the "program" is actually just a JSON object. If so,
// we'll handle the JSON object here. Else, we'll handle real JS code
// below at failedJSONP.

Vector<JSONPData> JSONPData;
bool parseResult;

// StringView 它提供一个字符串的视图,即可以通过这个类以各种方法“观测”字符串,但不允许修改字符串;
// 只读的特性,它并不真正持有这个字符串的拷贝,而是与相对应的字符串共享这一空间
StringView programSource = program->source().view();
if (programSource.isNull())
return jsUndefined();

if (programSource.is8Bit()) {
LiteralParser<LChar> literalParser(callFrame, programSource.characters8(), programSource.length(), JSONP);
parseResult = literalParser.tryJSONPParse(JSONPData, scope->globalObject()->globalObjectMethodTable()->supportsRichSourceInfo(scope->globalObject()));
} else {
LiteralParser<UChar> literalParser(callFrame, programSource.characters16(), programSource.length(), JSONP);
parseResult = literalParser.tryJSONPParse(JSONPData, scope->globalObject()->globalObjectMethodTable()->supportsRichSourceInfo(scope->globalObject()));
}

// 如果解析成功
if (parseResult) {
// JSON特殊处理
}
failedJSONP:
// If we get here, then we have already proven that the script is not a JSON object.

VMEntryScope entryScope(vm, scope->globalObject());

// Compile source to bytecode if necessary

// 初始化全局属性
if (JSObject* error = program->initializeGlobalProperties(vm, callFrame, scope))
return checkedReturn(callFrame->vm().throwException(callFrame, error));

// 执行前准备
if (JSObject* error = program->prepareForExecution(callFrame, nullptr, scope, CodeForCall))
return checkedReturn(callFrame->vm().throwException(callFrame, error));

ProgramCodeBlock* codeBlock = program->codeBlock();

if (UNLIKELY(vm.shouldTriggerTermination(callFrame)))
return throwTerminatedExecutionException(callFrame);

if (scope->structure()->isUncacheableDictionary())
scope->flattenDictionaryObject(vm);

ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'.

// 原型调用栈
ProtoCallFrame protoCallFrame;
protoCallFrame.init(codeBlock, JSCallee::create(vm, scope->globalObject(), scope), thisObj, 1);

// 执行代码:先生成JIT代码,然后执行
JSValue result = program->generatedJITCode()->execute(&vm, &protoCallFrame);

return checkedReturn(result);
}

ProgramExecutable

ProgramExecutable可以理解为当前所执行脚本的大总管,从其名字上可以看出来是代表一个可执行程序

先初始化全局属性

处理的结果都会记录在 callFrame 里。主要是通过 JSGlobalObject 这个对象的 addFunction 和 addVar 方法记录 Parser 出那些在全局空间的那些 let,const 和 class 的全局属性,或者 var,let 和 const 的全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// 初始化全局属性
JSObject* ProgramExecutable::initializeGlobalProperties(VM& vm, CallFrame* callFrame, JSScope* scope)
{
RELEASE_ASSERT(scope);
JSGlobalObject* globalObject = scope->globalObject();
RELEASE_ASSERT(globalObject);
ASSERT(&globalObject->vm() == &vm);

JSObject* exception = 0;

// UnlinkedCodeBlock存储的是编译后的ByteCode
UnlinkedProgramCodeBlock* unlinkedCodeBlock = globalObject->createProgramCodeBlock(callFrame, this, &exception);
if (exception)
return exception;

// 全局词典环境
JSGlobalLexicalEnvironment* globalLexicalEnvironment = globalObject->globalLexicalEnvironment();

// 声明的变量
const VariableEnvironment& variableDeclarations = unlinkedCodeBlock->variableDeclarations();

// 词法声明
const VariableEnvironment& lexicalDeclarations = unlinkedCodeBlock->lexicalDeclarations();

m_unlinkedProgramCodeBlock.set(vm, this, unlinkedCodeBlock);

BatchedTransitionOptimizer optimizer(vm, globalObject);

// 遍历当前unlinkedCodeBlock中的方法
for (size_t i = 0, numberOfFunctions = unlinkedCodeBlock->numberOfFunctionDecls(); i < numberOfFunctions; ++i) {
UnlinkedFunctionExecutable* unlinkedFunctionExecutable = unlinkedCodeBlock->functionDecl(i);
ASSERT(!unlinkedFunctionExecutable->name().isEmpty());

// 将该方法添加到全局对象中
globalObject->addFunction(callFrame, unlinkedFunctionExecutable->name());

// 如果虚拟机收集运行信息或者有控制流
if (vm.typeProfiler() || vm.controlFlowProfiler()) {
vm.functionHasExecutedCache()->insertUnexecutedRange(sourceID(),
unlinkedFunctionExecutable->typeProfilingStartOffset(),
unlinkedFunctionExecutable->typeProfilingEndOffset());
}
}

// 遍历当前unlinkedCodeBlock中声明的变量
for (auto& entry : variableDeclarations) {
ASSERT(entry.value.isVar());
globalObject->addVar(callFrame, Identifier::fromUid(&vm, entry.key.get()));
}

// 词法声明处理
{
JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsCast<JSGlobalLexicalEnvironment*>(globalObject->globalScope());

// 获取全局符号表
SymbolTable* symbolTable = globalLexicalEnvironment->symbolTable();
ConcurrentJITLocker locker(symbolTable->m_lock);
for (auto& entry : lexicalDeclarations) {
if (UNLIKELY(entry.value.isConst() && !vm.globalConstRedeclarationShouldThrow() && !isStrictMode())) {
if (symbolTable->contains(locker, entry.key.get()))
continue;
}
ScopeOffset offset = symbolTable->takeNextScopeOffset(locker);
SymbolTableEntry newEntry(VarOffset(offset), entry.value.isConst() ? ReadOnly : 0);
newEntry.prepareToWatch();
symbolTable->add(locker, entry.key.get(), newEntry);

ScopeOffset offsetForAssert = globalLexicalEnvironment->addVariables(1, jsTDZValue());
RELEASE_ASSERT(offsetForAssert == offset);
}
}
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
JSObject* ScriptExecutable::prepareForExecutionImpl(
ExecState* exec, JSFunction* function, JSScope* scope, CodeSpecializationKind kind)
{
VM& vm = exec->vm();
DeferGC deferGC(vm.heap);

if (vm.getAndClearFailNextNewCodeBlock())
return createError(exec->callerFrame(), ASCIILiteral("Forced Failure"));

JSObject* exception = 0;
CodeBlock* codeBlock = newCodeBlockFor(kind, function, scope, exception);
if (!codeBlock) {
RELEASE_ASSERT(exception);
return exception;
}

if (Options::validateBytecode())
codeBlock->validate();

if (Options::useLLInt())
setupLLInt(vm, codeBlock);// 解释执行
else
setupJIT(vm, codeBlock);// JIT编译执行

// 安装code
installCode(*codeBlock->vm(), codeBlock, codeBlock->codeType(), codeBlock->specializationKind());
return 0;
}
-------------本文结束 感谢您的阅读-------------

本文标题:JavaScriptCore引擎深度解析6——LLInt解释器篇

文章作者:lingyun

发布时间:2018年08月20日 - 00:08

最后更新:2019年04月12日 - 11:04

原始链接:https://tsuijunxi.github.io/2018/08/20/JavaScriptCore引擎深度解析-6-LLInt解释器篇/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。