1 | /* |
2 | * Copyright (C) 1999-2001 Harri Porten ([email protected]) |
3 | * Copyright (C) 2001 Peter Kelly ([email protected]) |
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 "BytecodeCacheError.h" |
27 | #include "CallFrame.h" |
28 | #include "CatchScope.h" |
29 | #include "CodeCache.h" |
30 | #include "CodeProfiling.h" |
31 | #include "Exception.h" |
32 | #include "IdentifierInlines.h" |
33 | #include "Interpreter.h" |
34 | #include "JSCInlines.h" |
35 | #include "JSGlobalObject.h" |
36 | #include "JSInternalPromise.h" |
37 | #include "JSInternalPromiseDeferred.h" |
38 | #include "JSLock.h" |
39 | #include "JSModuleLoader.h" |
40 | #include "JSModuleRecord.h" |
41 | #include "JSWithScope.h" |
42 | #include "ModuleAnalyzer.h" |
43 | #include "Parser.h" |
44 | #include "ProgramExecutable.h" |
45 | #include "ScriptProfilingScope.h" |
46 | |
47 | namespace JSC { |
48 | |
49 | static inline bool checkSyntaxInternal(VM& vm, const SourceCode& source, ParserError& error) |
50 | { |
51 | return !!parse<ProgramNode>( |
52 | &vm, source, Identifier(), JSParserBuiltinMode::NotBuiltin, |
53 | JSParserStrictMode::NotStrict, JSParserScriptMode::Classic, SourceParseMode::ProgramMode, SuperBinding::NotNeeded, error); |
54 | } |
55 | |
56 | bool checkSyntax(ExecState* exec, const SourceCode& source, JSValue* returnedException) |
57 | { |
58 | VM& vm = exec->vm(); |
59 | JSLockHolder lock(vm); |
60 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
61 | |
62 | ParserError error; |
63 | if (checkSyntaxInternal(vm, source, error)) |
64 | return true; |
65 | ASSERT(error.isValid()); |
66 | if (returnedException) |
67 | *returnedException = error.toErrorObject(exec->lexicalGlobalObject(), source); |
68 | return false; |
69 | } |
70 | |
71 | bool checkSyntax(VM& vm, const SourceCode& source, ParserError& error) |
72 | { |
73 | JSLockHolder lock(vm); |
74 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
75 | return checkSyntaxInternal(vm, source, error); |
76 | } |
77 | |
78 | bool checkModuleSyntax(ExecState* exec, const SourceCode& source, ParserError& error) |
79 | { |
80 | VM& vm = exec->vm(); |
81 | JSLockHolder lock(vm); |
82 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
83 | std::unique_ptr<ModuleProgramNode> moduleProgramNode = parse<ModuleProgramNode>( |
84 | &vm, source, Identifier(), JSParserBuiltinMode::NotBuiltin, |
85 | JSParserStrictMode::Strict, JSParserScriptMode::Module, SourceParseMode::ModuleAnalyzeMode, SuperBinding::NotNeeded, error); |
86 | if (!moduleProgramNode) |
87 | return false; |
88 | |
89 | PrivateName privateName(PrivateName::Description, "EntryPointModule" ); |
90 | ModuleAnalyzer moduleAnalyzer(exec, Identifier::fromUid(privateName), source, moduleProgramNode->varDeclarations(), moduleProgramNode->lexicalVariables()); |
91 | moduleAnalyzer.analyze(*moduleProgramNode); |
92 | return true; |
93 | } |
94 | |
95 | RefPtr<CachedBytecode> generateProgramBytecode(VM& vm, const SourceCode& source, int fd, BytecodeCacheError& error) |
96 | { |
97 | JSLockHolder lock(vm); |
98 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
99 | |
100 | VariableEnvironment variablesUnderTDZ; |
101 | JSParserStrictMode strictMode = JSParserStrictMode::NotStrict; |
102 | JSParserScriptMode scriptMode = JSParserScriptMode::Classic; |
103 | EvalContextType evalContextType = EvalContextType::None; |
104 | |
105 | ParserError parserError; |
106 | UnlinkedCodeBlock* unlinkedCodeBlock = recursivelyGenerateUnlinkedCodeBlock<UnlinkedProgramCodeBlock>(vm, source, strictMode, scriptMode, { }, parserError, evalContextType, &variablesUnderTDZ); |
107 | if (parserError.isValid()) |
108 | error = parserError; |
109 | if (!unlinkedCodeBlock) |
110 | return nullptr; |
111 | |
112 | return serializeBytecode(vm, unlinkedCodeBlock, source, SourceCodeType::ProgramType, strictMode, scriptMode, fd, error, { }); |
113 | } |
114 | |
115 | RefPtr<CachedBytecode> generateModuleBytecode(VM& vm, const SourceCode& source, int fd, BytecodeCacheError& error) |
116 | { |
117 | JSLockHolder lock(vm); |
118 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
119 | |
120 | VariableEnvironment variablesUnderTDZ; |
121 | JSParserStrictMode strictMode = JSParserStrictMode::Strict; |
122 | JSParserScriptMode scriptMode = JSParserScriptMode::Module; |
123 | EvalContextType evalContextType = EvalContextType::None; |
124 | |
125 | ParserError parserError; |
126 | UnlinkedCodeBlock* unlinkedCodeBlock = recursivelyGenerateUnlinkedCodeBlock<UnlinkedModuleProgramCodeBlock>(vm, source, strictMode, scriptMode, { }, parserError, evalContextType, &variablesUnderTDZ); |
127 | if (parserError.isValid()) |
128 | error = parserError; |
129 | if (!unlinkedCodeBlock) |
130 | return nullptr; |
131 | return serializeBytecode(vm, unlinkedCodeBlock, source, SourceCodeType::ModuleType, strictMode, scriptMode, fd, error, { }); |
132 | } |
133 | |
134 | JSValue evaluate(ExecState* exec, const SourceCode& source, JSValue thisValue, NakedPtr<Exception>& returnedException) |
135 | { |
136 | VM& vm = exec->vm(); |
137 | JSLockHolder lock(vm); |
138 | auto scope = DECLARE_CATCH_SCOPE(vm); |
139 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
140 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
141 | |
142 | CodeProfiling profile(source); |
143 | |
144 | if (!thisValue || thisValue.isUndefinedOrNull()) |
145 | thisValue = vm.vmEntryGlobalObject(exec); |
146 | JSObject* thisObj = jsCast<JSObject*>(thisValue.toThis(exec, NotStrictMode)); |
147 | JSValue result = vm.interpreter->executeProgram(source, exec, thisObj); |
148 | |
149 | if (scope.exception()) { |
150 | returnedException = scope.exception(); |
151 | scope.clearException(); |
152 | return jsUndefined(); |
153 | } |
154 | |
155 | RELEASE_ASSERT(result); |
156 | return result; |
157 | } |
158 | |
159 | JSValue profiledEvaluate(ExecState* exec, ProfilingReason reason, const SourceCode& source, JSValue thisValue, NakedPtr<Exception>& returnedException) |
160 | { |
161 | VM& vm = exec->vm(); |
162 | ScriptProfilingScope profilingScope(vm.vmEntryGlobalObject(exec), reason); |
163 | return evaluate(exec, source, thisValue, returnedException); |
164 | } |
165 | |
166 | JSValue evaluateWithScopeExtension(ExecState* exec, const SourceCode& source, JSObject* scopeExtensionObject, NakedPtr<Exception>& returnedException) |
167 | { |
168 | VM& vm = exec->vm(); |
169 | JSGlobalObject* globalObject = vm.vmEntryGlobalObject(exec); |
170 | |
171 | if (scopeExtensionObject) { |
172 | JSScope* ignoredPreviousScope = globalObject->globalScope(); |
173 | globalObject->setGlobalScopeExtension(JSWithScope::create(vm, globalObject, ignoredPreviousScope, scopeExtensionObject)); |
174 | } |
175 | |
176 | JSValue returnValue = JSC::evaluate(globalObject->globalExec(), source, globalObject, returnedException); |
177 | |
178 | if (scopeExtensionObject) |
179 | globalObject->clearGlobalScopeExtension(); |
180 | |
181 | return returnValue; |
182 | } |
183 | |
184 | static Symbol* createSymbolForEntryPointModule(VM& vm) |
185 | { |
186 | // Generate the unique key for the source-provided module. |
187 | PrivateName privateName(PrivateName::Description, "EntryPointModule" ); |
188 | return Symbol::create(vm, privateName.uid()); |
189 | } |
190 | |
191 | static JSInternalPromise* rejectPromise(ExecState* exec, JSGlobalObject* globalObject) |
192 | { |
193 | VM& vm = exec->vm(); |
194 | auto scope = DECLARE_CATCH_SCOPE(vm); |
195 | scope.assertNoException(); |
196 | JSValue exception = scope.exception()->value(); |
197 | scope.clearException(); |
198 | JSInternalPromiseDeferred* deferred = JSInternalPromiseDeferred::tryCreate(exec, globalObject); |
199 | scope.releaseAssertNoException(); |
200 | deferred->reject(exec, exception); |
201 | scope.releaseAssertNoException(); |
202 | return deferred->promise(); |
203 | } |
204 | |
205 | JSInternalPromise* loadAndEvaluateModule(ExecState* exec, Symbol* moduleId, JSValue parameters, JSValue scriptFetcher) |
206 | { |
207 | VM& vm = exec->vm(); |
208 | JSLockHolder lock(vm); |
209 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
210 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
211 | |
212 | return vm.vmEntryGlobalObject(exec)->moduleLoader()->loadAndEvaluateModule(exec, moduleId, parameters, scriptFetcher); |
213 | } |
214 | |
215 | JSInternalPromise* loadAndEvaluateModule(ExecState* exec, const String& moduleName, JSValue parameters, JSValue scriptFetcher) |
216 | { |
217 | VM& vm = exec->vm(); |
218 | JSLockHolder lock(vm); |
219 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
220 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
221 | |
222 | return vm.vmEntryGlobalObject(exec)->moduleLoader()->loadAndEvaluateModule(exec, identifierToJSValue(vm, Identifier::fromString(exec, moduleName)), parameters, scriptFetcher); |
223 | } |
224 | |
225 | JSInternalPromise* loadAndEvaluateModule(ExecState* exec, const SourceCode& source, JSValue scriptFetcher) |
226 | { |
227 | VM& vm = exec->vm(); |
228 | JSLockHolder lock(vm); |
229 | auto scope = DECLARE_THROW_SCOPE(vm); |
230 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
231 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
232 | |
233 | Symbol* key = createSymbolForEntryPointModule(vm); |
234 | |
235 | JSGlobalObject* globalObject = vm.vmEntryGlobalObject(exec); |
236 | |
237 | // Insert the given source code to the ModuleLoader registry as the fetched registry entry. |
238 | globalObject->moduleLoader()->provideFetch(exec, key, source); |
239 | RETURN_IF_EXCEPTION(scope, rejectPromise(exec, globalObject)); |
240 | |
241 | return globalObject->moduleLoader()->loadAndEvaluateModule(exec, key, jsUndefined(), scriptFetcher); |
242 | } |
243 | |
244 | JSInternalPromise* loadModule(ExecState* exec, const String& moduleName, JSValue parameters, JSValue scriptFetcher) |
245 | { |
246 | VM& vm = exec->vm(); |
247 | JSLockHolder lock(vm); |
248 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
249 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
250 | |
251 | return vm.vmEntryGlobalObject(exec)->moduleLoader()->loadModule(exec, identifierToJSValue(vm, Identifier::fromString(exec, moduleName)), parameters, scriptFetcher); |
252 | } |
253 | |
254 | JSInternalPromise* loadModule(ExecState* exec, const SourceCode& source, JSValue scriptFetcher) |
255 | { |
256 | VM& vm = exec->vm(); |
257 | JSLockHolder lock(vm); |
258 | auto scope = DECLARE_THROW_SCOPE(vm); |
259 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
260 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
261 | |
262 | Symbol* key = createSymbolForEntryPointModule(vm); |
263 | |
264 | JSGlobalObject* globalObject = vm.vmEntryGlobalObject(exec); |
265 | |
266 | // Insert the given source code to the ModuleLoader registry as the fetched registry entry. |
267 | // FIXME: Introduce JSSourceCode object to wrap around this source. |
268 | globalObject->moduleLoader()->provideFetch(exec, key, source); |
269 | RETURN_IF_EXCEPTION(scope, rejectPromise(exec, globalObject)); |
270 | |
271 | return globalObject->moduleLoader()->loadModule(exec, key, jsUndefined(), scriptFetcher); |
272 | } |
273 | |
274 | JSValue linkAndEvaluateModule(ExecState* exec, const Identifier& moduleKey, JSValue scriptFetcher) |
275 | { |
276 | VM& vm = exec->vm(); |
277 | JSLockHolder lock(vm); |
278 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
279 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
280 | |
281 | JSGlobalObject* globalObject = vm.vmEntryGlobalObject(exec); |
282 | return globalObject->moduleLoader()->linkAndEvaluateModule(exec, identifierToJSValue(vm, moduleKey), scriptFetcher); |
283 | } |
284 | |
285 | JSInternalPromise* importModule(ExecState* exec, const Identifier& moduleKey, JSValue parameters, JSValue scriptFetcher) |
286 | { |
287 | VM& vm = exec->vm(); |
288 | JSLockHolder lock(vm); |
289 | RELEASE_ASSERT(vm.atomStringTable() == Thread::current().atomStringTable()); |
290 | RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread()); |
291 | |
292 | return vm.vmEntryGlobalObject(exec)->moduleLoader()->requestImportModule(exec, moduleKey, parameters, scriptFetcher); |
293 | } |
294 | |
295 | } // namespace JSC |
296 | |