mirror of
https://github.com/darlinghq/darling-JavaScriptCore.git
synced 2025-04-10 19:01:07 +00:00
249 lines
7.6 KiB
Ruby
249 lines
7.6 KiB
Ruby
# Copyright (C) 2011 Apple Inc. All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions
|
|
# are met:
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
|
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
# THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
require "config"
|
|
require "ast"
|
|
require "backends"
|
|
require "parser"
|
|
require "transform"
|
|
|
|
#
|
|
# computeSettingsCombinations(ast) -> settingsCombiations
|
|
#
|
|
# Computes an array of settings maps, where a settings map constitutes
|
|
# a configuration for the assembly code being generated. The map
|
|
# contains key value pairs where keys are settings names (strings) and
|
|
# the values are booleans (true for enabled, false for disabled).
|
|
#
|
|
|
|
def computeSettingsCombinations(ast)
|
|
settingsCombinations = []
|
|
|
|
def settingsCombinator(settingsCombinations, mapSoFar, remaining)
|
|
if remaining.empty?
|
|
settingsCombinations << mapSoFar
|
|
return
|
|
end
|
|
|
|
newMap = mapSoFar.dup
|
|
newMap[remaining[0]] = true
|
|
settingsCombinator(settingsCombinations, newMap, remaining[1..-1])
|
|
|
|
newMap = mapSoFar.dup
|
|
newMap[remaining[0]] = false
|
|
settingsCombinator(settingsCombinations, newMap, remaining[1..-1])
|
|
end
|
|
|
|
nonBackendSettings = ast.filter(Setting).uniq.collect{ |v| v.name }
|
|
nonBackendSettings.delete_if {
|
|
| setting |
|
|
isBackend? setting
|
|
}
|
|
|
|
allBackendsFalse = {}
|
|
BACKENDS.each {
|
|
| backend |
|
|
allBackendsFalse[backend] = false
|
|
}
|
|
|
|
# This will create entries for invalid backends. That's fine. It's necessary
|
|
# because it ensures that generate_offsets_extractor (which knows about valid
|
|
# backends) has settings indices that are compatible with what asm will see
|
|
# (asm doesn't know about valid backends).
|
|
BACKENDS.each {
|
|
| backend |
|
|
map = allBackendsFalse.clone
|
|
map[backend] = true
|
|
settingsCombinator(settingsCombinations, map, nonBackendSettings)
|
|
}
|
|
|
|
settingsCombinations
|
|
end
|
|
|
|
#
|
|
# forSettings(concreteSettings, ast) {
|
|
# | concreteSettings, lowLevelAST, backend | ... }
|
|
#
|
|
# Determines if the settings combination is valid, and if so, calls
|
|
# the block with the information you need to generate code.
|
|
#
|
|
|
|
def forSettings(concreteSettings, ast)
|
|
# Check which architectures this combinator claims to support.
|
|
selectedBackend = nil
|
|
BACKENDS.each {
|
|
| backend |
|
|
if concreteSettings[backend]
|
|
raise if selectedBackend
|
|
selectedBackend = backend
|
|
end
|
|
}
|
|
|
|
return unless isValidBackend? selectedBackend
|
|
|
|
# Resolve the AST down to a low-level form (no macros or conditionals).
|
|
lowLevelAST = ast.resolveSettings(concreteSettings)
|
|
|
|
yield concreteSettings, lowLevelAST, selectedBackend
|
|
end
|
|
|
|
#
|
|
# forEachValidSettingsCombination(ast) {
|
|
# | concreteSettings, ast, backend, index | ... }
|
|
#
|
|
# forEachValidSettingsCombination(ast, settingsCombinations) {
|
|
# | concreteSettings, ast, backend, index | ... }
|
|
#
|
|
# Executes the given block for each valid settings combination in the
|
|
# settings map. The ast passed into the block is resolved
|
|
# (ast.resolve) against the settings.
|
|
#
|
|
# The first form will call computeSettingsCombinations(ast) for you.
|
|
#
|
|
|
|
def forEachValidSettingsCombination(ast, *optionalSettingsCombinations)
|
|
raise if optionalSettingsCombinations.size > 1
|
|
|
|
if optionalSettingsCombinations.empty?
|
|
settingsCombinations = computeSettingsCombinations(ast)
|
|
else
|
|
settingsCombinations = optionalSettingsCombiations[0]
|
|
end
|
|
|
|
settingsCombinations.each_with_index {
|
|
| concreteSettings, index |
|
|
forSettings(concreteSettings, ast) {
|
|
| concreteSettings_, lowLevelAST, backend |
|
|
yield concreteSettings, lowLevelAST, backend, index
|
|
}
|
|
}
|
|
end
|
|
|
|
#
|
|
# cppSettingsTest(concreteSettings)
|
|
#
|
|
# Returns the C++ code used to test if we are in a configuration that
|
|
# corresponds to the given concrete settings.
|
|
#
|
|
|
|
def cppSettingsTest(concreteSettings)
|
|
"#if " + concreteSettings.to_a.collect{
|
|
| pair |
|
|
(if pair[1]
|
|
""
|
|
else
|
|
"!"
|
|
end) + "OFFLINE_ASM_" + pair[0]
|
|
}.join(" && ")
|
|
end
|
|
|
|
#
|
|
# isASTErroneous(ast)
|
|
#
|
|
# Tests to see if the AST claims that there is an error - i.e. if the
|
|
# user's code, after settings resolution, has Error nodes.
|
|
#
|
|
|
|
def isASTErroneous(ast)
|
|
not ast.demacroify({}).filter(Error).empty?
|
|
end
|
|
|
|
#
|
|
# assertConfiguration(concreteSettings)
|
|
#
|
|
# Emits a check that asserts that we're using the given configuration.
|
|
#
|
|
|
|
def assertConfiguration(concreteSettings)
|
|
$output.puts cppSettingsTest(concreteSettings)
|
|
$output.puts "#else"
|
|
$output.puts "#error \"Configuration mismatch.\""
|
|
$output.puts "#endif"
|
|
end
|
|
|
|
#
|
|
# emitCodeInConfiguration(concreteSettings, ast, backend) {
|
|
# | concreteSettings, ast, backend | ... }
|
|
#
|
|
# Emits all relevant guards to see if the configuration holds and
|
|
# calls the block if the configuration is not erroneous.
|
|
#
|
|
|
|
def emitCodeInConfiguration(concreteSettings, ast, backend)
|
|
Label.resetReferenced
|
|
|
|
if !$emitWinAsm
|
|
$output.puts cppSettingsTest(concreteSettings)
|
|
else
|
|
if backend == "X86_WIN"
|
|
$output.puts ".MODEL FLAT, C"
|
|
end
|
|
$output.puts "INCLUDE #{File.basename($output.path)}.sym"
|
|
$output.puts "_TEXT SEGMENT"
|
|
end
|
|
|
|
if isASTErroneous(ast)
|
|
$output.puts "#error \"Invalid configuration. Error at: #{ast.filter(Error)[0].codeOrigin}\""
|
|
elsif not WORKING_BACKENDS.include? backend
|
|
$output.puts "#error \"This backend is not supported yet.\""
|
|
else
|
|
yield concreteSettings, ast, backend
|
|
end
|
|
|
|
if !$emitWinAsm
|
|
$output.puts "#endif"
|
|
else
|
|
$output.puts "_TEXT ENDS"
|
|
$output.puts "END"
|
|
|
|
# Write symbols needed by MASM
|
|
File.open("#{File.basename($output.path)}.sym", "w") {
|
|
| outp |
|
|
Label.forReferencedExtern {
|
|
| name |
|
|
outp.puts "EXTERN #{name[1..-1]} : near"
|
|
}
|
|
}
|
|
end
|
|
end
|
|
|
|
#
|
|
# emitCodeInAllConfigurations(ast) {
|
|
# | concreteSettings, ast, backend, index | ... }
|
|
#
|
|
# Emits guard codes for all valid configurations, and calls the block
|
|
# for those configurations that are valid and not erroneous.
|
|
#
|
|
|
|
def emitCodeInAllConfigurations(ast)
|
|
forEachValidSettingsCombination(ast) {
|
|
| concreteSettings, lowLevelAST, backend, index |
|
|
$output.puts cppSettingsTest(concreteSettings)
|
|
yield concreteSettings, lowLevelAST, backend, index
|
|
$output.puts "#endif"
|
|
}
|
|
end
|
|
|
|
|
|
|