1 | /* |
2 | * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) |
3 | * Copyright (C) 2001 Peter Kelly (pmk@post.com) |
4 | * Copyright (C) 2003-2019 Apple Inc. |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Library General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2 of the License, or (at your option) any later version. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Library General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Library General Public License |
17 | * along with this library; see the file COPYING.LIB. If not, write to |
18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
19 | * Boston, MA 02110-1301, USA. |
20 | * |
21 | */ |
22 | |
23 | #include "config.h" |
24 | #include "Completion.h" |
25 | |
26 | #include "CallFrame.h" |
27 | #include "CatchScope.h" |
28 | #include "CodeCache.h" |
29 | #include "CodeProfiling.h" |
30 | #include "Exception.h" |
31 | #include "IdentifierInlines.h" |
32 | #include "Interpreter.h" |
33 | #include "JSCInlines.h" |
34 | #include "JSGlobalObject.h" |
35 | #include "JSInternalPromise.h" |
36 | #include "JSInternalPromiseDeferred.h" |
37 | #include "JSLock.h" |
38 | #include "JSModuleLoader.h" |
39 | #include "JSModuleRecord.h" |
40 | #include "JSWithScope.h" |
41 | #include "ModuleAnalyzer.h" |
42 | #include "Parser.h" |
43 | #include "ProgramExecutable.h" |
44 | #include "ScriptProfilingScope.h" |
45 | |
46 | namespace JSC { |
47 | |
48 | static inline bool checkSyntaxInternal(VM& vm, const SourceCode& source, ParserError& error) |
49 | { |
50 | return !!parse<ProgramNode>( |
51 | &vm, source, Identifier(), JSParserBuiltinMode::NotBuiltin, |
52 | JSParserStrictMode::NotStrict, JSParserScriptMode::Classic, SourceParseMode::ProgramMode, SuperBinding::NotNeeded, error); |
53 | } |
54 | |
55 | bool checkSyntax(ExecState* exec, const SourceCode& source, JSValue* returnedException) |
56 | { |
57 | VM& vm = exec->vm(); |
58 | JSLockHolder lock(vm); |
59 | RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable()); |
60 | |
61 | ParserError error; |
62 | if (checkSyntaxInternal(vm, source, error)) |
63 | return true; |
64 | ASSERT(error.isValid()); |
65 | if (returnedException) |
66 | *returnedException = error.toErrorObject(exec->lexicalGlobalObject(), source); |
67 | return false; |
68 | } |
69 | |
70 | bool checkSyntax(VM& vm, const SourceCode& source, ParserError& error) |
71 | { |
72 | JSLockHolder lock(vm); |
73 | RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable()); |
74 | return checkSyntaxInternal(vm, source, error); |
75 | } |
76 | |
77 | bool checkModuleSyntax(ExecState* exec, const SourceCode& source, ParserError& error) |
78 | { |
79 | VM& vm = exec->vm(); |
80 | JSLockHolder lock(vm); |
81 | RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable()); |
82 | std::unique_ptr<ModuleProgramNode> moduleProgramNode = parse<ModuleProgramNode>( |
83 | &vm, source, Identifier(), JSParserBuiltinMode::NotBuiltin, |
84 | JSParserStrictMode::Strict, JSParserScriptMode::Module, SourceParseMode::ModuleAnalyzeMode, SuperBinding::NotNeeded, error); |
85 | if (!moduleProgramNode) |
86 | return false; |
87 | |
88 | PrivateName privateName(PrivateName::Description, "EntryPointModule" ); |
89 | ModuleAnalyzer moduleAnalyzer(exec, Identifier::fromUid(privateName), source, moduleProgramNode->varDeclarations(), moduleProgramNode->lexicalVariables()); |
90 | moduleAnalyzer.analyze(*moduleProgramNode); |
91 | return true; |
92 | } |
93 | |
94 | Ref<CachedBytecode> generateProgramBytecode(VM& vm, const SourceCode& source, ParserError& error) |
95 | { |
96 | JSLockHolder lock(vm); |
97 | RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable()); |
98 | |
99 | VariableEnvironment variablesUnderTDZ; |
100 | JSParserStrictMode strictMode = JSParserStrictMode::NotStrict; |
101 | JSParserScriptMode scriptMode = JSParserScriptMode::Classic; |
102 | EvalContextType evalContextType = EvalContextType::None; |
103 | |
104 | UnlinkedCodeBlock* unlinkedCodeBlock = recursivelyGenerateUnlinkedCodeBlock<UnlinkedProgramCodeBlock>(vm, source, strictMode, scriptMode, { }, error, evalContextType, &variablesUnderTDZ); |
105 | if (!unlinkedCodeBlock) |
106 | return CachedBytecode::create(); |
107 | return serializeBytecode(vm, unlinkedCodeBlock, source, SourceCodeType::ProgramType, strictMode, scriptMode, { }); |
108 | } |
109 | |
110 | Ref<CachedBytecode> generateModuleBytecode(VM& vm, const SourceCode& source, ParserError& error) |
111 | { |
112 | JSLockHolder lock(vm); |
113 | RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable()); |
114 | |
115 | VariableEnvironment variablesUnderTDZ; |
116 | JSParserStrictMode strictMode = JSParserStrictMode::Strict; |
117 | JSParserScriptMode scriptMode = JSParserScriptMode::Module; |
118 | EvalContextType evalContextType = EvalContextType::None; |
119 | |
120 | UnlinkedCodeBlock* unlinkedCodeBlock = recursivelyGenerateUnlinkedCodeBlock<UnlinkedModuleProgramCodeBlock>(vm, source, strictMode, scriptMode, { }, error, evalContextType, &variablesUnderTDZ); |
121 | if (!unlinkedCodeBlock) |
122 | return CachedBytecode::create(); |
123 | return serializeBytecode(vm, unlinkedCodeBlock, source, SourceCodeType::ModuleType, strictMode, scriptMode, { }); |
124 | } |
125 | |
126 | JSValue evaluate(ExecState* exec, const SourceCode& source, JSValue thisValue, NakedPtr<Exception>& returnedException) |
127 | { |
128 | VM& vm = exec->vm(); |
129 | JSLockHolder lock(vm); |
130 | auto scope = DECLARE_CATCH_SCOPE(vm); |
131 | RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable()); |
132 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
133 | |
134 | CodeProfiling profile(source); |
135 | |
136 | if (!thisValue || thisValue.isUndefinedOrNull()) |
137 | thisValue = vm.vmEntryGlobalObject(exec); |
138 | JSObject* thisObj = jsCast<JSObject*>(thisValue.toThis(exec, NotStrictMode)); |
139 | JSValue result = vm.interpreter->executeProgram(source, exec, thisObj); |
140 | |
141 | if (scope.exception()) { |
142 | returnedException = scope.exception(); |
143 | scope.clearException(); |
144 | return jsUndefined(); |
145 | } |
146 | |
147 | RELEASE_ASSERT(result); |
148 | return result; |
149 | } |
150 | |
151 | JSValue profiledEvaluate(ExecState* exec, ProfilingReason reason, const SourceCode& source, JSValue thisValue, NakedPtr<Exception>& returnedException) |
152 | { |
153 | VM& vm = exec->vm(); |
154 | ScriptProfilingScope profilingScope(vm.vmEntryGlobalObject(exec), reason); |
155 | return evaluate(exec, source, thisValue, returnedException); |
156 | } |
157 | |
158 | JSValue evaluateWithScopeExtension(ExecState* exec, const SourceCode& source, JSObject* scopeExtensionObject, NakedPtr<Exception>& returnedException) |
159 | { |
160 | VM& vm = exec->vm(); |
161 | JSGlobalObject* globalObject = vm.vmEntryGlobalObject(exec); |
162 | |
163 | if (scopeExtensionObject) { |
164 | JSScope* ignoredPreviousScope = globalObject->globalScope(); |
165 | globalObject->setGlobalScopeExtension(JSWithScope::create(vm, globalObject, ignoredPreviousScope, scopeExtensionObject)); |
166 | } |
167 | |
168 | JSValue returnValue = JSC::evaluate(globalObject->globalExec(), source, globalObject, returnedException); |
169 | |
170 | if (scopeExtensionObject) |
171 | globalObject->clearGlobalScopeExtension(); |
172 | |
173 | return returnValue; |
174 | } |
175 | |
176 | static Symbol* createSymbolForEntryPointModule(VM& vm) |
177 | { |
178 | // Generate the unique key for the source-provided module. |
179 | PrivateName privateName(PrivateName::Description, "EntryPointModule" ); |
180 | return Symbol::create(vm, privateName.uid()); |
181 | } |
182 | |
183 | static JSInternalPromise* rejectPromise(ExecState* exec, JSGlobalObject* globalObject) |
184 | { |
185 | VM& vm = exec->vm(); |
186 | auto scope = DECLARE_CATCH_SCOPE(vm); |
187 | scope.assertNoException(); |
188 | JSValue exception = scope.exception()->value(); |
189 | scope.clearException(); |
190 | JSInternalPromiseDeferred* deferred = JSInternalPromiseDeferred::tryCreate(exec, globalObject); |
191 | scope.releaseAssertNoException(); |
192 | deferred->reject(exec, exception); |
193 | scope.releaseAssertNoException(); |
194 | return deferred->promise(); |
195 | } |
196 | |
197 | JSInternalPromise* loadAndEvaluateModule(ExecState* exec, Symbol* moduleId, JSValue parameters, JSValue scriptFetcher) |
198 | { |
199 | VM& vm = exec->vm(); |
200 | JSLockHolder lock(vm); |
201 | RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable()); |
202 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
203 | |
204 | return vm.vmEntryGlobalObject(exec)->moduleLoader()->loadAndEvaluateModule(exec, moduleId, parameters, scriptFetcher); |
205 | } |
206 | |
207 | JSInternalPromise* loadAndEvaluateModule(ExecState* exec, const String& moduleName, JSValue parameters, JSValue scriptFetcher) |
208 | { |
209 | VM& vm = exec->vm(); |
210 | JSLockHolder lock(vm); |
211 | RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable()); |
212 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
213 | |
214 | return vm.vmEntryGlobalObject(exec)->moduleLoader()->loadAndEvaluateModule(exec, identifierToJSValue(vm, Identifier::fromString(exec, moduleName)), parameters, scriptFetcher); |
215 | } |
216 | |
217 | JSInternalPromise* loadAndEvaluateModule(ExecState* exec, const SourceCode& source, JSValue scriptFetcher) |
218 | { |
219 | VM& vm = exec->vm(); |
220 | JSLockHolder lock(vm); |
221 | auto scope = DECLARE_THROW_SCOPE(vm); |
222 | RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable()); |
223 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
224 | |
225 | Symbol* key = createSymbolForEntryPointModule(vm); |
226 | |
227 | JSGlobalObject* globalObject = vm.vmEntryGlobalObject(exec); |
228 | |
229 | // Insert the given source code to the ModuleLoader registry as the fetched registry entry. |
230 | globalObject->moduleLoader()->provideFetch(exec, key, source); |
231 | RETURN_IF_EXCEPTION(scope, rejectPromise(exec, globalObject)); |
232 | |
233 | return globalObject->moduleLoader()->loadAndEvaluateModule(exec, key, jsUndefined(), scriptFetcher); |
234 | } |
235 | |
236 | JSInternalPromise* loadModule(ExecState* exec, const String& moduleName, JSValue parameters, JSValue scriptFetcher) |
237 | { |
238 | VM& vm = exec->vm(); |
239 | JSLockHolder lock(vm); |
240 | RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable()); |
241 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
242 | |
243 | return vm.vmEntryGlobalObject(exec)->moduleLoader()->loadModule(exec, identifierToJSValue(vm, Identifier::fromString(exec, moduleName)), parameters, scriptFetcher); |
244 | } |
245 | |
246 | JSInternalPromise* loadModule(ExecState* exec, const SourceCode& source, JSValue scriptFetcher) |
247 | { |
248 | VM& vm = exec->vm(); |
249 | JSLockHolder lock(vm); |
250 | auto scope = DECLARE_THROW_SCOPE(vm); |
251 | RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable()); |
252 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
253 | |
254 | Symbol* key = createSymbolForEntryPointModule(vm); |
255 | |
256 | JSGlobalObject* globalObject = vm.vmEntryGlobalObject(exec); |
257 | |
258 | // Insert the given source code to the ModuleLoader registry as the fetched registry entry. |
259 | // FIXME: Introduce JSSourceCode object to wrap around this source. |
260 | globalObject->moduleLoader()->provideFetch(exec, key, source); |
261 | RETURN_IF_EXCEPTION(scope, rejectPromise(exec, globalObject)); |
262 | |
263 | return globalObject->moduleLoader()->loadModule(exec, key, jsUndefined(), scriptFetcher); |
264 | } |
265 | |
266 | JSValue linkAndEvaluateModule(ExecState* exec, const Identifier& moduleKey, JSValue scriptFetcher) |
267 | { |
268 | VM& vm = exec->vm(); |
269 | JSLockHolder lock(vm); |
270 | RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable()); |
271 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
272 | |
273 | JSGlobalObject* globalObject = vm.vmEntryGlobalObject(exec); |
274 | return globalObject->moduleLoader()->linkAndEvaluateModule(exec, identifierToJSValue(vm, moduleKey), scriptFetcher); |
275 | } |
276 | |
277 | JSInternalPromise* importModule(ExecState* exec, const Identifier& moduleKey, JSValue parameters, JSValue scriptFetcher) |
278 | { |
279 | VM& vm = exec->vm(); |
280 | JSLockHolder lock(vm); |
281 | RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable()); |
282 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
283 | |
284 | return vm.vmEntryGlobalObject(exec)->moduleLoader()->requestImportModule(exec, moduleKey, parameters, scriptFetcher); |
285 | } |
286 | |
287 | } // namespace JSC |
288 | |